absmartly-sdk 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +180 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +92 -0
- data/LICENSE.txt +21 -0
- data/README +180 -0
- data/Rakefile +12 -0
- data/example/example.rb +48 -0
- data/lib/a_b_smartly.rb +92 -0
- data/lib/a_b_smartly_config.rb +49 -0
- data/lib/absmartly/md5.rb +178 -0
- data/lib/absmartly/variant_assigner.rb +36 -0
- data/lib/absmartly/version.rb +5 -0
- data/lib/absmartly.rb +57 -0
- data/lib/audience_deserializer.rb +8 -0
- data/lib/audience_matcher.rb +38 -0
- data/lib/client.rb +80 -0
- data/lib/client_config.rb +43 -0
- data/lib/context.rb +528 -0
- data/lib/context_config.rb +70 -0
- data/lib/context_data_deserializer.rb +8 -0
- data/lib/context_data_provider.rb +8 -0
- data/lib/context_event_handler.rb +8 -0
- data/lib/context_event_logger.rb +9 -0
- data/lib/context_event_serializer.rb +8 -0
- data/lib/default_audience_deserializer.rb +13 -0
- data/lib/default_context_data_deserializer.rb +16 -0
- data/lib/default_context_data_provider.rb +15 -0
- data/lib/default_context_event_handler.rb +15 -0
- data/lib/default_context_event_serializer.rb +58 -0
- data/lib/default_http_client.rb +61 -0
- data/lib/default_http_client_config.rb +19 -0
- data/lib/default_variable_parser.rb +13 -0
- data/lib/hashing.rb +9 -0
- data/lib/http_client.rb +20 -0
- data/lib/json/attribute.rb +30 -0
- data/lib/json/context_data.rb +31 -0
- data/lib/json/experiment.rb +83 -0
- data/lib/json/experiment_application.rb +27 -0
- data/lib/json/experiment_variant.rb +29 -0
- data/lib/json/exposure.rb +60 -0
- data/lib/json/goal_achievement.rb +32 -0
- data/lib/json/publish_event.rb +42 -0
- data/lib/json/unit.rb +30 -0
- data/lib/json_expr/evaluator.rb +27 -0
- data/lib/json_expr/expr_evaluator.rb +122 -0
- data/lib/json_expr/json_expr.rb +37 -0
- data/lib/json_expr/operator.rb +8 -0
- data/lib/json_expr/operators/and_combinator.rb +14 -0
- data/lib/json_expr/operators/binary_operator.rb +22 -0
- data/lib/json_expr/operators/boolean_combinator.rb +15 -0
- data/lib/json_expr/operators/equals_operator.rb +12 -0
- data/lib/json_expr/operators/greater_than_operator.rb +12 -0
- data/lib/json_expr/operators/greater_than_or_equal_operator.rb +12 -0
- data/lib/json_expr/operators/in_operator.rb +23 -0
- data/lib/json_expr/operators/less_than_operator.rb +12 -0
- data/lib/json_expr/operators/less_than_or_equal_operator.rb +12 -0
- data/lib/json_expr/operators/match_operator.rb +17 -0
- data/lib/json_expr/operators/nil_operator.rb +11 -0
- data/lib/json_expr/operators/not_operator.rb +11 -0
- data/lib/json_expr/operators/or_combinator.rb +14 -0
- data/lib/json_expr/operators/unary_operator.rb +13 -0
- data/lib/json_expr/operators/value_operator.rb +11 -0
- data/lib/json_expr/operators/var_operator.rb +21 -0
- data/lib/scheduled_executor_service.rb +8 -0
- data/lib/scheduled_thread_pool_executor.rb +14 -0
- data/lib/string.rb +30 -0
- data/lib/variable_parser.rb +8 -0
- data/lib/variant_assigner.rb +47 -0
- data/sig/absmartly.rbs +4 -0
- metadata +120 -0
data/lib/a_b_smartly.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "time"
|
4
|
+
require_relative "context"
|
5
|
+
require_relative "audience_matcher"
|
6
|
+
require_relative "default_context_data_provider"
|
7
|
+
require_relative "default_context_event_handler"
|
8
|
+
require_relative "default_variable_parser"
|
9
|
+
require_relative "default_audience_deserializer"
|
10
|
+
require_relative "scheduled_thread_pool_executor"
|
11
|
+
|
12
|
+
class ABSmartly
|
13
|
+
attr_accessor :context_data_provider, :context_event_handler,
|
14
|
+
:variable_parser, :scheduler, :context_event_logger,
|
15
|
+
:audience_deserializer, :client
|
16
|
+
|
17
|
+
def self.configure_client(&block)
|
18
|
+
@@init_http = block
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.create(config)
|
22
|
+
ABSmartly.new(config)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(config)
|
26
|
+
@@init_http = nil
|
27
|
+
@context_data_provider = config.context_data_provider
|
28
|
+
@context_event_handler = config.context_event_handler
|
29
|
+
@context_event_logger = config.context_event_logger
|
30
|
+
@variable_parser = config.variable_parser
|
31
|
+
@audience_deserializer = config.audience_deserializer
|
32
|
+
@scheduler = config.scheduler
|
33
|
+
|
34
|
+
if @context_data_provider.nil? || @context_event_handler.nil?
|
35
|
+
@client = config.client
|
36
|
+
raise ArgumentError.new("Missing Client instance configuration") if @client.nil?
|
37
|
+
|
38
|
+
if @context_data_provider.nil?
|
39
|
+
@context_data_provider = DefaultContextDataProvider.new(@client)
|
40
|
+
end
|
41
|
+
|
42
|
+
if @context_event_handler.nil?
|
43
|
+
@context_event_handler = DefaultContextEventHandler.new(@client)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if @variable_parser.nil?
|
48
|
+
@variable_parser = DefaultVariableParser.new
|
49
|
+
end
|
50
|
+
|
51
|
+
if @audience_deserializer.nil?
|
52
|
+
@audience_deserializer = DefaultAudienceDeserializer.new
|
53
|
+
end
|
54
|
+
if @scheduler.nil?
|
55
|
+
@scheduler = ScheduledThreadPoolExecutor.new(1)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_context(config)
|
60
|
+
validate_params(config)
|
61
|
+
Context.create(get_utc_format, config, @scheduler, @context_data_provider.context_data,
|
62
|
+
@context_data_provider, @context_event_handler, @context_event_logger, @variable_parser,
|
63
|
+
AudienceMatcher.new(@audience_deserializer))
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_context_with(config, data)
|
67
|
+
Context.create(get_utc_format, config, scheduler, data,
|
68
|
+
@context_data_provider, @context_event_handler, @context_event_logger, @variable_parser,
|
69
|
+
AudienceMatcher.new(@audience_deserializer))
|
70
|
+
end
|
71
|
+
|
72
|
+
def context_data
|
73
|
+
@context_data_provider.context_data
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def get_utc_format
|
78
|
+
Time.now.utc.iso8601(3)
|
79
|
+
end
|
80
|
+
|
81
|
+
def validate_params(params)
|
82
|
+
params.units.each do |key, value|
|
83
|
+
unless value.is_a?(String) || value.is_a?(Numeric)
|
84
|
+
raise ArgumentError.new("Unit '#{key}' UID is of unsupported type '#{value.class}'. UID must be one of ['string', 'number']")
|
85
|
+
end
|
86
|
+
|
87
|
+
if value.to_s.size.zero?
|
88
|
+
raise ArgumentError.new("Unit '#{key}' UID length must be >= 1")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ABSmartlyConfig
|
4
|
+
attr_accessor :context_data_provider, :context_event_handler,
|
5
|
+
:variable_parser, :scheduler, :context_event_logger,
|
6
|
+
:client, :audience_deserializer
|
7
|
+
def self.create
|
8
|
+
ABSmartlyConfig.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def context_data_provider=(context_data_provider)
|
12
|
+
@context_data_provider = context_data_provider
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def context_event_handler=(context_event_handler)
|
17
|
+
@context_event_handler = context_event_handler
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def context_data_provide
|
22
|
+
@context_event_handler
|
23
|
+
end
|
24
|
+
|
25
|
+
def variable_parser=(variable_parser)
|
26
|
+
@variable_parser = variable_parser
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def scheduler=(scheduler)
|
31
|
+
@scheduler = scheduler
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def context_event_logger=(context_event_logger)
|
36
|
+
@context_event_logger = context_event_logger
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def audience_deserializer=(audience_deserializer)
|
41
|
+
@audience_deserializer = audience_deserializer
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def client=(client)
|
46
|
+
@client = client
|
47
|
+
self
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "arraybuffer"
|
4
|
+
|
5
|
+
module Absmartly
|
6
|
+
class Md5
|
7
|
+
def self.cmn (q, a, b, x, s, t)
|
8
|
+
a = a + q + (x >> 0) + t # TODO >>>
|
9
|
+
((a << s) | (a >> (32 - s))) + b # TODO >>>
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.ff(a, b, c, d, x, s, t)
|
13
|
+
cmn((b & c) | (~b & d), a, b, x, s, t)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.gg(a, b, c, d, x, s, t)
|
17
|
+
cmn((b & d) | (c & ~d), a, b, x, s, t)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.hh(a, b, c, d, x, s, t)
|
21
|
+
cmn(b ^ c ^ d, a, b, x, s, t)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.ii(a, b, c, d, x, s, t)
|
25
|
+
cmn(c ^ (b | ~d), a, b, x, s, t)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.md5_cycle(x, k)
|
29
|
+
a = x[0]
|
30
|
+
b = x[1]
|
31
|
+
c = x[2]
|
32
|
+
d = x[3]
|
33
|
+
|
34
|
+
a = ff(a, b, c, d, k[0], 7, -680876936)
|
35
|
+
d = ff(d, a, b, c, k[1], 12, -389564586)
|
36
|
+
c = ff(c, d, a, b, k[2], 17, 606105819)
|
37
|
+
b = ff(b, c, d, a, k[3], 22, -1044525330)
|
38
|
+
a = ff(a, b, c, d, k[4], 7, -176418897)
|
39
|
+
d = ff(d, a, b, c, k[5], 12, 1200080426)
|
40
|
+
c = ff(c, d, a, b, k[6], 17, -1473231341)
|
41
|
+
b = ff(b, c, d, a, k[7], 22, -45705983)
|
42
|
+
a = ff(a, b, c, d, k[8], 7, 1770035416)
|
43
|
+
d = ff(d, a, b, c, k[9], 12, -1958414417)
|
44
|
+
c = ff(c, d, a, b, k[10], 17, -42063)
|
45
|
+
b = ff(b, c, d, a, k[11], 22, -1990404162)
|
46
|
+
a = ff(a, b, c, d, k[12], 7, 1804603682)
|
47
|
+
d = ff(d, a, b, c, k[13], 12, -40341101)
|
48
|
+
c = ff(c, d, a, b, k[14], 17, -1502002290)
|
49
|
+
b = ff(b, c, d, a, k[15], 22, 1236535329)
|
50
|
+
|
51
|
+
a = gg(a, b, c, d, k[1], 5, -165796510)
|
52
|
+
d = gg(d, a, b, c, k[6], 9, -1069501632)
|
53
|
+
c = gg(c, d, a, b, k[11], 14, 643717713)
|
54
|
+
b = gg(b, c, d, a, k[0], 20, -373897302)
|
55
|
+
a = gg(a, b, c, d, k[5], 5, -701558691)
|
56
|
+
d = gg(d, a, b, c, k[10], 9, 38016083)
|
57
|
+
c = gg(c, d, a, b, k[15], 14, -660478335)
|
58
|
+
b = gg(b, c, d, a, k[4], 20, -405537848)
|
59
|
+
a = gg(a, b, c, d, k[9], 5, 568446438)
|
60
|
+
d = gg(d, a, b, c, k[14], 9, -1019803690)
|
61
|
+
c = gg(c, d, a, b, k[3], 14, -187363961)
|
62
|
+
b = gg(b, c, d, a, k[8], 20, 1163531501)
|
63
|
+
a = gg(a, b, c, d, k[13], 5, -1444681467)
|
64
|
+
d = gg(d, a, b, c, k[2], 9, -51403784)
|
65
|
+
c = gg(c, d, a, b, k[7], 14, 1735328473)
|
66
|
+
b = gg(b, c, d, a, k[12], 20, -1926607734)
|
67
|
+
|
68
|
+
a = hh(a, b, c, d, k[5], 4, -378558)
|
69
|
+
d = hh(d, a, b, c, k[8], 11, -2022574463)
|
70
|
+
c = hh(c, d, a, b, k[11], 16, 1839030562)
|
71
|
+
b = hh(b, c, d, a, k[14], 23, -35309556)
|
72
|
+
a = hh(a, b, c, d, k[1], 4, -1530992060)
|
73
|
+
d = hh(d, a, b, c, k[4], 11, 1272893353)
|
74
|
+
c = hh(c, d, a, b, k[7], 16, -155497632)
|
75
|
+
b = hh(b, c, d, a, k[10], 23, -1094730640)
|
76
|
+
a = hh(a, b, c, d, k[13], 4, 681279174)
|
77
|
+
d = hh(d, a, b, c, k[0], 11, -358537222)
|
78
|
+
c = hh(c, d, a, b, k[3], 16, -722521979)
|
79
|
+
b = hh(b, c, d, a, k[6], 23, 76029189)
|
80
|
+
a = hh(a, b, c, d, k[9], 4, -640364487)
|
81
|
+
d = hh(d, a, b, c, k[12], 11, -421815835)
|
82
|
+
c = hh(c, d, a, b, k[15], 16, 530742520)
|
83
|
+
b = hh(b, c, d, a, k[2], 23, -995338651)
|
84
|
+
|
85
|
+
a = ii(a, b, c, d, k[0], 6, -198630844)
|
86
|
+
d = ii(d, a, b, c, k[7], 10, 1126891415)
|
87
|
+
c = ii(c, d, a, b, k[14], 15, -1416354905)
|
88
|
+
b = ii(b, c, d, a, k[5], 21, -57434055)
|
89
|
+
a = ii(a, b, c, d, k[12], 6, 1700485571)
|
90
|
+
d = ii(d, a, b, c, k[3], 10, -1894986606)
|
91
|
+
c = ii(c, d, a, b, k[10], 15, -1051523)
|
92
|
+
b = ii(b, c, d, a, k[1], 21, -2054922799)
|
93
|
+
a = ii(a, b, c, d, k[8], 6, 1873313359)
|
94
|
+
d = ii(d, a, b, c, k[15], 10, -30611744)
|
95
|
+
c = ii(c, d, a, b, k[6], 15, -1560198380)
|
96
|
+
b = ii(b, c, d, a, k[13], 21, 1309151649)
|
97
|
+
a = ii(a, b, c, d, k[4], 6, -145523070)
|
98
|
+
d = ii(d, a, b, c, k[11], 10, -1120210379)
|
99
|
+
c = ii(c, d, a, b, k[2], 15, 718787259)
|
100
|
+
b = ii(b, c, d, a, k[9], 21, -343485551)
|
101
|
+
|
102
|
+
x[0] = (a + x[0]) >> 0 # TODO >>>
|
103
|
+
x[1] = (b + x[1]) >> 0 # TODO >>>
|
104
|
+
x[2] = (c + x[2]) >> 0 # TODO >>>
|
105
|
+
x[3] = (d + x[3]) >> 0 # TODO >>>
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.process(new_bytes_buffer)
|
109
|
+
key = DataView.new new_bytes_buffer, 0, new_bytes_buffer.length
|
110
|
+
|
111
|
+
l = new_bytes_buffer.length
|
112
|
+
n = l & ~63
|
113
|
+
i = 0
|
114
|
+
block = ArrayBuffer.new(16)
|
115
|
+
state = ArrayBuffer.new(4)
|
116
|
+
|
117
|
+
[1732584193, -271733879, -1732584194, 271733878].each_with_index do |el, index|
|
118
|
+
state[index] = el
|
119
|
+
end
|
120
|
+
|
121
|
+
(0...n).step(64).each do |val|
|
122
|
+
(0...16).each do |w|
|
123
|
+
block[w] = key.getU32(val + (w << 2))
|
124
|
+
end
|
125
|
+
|
126
|
+
md5_cycle(state, block)
|
127
|
+
|
128
|
+
i += 64
|
129
|
+
end
|
130
|
+
|
131
|
+
w = 0
|
132
|
+
m = l & ~3
|
133
|
+
|
134
|
+
(i...m).step(4).each do |val|
|
135
|
+
block[w] = key.getU32(val)
|
136
|
+
w += 1
|
137
|
+
end
|
138
|
+
|
139
|
+
p = l & 3
|
140
|
+
|
141
|
+
case p
|
142
|
+
when 3
|
143
|
+
block[w] = 0x80000000 | key.getU8(i) | (key.getU8(i + 1) << 8) | (key.getU8(i + 2) << 16)
|
144
|
+
w += 1
|
145
|
+
when 2
|
146
|
+
block[w] = 0x800000 | key.getUint8(i) | (key.getUint8(i + 1) << 8)
|
147
|
+
w += 1
|
148
|
+
|
149
|
+
when 1
|
150
|
+
block[w] = 0x8000 | key.getUint8(i)
|
151
|
+
w += 1
|
152
|
+
|
153
|
+
else
|
154
|
+
block[w] = 0x80
|
155
|
+
w += 1
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
if w > 14
|
160
|
+
(w...16).each do |v|
|
161
|
+
block[v] = 0
|
162
|
+
end
|
163
|
+
|
164
|
+
md5_cycle(state, block)
|
165
|
+
w = 0
|
166
|
+
end
|
167
|
+
|
168
|
+
(w...16).each do |v|
|
169
|
+
block[v] = 0
|
170
|
+
end
|
171
|
+
|
172
|
+
block[14] = l << 3
|
173
|
+
md5_cycle(state, block)
|
174
|
+
|
175
|
+
state
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "arraybuffer"
|
4
|
+
require "murmurhash3"
|
5
|
+
|
6
|
+
module Absmartly
|
7
|
+
# VariantAssigner
|
8
|
+
class VariantAssigner
|
9
|
+
def initialize(_unit)
|
10
|
+
# this._unitHash = murmur3_32(stringToUint8Array(unit).buffer);
|
11
|
+
end
|
12
|
+
|
13
|
+
def assign(split, seed_hi, seed_lo)
|
14
|
+
prob = probability(seed_hi, seed_lo)
|
15
|
+
chooseVariant(split, prob)
|
16
|
+
end
|
17
|
+
|
18
|
+
# private
|
19
|
+
|
20
|
+
# def chooseVariant(split, prob)
|
21
|
+
#
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def probability(seedHi, seedLo)
|
25
|
+
# const key = this._unitHash;
|
26
|
+
# const buffer = new ArrayBuffer(12)
|
27
|
+
# const view = new DataView(buffer)
|
28
|
+
# view.setUint32(0, seedLo, true)
|
29
|
+
# view.setUint32(4, seedHi, true)
|
30
|
+
# view.setUint32(8, key, true)
|
31
|
+
#
|
32
|
+
# murmur3_32(buffer) * (1.0 / 0xffffffff)
|
33
|
+
# end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
# DataView.new(new_bytes_buffer, src_offset, new_bytes.length)
|
data/lib/absmartly.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "absmartly/version"
|
4
|
+
require_relative "absmartly/variant_assigner"
|
5
|
+
require_relative "absmartly/md5"
|
6
|
+
require_relative "a_b_smartly"
|
7
|
+
require_relative "a_b_smartly_config"
|
8
|
+
require_relative "client"
|
9
|
+
require_relative "client_config"
|
10
|
+
require_relative "context_config"
|
11
|
+
|
12
|
+
module Absmartly
|
13
|
+
@@init_config = nil
|
14
|
+
|
15
|
+
class Error < StandardError
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
attr_accessor :endpoint, :api_key, :application, :environment
|
20
|
+
|
21
|
+
def configure_client
|
22
|
+
yield self
|
23
|
+
end
|
24
|
+
|
25
|
+
def create
|
26
|
+
ABSmartly.create(sdk_config)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_context_config
|
30
|
+
ContextConfig.create
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_context(context_config)
|
34
|
+
sdk.create_context(context_config)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def client_config
|
39
|
+
@client_config = ClientConfig.create
|
40
|
+
@client_config.endpoint = @endpoint
|
41
|
+
@client_config.api_key = @api_key
|
42
|
+
@client_config.application = @application
|
43
|
+
@client_config.environment = @environment
|
44
|
+
@client_config
|
45
|
+
end
|
46
|
+
|
47
|
+
def sdk_config
|
48
|
+
@sdk_config = ABSmartlyConfig.create
|
49
|
+
@sdk_config.client = Client.create(client_config)
|
50
|
+
@sdk_config
|
51
|
+
end
|
52
|
+
|
53
|
+
def sdk
|
54
|
+
@sdk ||= create
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require_relative "json_expr/json_expr"
|
5
|
+
|
6
|
+
class AudienceMatcher
|
7
|
+
attr_accessor :deserializer, :json_expr
|
8
|
+
|
9
|
+
def initialize(deserializer)
|
10
|
+
@deserializer = deserializer
|
11
|
+
@json_expr = JsonExpr.new
|
12
|
+
end
|
13
|
+
|
14
|
+
class Result
|
15
|
+
attr_accessor :result
|
16
|
+
|
17
|
+
def initialize(result)
|
18
|
+
@result = result
|
19
|
+
end
|
20
|
+
|
21
|
+
def get
|
22
|
+
@result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def evaluate(audience, attributes)
|
27
|
+
audience_map = JSON.parse(audience, symbolize_names: true)
|
28
|
+
|
29
|
+
unless audience_map.nil?
|
30
|
+
filter = audience_map[:filter]
|
31
|
+
if filter.is_a?(Hash) || filter.is_a?(Array)
|
32
|
+
Result.new(@json_expr.evaluate_boolean_expr(filter, attributes))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
rescue
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
end
|
data/lib/client.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "default_http_client"
|
4
|
+
require_relative "default_http_client_config"
|
5
|
+
require_relative "default_context_data_deserializer"
|
6
|
+
require_relative "default_context_event_serializer"
|
7
|
+
|
8
|
+
class Client
|
9
|
+
attr_accessor :url, :query, :headers, :http_client, :executor, :deserializer, :serializer
|
10
|
+
attr_reader :data_future, :promise, :exception
|
11
|
+
|
12
|
+
def self.create(config, http_client = nil)
|
13
|
+
Client.new(config, http_client || DefaultHttpClient.create(DefaultHttpClientConfig.create))
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(config = nil, http_client = nil)
|
17
|
+
endpoint = config.endpoint
|
18
|
+
raise ArgumentError.new("Missing Endpoint configuration") if endpoint.nil? || endpoint.empty?
|
19
|
+
|
20
|
+
api_key = config.api_key
|
21
|
+
raise ArgumentError.new("Missing APIKey configuration") if api_key.nil? || api_key.empty?
|
22
|
+
|
23
|
+
application = config.application
|
24
|
+
raise ArgumentError.new("Missing Application configuration") if application.nil? || application.empty?
|
25
|
+
|
26
|
+
environment = config.environment
|
27
|
+
raise ArgumentError.new("Missing Environment configuration") if environment.nil? || environment.empty?
|
28
|
+
|
29
|
+
@url = "#{endpoint}/context"
|
30
|
+
@http_client = http_client
|
31
|
+
@deserializer = config.context_data_deserializer
|
32
|
+
@serializer = config.context_event_serializer
|
33
|
+
@executor = config.executor
|
34
|
+
|
35
|
+
@deserializer = DefaultContextDataDeserializer.new if @deserializer.nil?
|
36
|
+
@serializer = DefaultContextEventSerializer.new if @serializer.nil?
|
37
|
+
|
38
|
+
@headers = {
|
39
|
+
"Content-Type": "application/json",
|
40
|
+
"X-API-Key": api_key,
|
41
|
+
"X-Application": application,
|
42
|
+
"X-Environment": environment,
|
43
|
+
"X-Application-Version": "0",
|
44
|
+
"X-Agent": "absmartly-ruby-sdk"
|
45
|
+
}
|
46
|
+
|
47
|
+
@query = {
|
48
|
+
"application": application,
|
49
|
+
"environment": environment
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def context_data
|
54
|
+
@promise = @http_client.get(@url, @query, @headers)
|
55
|
+
unless @promise.success?
|
56
|
+
@exception = Exception.new(@promise.body)
|
57
|
+
return self
|
58
|
+
end
|
59
|
+
|
60
|
+
content = (@promise.body || {}).to_s
|
61
|
+
@data_future = @deserializer.deserialize(content, 0, content.size)
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
def publish(event)
|
66
|
+
content = @serializer.serialize(event)
|
67
|
+
response = @http_client.put(@url, nil, @headers, content)
|
68
|
+
return Exception.new(response.body) unless response.success?
|
69
|
+
|
70
|
+
response
|
71
|
+
end
|
72
|
+
|
73
|
+
def close
|
74
|
+
@http_client.close
|
75
|
+
end
|
76
|
+
|
77
|
+
def success?
|
78
|
+
@promise.success?
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ClientConfig
|
4
|
+
attr_accessor :endpoint, :api_key, :environment, :application, :deserializer,
|
5
|
+
:serializer, :executor
|
6
|
+
|
7
|
+
def self.create
|
8
|
+
ClientConfig.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.create_from_properties(properties, prefix)
|
12
|
+
properties = properties.transform_keys(&:to_sym)
|
13
|
+
client_config = create
|
14
|
+
client_config.endpoint = properties["#{prefix}endpoint".to_sym]
|
15
|
+
client_config.environment = properties["#{prefix}environment".to_sym]
|
16
|
+
client_config.application = properties["#{prefix}application".to_sym]
|
17
|
+
client_config.api_key = properties["#{prefix}apikey".to_sym]
|
18
|
+
client_config
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(endpoint: nil, environment: nil, application: nil, api_key: nil)
|
22
|
+
@endpoint = endpoint
|
23
|
+
@environment = environment
|
24
|
+
@application = application
|
25
|
+
@api_key = api_key
|
26
|
+
end
|
27
|
+
|
28
|
+
def context_data_deserializer
|
29
|
+
@deserializer
|
30
|
+
end
|
31
|
+
|
32
|
+
def context_data_deserializer=(deserializer)
|
33
|
+
@deserializer = deserializer
|
34
|
+
end
|
35
|
+
|
36
|
+
def context_event_serializer
|
37
|
+
@serializer
|
38
|
+
end
|
39
|
+
|
40
|
+
def context_event_serializer=(serializer)
|
41
|
+
@serializer = serializer
|
42
|
+
end
|
43
|
+
end
|