devcycle-ruby-server-sdk 2.0.2 → 2.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.
- checksums.yaml +4 -4
- data/lib/devcycle-ruby-server-sdk/api/devcycle_api.rb +78 -34
- data/lib/devcycle-ruby-server-sdk/localbucketing/bucketing-lib.release.wasm +0 -0
- data/lib/devcycle-ruby-server-sdk/localbucketing/config_manager.rb +45 -31
- data/lib/devcycle-ruby-server-sdk/localbucketing/local_bucketing.rb +138 -91
- data/lib/devcycle-ruby-server-sdk/models/variable.rb +8 -2
- data/lib/devcycle-ruby-server-sdk/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66277b1d5bea79db7794168b2f9be005a3c37ef6ea0dc1d9b0cbf876c5fb1c17
|
4
|
+
data.tar.gz: 94381d579783bffc333ac56391ea530466c796a44ce700c25dc99267409412ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b958f804a26259dadd8f33d46a0326d9c4ca5a8aad5c0a230ffdbb54d0adc7d82714677add429dc5161c855d31a0e1a19fdc0461db70a0945436285160996b2
|
7
|
+
data.tar.gz: 731696f8b1129e8151031d8ede467f646f0c910f747a36dfc9ab54b127db6097eddfcbb35afc2cf1d49da2d53a8c22e20e0d908f9a6371845a7f67e63a84b701
|
@@ -32,8 +32,8 @@ module DevCycle
|
|
32
32
|
@api_client.config.enable_edge_db = @dvc_options.enable_edge_db
|
33
33
|
@api_client.config.logger = @logger
|
34
34
|
else
|
35
|
-
@
|
36
|
-
@event_queue = EventQueue.new(@sdkKey, dvc_options.event_queue_options, @
|
35
|
+
@local_bucketing = LocalBucketing.new(@sdkKey, dvc_options, wait_for_init)
|
36
|
+
@event_queue = EventQueue.new(@sdkKey, dvc_options.event_queue_options, @local_bucketing)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -42,15 +42,15 @@ module DevCycle
|
|
42
42
|
@logger.info("Cloud Bucketing does not require closing.")
|
43
43
|
return
|
44
44
|
end
|
45
|
-
if @
|
46
|
-
if !@
|
45
|
+
if @local_bucketing != nil
|
46
|
+
if !@local_bucketing.initialized
|
47
47
|
@logger.info("Awaiting client initialization before closing")
|
48
|
-
while !@
|
48
|
+
while !@local_bucketing.initialized
|
49
49
|
sleep(0.5)
|
50
50
|
end
|
51
51
|
end
|
52
|
-
@
|
53
|
-
@
|
52
|
+
@local_bucketing.close
|
53
|
+
@local_bucketing = nil
|
54
54
|
@logger.info("Closed DevCycle Local Bucketing Engine.")
|
55
55
|
end
|
56
56
|
|
@@ -60,10 +60,16 @@ module DevCycle
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def set_client_custom_data(customdata)
|
63
|
-
if @
|
64
|
-
|
63
|
+
if @dvc_options.enable_cloud_bucketing
|
64
|
+
raise StandardError.new("Client Custom Data is only available in Local bucketing mode.")
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
|
+
if local_bucketing_initialized?
|
68
|
+
@local_bucketing.set_client_custom_data(customdata)
|
69
|
+
else
|
70
|
+
@logger.warn("Local bucketing not initialized. Unable to set client custom data.")
|
71
|
+
end
|
72
|
+
nil
|
67
73
|
end
|
68
74
|
|
69
75
|
def validate_model(model)
|
@@ -87,8 +93,8 @@ module DevCycle
|
|
87
93
|
return data
|
88
94
|
end
|
89
95
|
|
90
|
-
if local_bucketing_initialized?
|
91
|
-
bucketed_config = @
|
96
|
+
if local_bucketing_initialized? && @local_bucketing.has_config
|
97
|
+
bucketed_config = @local_bucketing.generate_bucketed_config(user_data)
|
92
98
|
bucketed_config.features
|
93
99
|
else
|
94
100
|
{}
|
@@ -170,31 +176,40 @@ module DevCycle
|
|
170
176
|
return data
|
171
177
|
end
|
172
178
|
|
173
|
-
|
174
|
-
|
175
|
-
|
179
|
+
value = default
|
180
|
+
type = determine_variable_type(default)
|
181
|
+
defaulted = true
|
182
|
+
if local_bucketing_initialized? && @local_bucketing.has_config
|
183
|
+
type_code = variable_type_code_from_type(type)
|
184
|
+
variable_json = variable_for_user(user_data, key, type_code)
|
176
185
|
if variable_json == nil
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
186
|
+
@logger.warn("No variable found for key #{key}, returning default value")
|
187
|
+
elsif type != variable_json['type']
|
188
|
+
@logger.warn("Type mismatch for variable #{key}, returning default value")
|
189
|
+
else
|
190
|
+
value = variable_json['value']
|
191
|
+
defaulted = false
|
181
192
|
end
|
182
|
-
|
183
|
-
variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_evaluated], target: key })
|
184
|
-
@event_queue.queue_aggregate_event(variable_event, bucketed_config)
|
185
|
-
|
186
|
-
Variable.new({
|
187
|
-
key: key,
|
188
|
-
type: variable_json['type'],
|
189
|
-
value: variable_json['value'],
|
190
|
-
isDefaulted: false
|
191
|
-
})
|
192
193
|
else
|
194
|
+
@logger.warn("Local bucketing not initialized, returning default value for variable #{key}")
|
193
195
|
variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_defaulted], target: key })
|
196
|
+
bucketed_config = BucketedUserConfig.new({}, {}, {}, {}, {}, {}, [])
|
194
197
|
@event_queue.queue_aggregate_event(variable_event, bucketed_config)
|
195
|
-
|
196
|
-
Variable.new({ key: key, value: default, isDefaulted: true })
|
197
198
|
end
|
199
|
+
|
200
|
+
Variable.new({
|
201
|
+
key: key,
|
202
|
+
value: value,
|
203
|
+
type: type,
|
204
|
+
defaultValue: default,
|
205
|
+
isDefaulted: defaulted
|
206
|
+
})
|
207
|
+
end
|
208
|
+
|
209
|
+
def variable_for_user(user, key, variable_type_code)
|
210
|
+
json_str = @local_bucketing.variable_for_user(user, key, variable_type_code)
|
211
|
+
return nil if json_str.nil?
|
212
|
+
JSON.parse(json_str)
|
198
213
|
end
|
199
214
|
|
200
215
|
# Get variable by key for user data
|
@@ -284,8 +299,8 @@ module DevCycle
|
|
284
299
|
return data
|
285
300
|
end
|
286
301
|
|
287
|
-
if local_bucketing_initialized?
|
288
|
-
bucketed_config = @
|
302
|
+
if local_bucketing_initialized? && @local_bucketing.has_config
|
303
|
+
bucketed_config = @local_bucketing.generate_bucketed_config(user_data)
|
289
304
|
bucketed_config.variables
|
290
305
|
else
|
291
306
|
{}
|
@@ -451,7 +466,36 @@ module DevCycle
|
|
451
466
|
end
|
452
467
|
|
453
468
|
def local_bucketing_initialized?
|
454
|
-
!@
|
469
|
+
!@local_bucketing.nil? && @local_bucketing.initialized
|
470
|
+
end
|
471
|
+
|
472
|
+
def determine_variable_type(variable_value)
|
473
|
+
if variable_value.is_a?(String)
|
474
|
+
'String'
|
475
|
+
elsif variable_value.is_a?(TrueClass) || variable_value.is_a?(FalseClass)
|
476
|
+
'Boolean'
|
477
|
+
elsif variable_value.is_a?(Integer) || variable_value.is_a?(Float)
|
478
|
+
'Number'
|
479
|
+
elsif variable_value.is_a?(Hash)
|
480
|
+
'JSON'
|
481
|
+
else
|
482
|
+
raise ArgumentError, "Invalid type for variable: #{variable_value}"
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
def variable_type_code_from_type(type)
|
487
|
+
case type
|
488
|
+
when 'String'
|
489
|
+
@local_bucketing.variable_type_codes[:string]
|
490
|
+
when 'Boolean'
|
491
|
+
@local_bucketing.variable_type_codes[:boolean]
|
492
|
+
when 'Number'
|
493
|
+
@local_bucketing.variable_type_codes[:number]
|
494
|
+
when 'JSON'
|
495
|
+
@local_bucketing.variable_type_codes[:json]
|
496
|
+
else
|
497
|
+
raise ArgumentError.new("Invalid type for variable: #{type}")
|
498
|
+
end
|
455
499
|
end
|
456
500
|
end
|
457
501
|
end
|
Binary file
|
@@ -20,19 +20,33 @@ module DevCycle
|
|
20
20
|
@sdkKey = sdkKey
|
21
21
|
@config_e_tag = ""
|
22
22
|
@logger = local_bucketing.options.logger
|
23
|
+
@polling_enabled = true
|
24
|
+
@max_config_retries = 2
|
23
25
|
|
24
|
-
@config_poller = Concurrent::TimerTask.new(
|
25
|
-
{
|
26
|
+
@config_poller = Concurrent::TimerTask.new({
|
26
27
|
execution_interval: @local_bucketing.options.config_polling_interval_ms.fdiv(1000)
|
27
28
|
}) do |task|
|
28
|
-
fetch_config
|
29
|
+
fetch_config
|
29
30
|
end
|
30
31
|
|
31
|
-
t = Thread.new {
|
32
|
+
t = Thread.new { initialize_config }
|
32
33
|
t.join if wait_for_init
|
33
34
|
end
|
34
35
|
|
35
|
-
def
|
36
|
+
def initialize_config
|
37
|
+
begin
|
38
|
+
fetch_config
|
39
|
+
@config_poller.execute if @polling_enabled
|
40
|
+
rescue => e
|
41
|
+
@logger.error("DVC Error Initializing Config: #{e.message}")
|
42
|
+
ensure
|
43
|
+
@local_bucketing.initialized = true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def fetch_config
|
48
|
+
return unless @polling_enabled
|
49
|
+
|
36
50
|
req = Typhoeus::Request.new(
|
37
51
|
get_config_url,
|
38
52
|
headers: {
|
@@ -43,25 +57,26 @@ module DevCycle
|
|
43
57
|
req.options[:headers]['If-None-Match'] = @config_e_tag
|
44
58
|
end
|
45
59
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
60
|
+
@max_config_retries.times do
|
61
|
+
resp = req.run
|
62
|
+
case resp.code
|
63
|
+
when 304
|
64
|
+
@logger.debug("Config not modified, using cache, etag: #{this.configEtag}")
|
65
|
+
return
|
66
|
+
when 200
|
67
|
+
set_config(resp.body, resp.headers['Etag'])
|
68
|
+
return
|
69
|
+
when 403
|
70
|
+
stop_polling
|
71
|
+
@logger.error("Failed to download DevCycle config; Invalid SDK Key.")
|
72
|
+
return
|
73
|
+
when 500...599
|
74
|
+
@logger.error("Failed to download DevCycle config. Status: #{resp.code}")
|
75
|
+
else
|
76
|
+
stop_polling
|
77
|
+
@logger.error("Unexpected response code - DevCycle Response: #{Oj.dump(resp)}")
|
78
|
+
return
|
58
79
|
end
|
59
|
-
@logger.warn("Failed to download DevCycle config. Status: #{resp.code}")
|
60
|
-
else
|
61
|
-
if task != nil
|
62
|
-
task.shutdown
|
63
|
-
end
|
64
|
-
raise("Unexpected response code - DevCycle Response: #{Oj.dump(resp)}")
|
65
80
|
end
|
66
81
|
|
67
82
|
nil
|
@@ -74,13 +89,7 @@ module DevCycle
|
|
74
89
|
|
75
90
|
@local_bucketing.store_config(config)
|
76
91
|
@config_e_tag = etag
|
77
|
-
|
78
|
-
if @first_load
|
79
|
-
@logger.info("Config Set. Client Initialized.")
|
80
|
-
@first_load = false
|
81
|
-
@local_bucketing.initialized = true
|
82
|
-
@config_poller.execute
|
83
|
-
end
|
92
|
+
@local_bucketing.has_config = true
|
84
93
|
end
|
85
94
|
|
86
95
|
def get_config_url
|
@@ -88,8 +97,13 @@ module DevCycle
|
|
88
97
|
"#{configBasePath}/config/#{@config_version}/server/#{@sdkKey}.json"
|
89
98
|
end
|
90
99
|
|
100
|
+
def stop_polling
|
101
|
+
@polling_enabled = false
|
102
|
+
@config_poller.shutdown if @config_poller.running?
|
103
|
+
end
|
104
|
+
|
91
105
|
def close
|
92
|
-
@config_poller.shutdown
|
106
|
+
@config_poller.shutdown if @config_poller.running?
|
93
107
|
nil
|
94
108
|
end
|
95
109
|
end
|
@@ -13,7 +13,9 @@ module DevCycle
|
|
13
13
|
extend T::Sig
|
14
14
|
|
15
15
|
attr_reader :options
|
16
|
+
attr_reader :variable_type_codes
|
16
17
|
attr_accessor :initialized
|
18
|
+
attr_accessor :has_config
|
17
19
|
|
18
20
|
@@rand = Random.new(seed = Random.new_seed)
|
19
21
|
@@engine = Wasmtime::Engine.new
|
@@ -47,6 +49,8 @@ module DevCycle
|
|
47
49
|
result
|
48
50
|
}
|
49
51
|
|
52
|
+
@@stack_tracer_raise = lambda { |message| raise message }
|
53
|
+
# each method reassigns stack_tracer so the call stack is properly displayed
|
50
54
|
@@stack_tracer = lambda {}
|
51
55
|
|
52
56
|
@@linker.func_new("env", "abort", [:i32, :i32, :i32, :i32], []) do |_caller, messagePtr, filenamePtr, lineNum, colNum|
|
@@ -86,139 +90,182 @@ module DevCycle
|
|
86
90
|
).void }
|
87
91
|
def initialize(sdkkey, options, wait_for_init)
|
88
92
|
@initialized = false
|
93
|
+
@has_config = false
|
89
94
|
@sdkkey = sdkkey
|
90
95
|
@options = options
|
91
96
|
@logger = options.logger
|
97
|
+
@wasm_mutex = Mutex.new
|
98
|
+
@variable_type_codes = {
|
99
|
+
boolean: @@instance.export("VariableType.Boolean").to_global.get.to_i,
|
100
|
+
string: @@instance.export("VariableType.String").to_global.get.to_i,
|
101
|
+
number: @@instance.export("VariableType.Number").to_global.get.to_i,
|
102
|
+
json: @@instance.export("VariableType.JSON").to_global.get.to_i
|
103
|
+
}
|
92
104
|
set_sdk_key_internal(sdkkey)
|
93
105
|
platform_data = PlatformData.new('server', VERSION, RUBY_VERSION, nil, 'Ruby', Socket.gethostname)
|
94
106
|
set_platform_data(platform_data)
|
95
|
-
@
|
107
|
+
@config_manager = ConfigManager.new(@sdkkey, self, wait_for_init)
|
96
108
|
end
|
97
109
|
|
98
110
|
def close
|
99
|
-
@
|
100
|
-
@
|
111
|
+
@config_manager.close
|
112
|
+
@config_manager = nil
|
101
113
|
end
|
102
114
|
|
103
115
|
sig { params(user: UserData).returns(BucketedUserConfig) }
|
104
116
|
def generate_bucketed_config(user)
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
117
|
+
@wasm_mutex.synchronize do
|
118
|
+
user_addr = malloc_asc_string(user.to_json)
|
119
|
+
@@stack_tracer = @@stack_tracer_raise
|
120
|
+
config_addr = @@instance.invoke("generateBucketedConfigForUser", @sdkKeyAddr, user_addr)
|
121
|
+
bucketed_config_json = read_asc_string(config_addr)
|
122
|
+
bucketed_config_hash = Oj.load(bucketed_config_json)
|
123
|
+
|
124
|
+
BucketedUserConfig.new(bucketed_config_hash['project'],
|
125
|
+
bucketed_config_hash['environment'],
|
126
|
+
bucketed_config_hash['features'],
|
127
|
+
bucketed_config_hash['featureVariationMap'],
|
128
|
+
bucketed_config_hash['variableVariationMap'],
|
129
|
+
bucketed_config_hash['variables'],
|
130
|
+
bucketed_config_hash['knownVariableKeys'])
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
sig { params(user: UserData, key: String, variable_type: Integer).returns(T.nilable(String)) }
|
135
|
+
def variable_for_user(user, key, variable_type)
|
136
|
+
@wasm_mutex.synchronize do
|
137
|
+
user_addr = malloc_asc_string(user.to_json)
|
138
|
+
key_addr = malloc_asc_string(key)
|
139
|
+
@@stack_tracer = @@stack_tracer_raise
|
140
|
+
var_addr = @@instance.invoke("variableForUser", @sdkKeyAddr, user_addr, key_addr, variable_type, 1)
|
141
|
+
read_asc_string(var_addr)
|
142
|
+
end
|
118
143
|
end
|
119
144
|
|
120
145
|
sig { returns(T::Array[EventsPayload]) }
|
121
146
|
def flush_event_queue
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
147
|
+
@wasm_mutex.synchronize do
|
148
|
+
@@stack_tracer = @@stack_tracer_raise
|
149
|
+
payload_addr = @@instance.invoke("flushEventQueue", @sdkKeyAddr)
|
150
|
+
raw_json = read_asc_string(payload_addr)
|
151
|
+
raw_payloads = Oj.load(raw_json)
|
152
|
+
|
153
|
+
if raw_payloads == nil
|
154
|
+
return []
|
155
|
+
end
|
156
|
+
raw_payloads.map { |raw_payload| EventsPayload.new(raw_payload["records"], raw_payload["payloadId"], raw_payload["eventCount"]) }
|
129
157
|
end
|
130
|
-
raw_payloads.map { |raw_payload| EventsPayload.new(raw_payload["records"], raw_payload["payloadId"], raw_payload["eventCount"]) }
|
131
158
|
end
|
132
159
|
|
133
160
|
sig { returns(Integer) }
|
134
161
|
def check_event_queue_size
|
135
|
-
|
136
|
-
|
162
|
+
@wasm_mutex.synchronize do
|
163
|
+
@@stack_tracer = @@stack_tracer_raise
|
164
|
+
@@instance.invoke("eventQueueSize", @sdkKeyAddr)
|
165
|
+
end
|
137
166
|
end
|
138
167
|
|
139
168
|
sig { params(payload_id: String).returns(NilClass) }
|
140
169
|
def on_payload_success(payload_id)
|
141
|
-
|
142
|
-
|
143
|
-
|
170
|
+
@wasm_mutex.synchronize do
|
171
|
+
payload_addr = malloc_asc_string(payload_id)
|
172
|
+
@@stack_tracer = @@stack_tracer_raise
|
173
|
+
@@instance.invoke("onPayloadSuccess", @sdkKeyAddr, payload_addr)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
sig { params(payload_id: String, retryable: Object).returns(NilClass) }
|
178
|
+
def on_payload_failure(payload_id, retryable)
|
179
|
+
@wasm_mutex.synchronize do
|
180
|
+
payload_addr = malloc_asc_string(payload_id)
|
181
|
+
@@stack_tracer = @@stack_tracer_raise
|
182
|
+
@@instance.invoke("onPayloadFailure", @sdkKeyAddr, payload_addr, retryable ? 1 : 0)
|
183
|
+
end
|
144
184
|
end
|
145
185
|
|
146
186
|
sig { params(user: UserData, event: Event).returns(NilClass) }
|
147
187
|
def queue_event(user, event)
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
188
|
+
@wasm_mutex.synchronize do
|
189
|
+
begin
|
190
|
+
user_addr = malloc_asc_string(Oj.dump(user))
|
191
|
+
asc_pin(user_addr)
|
192
|
+
event_addr = malloc_asc_string(Oj.dump(event))
|
193
|
+
@@stack_tracer = @@stack_tracer_raise
|
194
|
+
@@instance.invoke("queueEvent", @sdkKeyAddr, user_addr, event_addr)
|
195
|
+
ensure
|
196
|
+
asc_unpin(user_addr)
|
197
|
+
end
|
156
198
|
end
|
157
199
|
end
|
158
200
|
|
159
201
|
sig { params(event: Event, bucketeduser: T.nilable(BucketedUserConfig)).returns(NilClass) }
|
160
202
|
def queue_aggregate_event(event, bucketeduser)
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
bucketeduser.
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
203
|
+
@wasm_mutex.synchronize do
|
204
|
+
begin
|
205
|
+
variable_variation_map =
|
206
|
+
if !bucketeduser.nil?
|
207
|
+
bucketeduser.variable_variation_map
|
208
|
+
else
|
209
|
+
{}
|
210
|
+
end
|
211
|
+
varmap_addr = malloc_asc_string(Oj.dump(variable_variation_map))
|
212
|
+
asc_pin(varmap_addr)
|
213
|
+
event_addr = malloc_asc_string(Oj.dump(event))
|
214
|
+
@@stack_tracer = @@stack_tracer_raise
|
215
|
+
@@instance.invoke("queueAggregateEvent", @sdkKeyAddr, event_addr, varmap_addr)
|
216
|
+
ensure
|
217
|
+
asc_unpin(varmap_addr)
|
218
|
+
end
|
175
219
|
end
|
176
220
|
end
|
177
221
|
|
178
|
-
sig { params(payload_id: String, retryable: Object).returns(NilClass) }
|
179
|
-
def on_payload_failure(payload_id, retryable)
|
180
|
-
payload_addr = malloc_asc_string(payload_id)
|
181
|
-
@@stack_tracer = lambda { |message| raise message }
|
182
|
-
@@instance.invoke("onPayloadFailure", @sdkKeyAddr, payload_addr, retryable ? 1 : 0)
|
183
|
-
end
|
184
|
-
|
185
222
|
sig { params(config: String).returns(NilClass) }
|
186
223
|
def store_config(config)
|
187
|
-
|
188
|
-
|
189
|
-
|
224
|
+
@wasm_mutex.synchronize do
|
225
|
+
config_addr = malloc_asc_string(config)
|
226
|
+
@@stack_tracer = @@stack_tracer_raise
|
227
|
+
@@instance.invoke("setConfigData", @sdkKeyAddr, config_addr)
|
228
|
+
end
|
190
229
|
end
|
191
230
|
|
192
231
|
sig { params(options: EventQueueOptions).returns(NilClass) }
|
193
232
|
def init_event_queue(options)
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
233
|
+
@wasm_mutex.synchronize do
|
234
|
+
options_json = Oj.dump(options)
|
235
|
+
options_addr = malloc_asc_string(options_json)
|
236
|
+
@@stack_tracer = @@stack_tracer_raise
|
237
|
+
@@instance.invoke("initEventQueue", @sdkKeyAddr, options_addr)
|
238
|
+
end
|
198
239
|
end
|
199
240
|
|
200
241
|
sig { params(customdata: Hash).returns(NilClass) }
|
201
242
|
def set_client_custom_data(customdata)
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
243
|
+
@wasm_mutex.synchronize do
|
244
|
+
customdata_json = Oj.dump(customdata)
|
245
|
+
customdata_addr = malloc_asc_string(customdata_json)
|
246
|
+
@@stack_tracer = @@stack_tracer_raise
|
247
|
+
@@instance.invoke("setClientCustomData", @sdkKeyAddr, customdata_addr)
|
248
|
+
end
|
206
249
|
end
|
207
250
|
|
208
251
|
private
|
209
252
|
|
210
253
|
sig { params(platformdata: PlatformData).returns(NilClass) }
|
211
254
|
def set_platform_data(platformdata)
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
255
|
+
@wasm_mutex.synchronize do
|
256
|
+
platformdata_json = Oj.dump(platformdata)
|
257
|
+
platformdata_addr = malloc_asc_string(platformdata_json)
|
258
|
+
@@stack_tracer = @@stack_tracer_raise
|
259
|
+
@@instance.invoke("setPlatformData", platformdata_addr)
|
260
|
+
end
|
216
261
|
end
|
217
262
|
|
218
263
|
def set_sdk_key_internal(sdkKey)
|
219
|
-
|
220
|
-
|
221
|
-
|
264
|
+
@wasm_mutex.synchronize do
|
265
|
+
addr = malloc_asc_string(sdkKey)
|
266
|
+
@sdkKeyAddr = addr
|
267
|
+
asc_pin(addr)
|
268
|
+
end
|
222
269
|
end
|
223
270
|
|
224
271
|
def asc_pin(addr)
|
@@ -234,16 +281,16 @@ module DevCycle
|
|
234
281
|
sig { params(string: String).returns(Integer) }
|
235
282
|
def malloc_asc_string(string)
|
236
283
|
wasm_object_id = 1
|
237
|
-
@@stack_tracer =
|
284
|
+
@@stack_tracer = @@stack_tracer_raise
|
238
285
|
wasm_new = @@instance.export("__new").to_func
|
239
|
-
utf8_bytes = string.
|
286
|
+
utf8_bytes = string.bytes
|
240
287
|
byte_len = utf8_bytes.length
|
241
288
|
|
242
289
|
start_addr = wasm_new.call(byte_len * 2, wasm_object_id)
|
243
290
|
i = 0
|
244
291
|
while i < byte_len
|
245
|
-
@@stack_tracer =
|
246
|
-
@@memory.write(start_addr + (i * 2), [utf8_bytes[i]].pack('
|
292
|
+
@@stack_tracer = @@stack_tracer_raise
|
293
|
+
@@memory.write(start_addr + (i * 2), [utf8_bytes[i]].pack('c'))
|
247
294
|
i += 1
|
248
295
|
end
|
249
296
|
start_addr
|
@@ -251,23 +298,23 @@ module DevCycle
|
|
251
298
|
|
252
299
|
# @param [Integer] address start address of string.
|
253
300
|
# @return [String] resulting string
|
254
|
-
sig { params(address: Integer).returns(String) }
|
301
|
+
sig { params(address: Integer).returns(T.nilable(String)) }
|
255
302
|
def read_asc_string(address)
|
256
|
-
|
303
|
+
if address == 0
|
304
|
+
@logger.debug("null address passed to read_asc_string")
|
305
|
+
return nil
|
306
|
+
end
|
307
|
+
|
308
|
+
@@stack_tracer = @@stack_tracer_raise
|
257
309
|
raw_bytes = @@memory.read(address - 4, 4).bytes.reverse
|
258
310
|
len = 0
|
259
311
|
raw_bytes.each { |j|
|
260
312
|
len = (len << 8) + (j & 0xFF)
|
261
313
|
}
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
result += @@memory.read(address + i, 1)
|
267
|
-
i += 2
|
268
|
-
end
|
269
|
-
result
|
314
|
+
|
315
|
+
@@stack_tracer = @@stack_tracer_raise
|
316
|
+
result = @@memory.read(address, len).bytes
|
317
|
+
result.select.with_index { |_, i| i.even? }.pack('c*')
|
270
318
|
end
|
271
319
|
end
|
272
320
|
end
|
273
|
-
|
@@ -27,6 +27,8 @@ module DevCycle
|
|
27
27
|
# Set to true if the Variable could not be fetched
|
28
28
|
attr_accessor :isDefaulted
|
29
29
|
|
30
|
+
attr_accessor :defaultValue
|
31
|
+
|
30
32
|
class EnumAttributeValidator
|
31
33
|
attr_reader :datatype
|
32
34
|
attr_reader :allowable_values
|
@@ -55,6 +57,7 @@ module DevCycle
|
|
55
57
|
:'key' => :'key',
|
56
58
|
:'type' => :'type',
|
57
59
|
:'value' => :'value',
|
60
|
+
:'defaultValue' => :'defaultValue',
|
58
61
|
:'isDefaulted' => :'isDefaulted'
|
59
62
|
}
|
60
63
|
end
|
@@ -70,6 +73,7 @@ module DevCycle
|
|
70
73
|
:'key' => :'String',
|
71
74
|
:'type' => :'String',
|
72
75
|
:'value' => :'Object',
|
76
|
+
:'defaultValue' => :'Object',
|
73
77
|
:'isDefaulted' => :'Boolean'
|
74
78
|
}
|
75
79
|
end
|
@@ -112,6 +116,10 @@ module DevCycle
|
|
112
116
|
else
|
113
117
|
self.isDefaulted = false
|
114
118
|
end
|
119
|
+
|
120
|
+
if attributes.key?(:'defaultValue')
|
121
|
+
self.defaultValue = attributes[:'defaultValue']
|
122
|
+
end
|
115
123
|
end
|
116
124
|
|
117
125
|
# Show invalid properties with the reasons. Usually used together with valid?
|
@@ -296,7 +304,5 @@ module DevCycle
|
|
296
304
|
value
|
297
305
|
end
|
298
306
|
end
|
299
|
-
|
300
307
|
end
|
301
|
-
|
302
308
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: devcycle-ruby-server-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- DevCycleHQ
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: typhoeus
|