devcycle-ruby-server-sdk 2.0.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86e5a04ee7157791c8ed075df281ecc09bad3ae898c7e48d520046953ed5aee7
4
- data.tar.gz: 44271e4418ab7b2219403d9a90bbc702befabb034446bc58a2f32a365038f9cb
3
+ metadata.gz: 66277b1d5bea79db7794168b2f9be005a3c37ef6ea0dc1d9b0cbf876c5fb1c17
4
+ data.tar.gz: 94381d579783bffc333ac56391ea530466c796a44ce700c25dc99267409412ed
5
5
  SHA512:
6
- metadata.gz: f5946e97ac8b2d0abda40af307609035945e1881587edc5d1d37c01fc1f75e9e0461527f3c232713ba0c839834b2447e4ab95a3f7a471e314fe28c01c15f3281
7
- data.tar.gz: 147519317a620feef9d975bcb3a5cb844c94e03b7f6fb8631d34e98b57b770a5994c4324dd9c1359e7e744b0b5652f6a5f612fdbc0f68a600816187717b22c43
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
- @localbucketing = LocalBucketing.new(@sdkKey, dvc_options, wait_for_init)
36
- @event_queue = EventQueue.new(@sdkKey, dvc_options.event_queue_options, @localbucketing)
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 @localbucketing != nil
46
- if !@localbucketing.initialized
45
+ if @local_bucketing != nil
46
+ if !@local_bucketing.initialized
47
47
  @logger.info("Awaiting client initialization before closing")
48
- while !@localbucketing.initialized
48
+ while !@local_bucketing.initialized
49
49
  sleep(0.5)
50
50
  end
51
51
  end
52
- @localbucketing.close
53
- @localbucketing = nil
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 @api_client.config.enable_cloud_bucketing
64
- fail ArgumentError("Client Custom Data is only available in Local bucketing mode.")
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
- @localbucketing.set_client_custom_data(customdata)
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 = @localbucketing.generate_bucketed_config(user_data)
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
- if local_bucketing_initialized?
174
- bucketed_config = @localbucketing.generate_bucketed_config(user_data)
175
- variable_json = bucketed_config.variables[key]
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
- variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_defaulted], target: key })
178
- @event_queue.queue_aggregate_event(variable_event, bucketed_config)
179
-
180
- return Variable.new({ key: key, value: default, isDefaulted: true })
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 = @localbucketing.generate_bucketed_config(user_data)
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
- !@localbucketing.nil? && @localbucketing.initialized
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
@@ -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(false, task)
29
+ fetch_config
29
30
  end
30
31
 
31
- t = Thread.new { fetch_config(false, nil) }
32
+ t = Thread.new { initialize_config }
32
33
  t.join if wait_for_init
33
34
  end
34
35
 
35
- def fetch_config(retrying, task)
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
- resp = req.run
47
-
48
- case resp.code
49
- when 304
50
- return nil
51
- when 200
52
- return set_config(resp.body, resp.headers['Etag'])
53
- when 403
54
- raise("Failed to download DevCycle config; Invalid SDK Key.")
55
- when 500...599
56
- if !retrying
57
- return fetch_config(true, task)
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
- @configmanager = ConfigManager.new(@sdkkey, self, wait_for_init)
107
+ @config_manager = ConfigManager.new(@sdkkey, self, wait_for_init)
96
108
  end
97
109
 
98
110
  def close
99
- @configmanager.close
100
- @configmanager = nil
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
- user_addr = malloc_asc_string(user.to_json)
106
- @@stack_tracer = lambda { |message| raise message }
107
- config_addr = @@instance.invoke("generateBucketedConfigForUser", @sdkKeyAddr, user_addr)
108
- bucketed_config_json = read_asc_string(config_addr)
109
- bucketed_config_hash = Oj.load(bucketed_config_json)
110
-
111
- BucketedUserConfig.new(bucketed_config_hash['project'],
112
- bucketed_config_hash['environment'],
113
- bucketed_config_hash['features'],
114
- bucketed_config_hash['featureVariationMap'],
115
- bucketed_config_hash['variableVariationMap'],
116
- bucketed_config_hash['variables'],
117
- bucketed_config_hash['knownVariableKeys'])
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
- @@stack_tracer = lambda { |message| raise message }
123
- payload_addr = @@instance.invoke("flushEventQueue", @sdkKeyAddr)
124
- raw_json = read_asc_string(payload_addr)
125
- raw_payloads = Oj.load(raw_json)
126
-
127
- if raw_payloads == nil
128
- return []
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
- @@stack_tracer = lambda { |message| raise message }
136
- @@instance.invoke("eventQueueSize", @sdkKeyAddr)
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
- payload_addr = malloc_asc_string(payload_id)
142
- @@stack_tracer = lambda { |message| raise message }
143
- @@instance.invoke("onPayloadSuccess", @sdkKeyAddr, payload_addr)
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
- begin
149
- user_addr = malloc_asc_string(Oj.dump(user))
150
- asc_pin(user_addr)
151
- event_addr = malloc_asc_string(Oj.dump(event))
152
- @@stack_tracer = lambda { |message| raise message }
153
- @@instance.invoke("queueEvent", @sdkKeyAddr, user_addr, event_addr)
154
- ensure
155
- asc_unpin(user_addr)
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
- begin
162
- variable_variation_map =
163
- if !bucketeduser.nil?
164
- bucketeduser.variable_variation_map
165
- else
166
- {}
167
- end
168
- varmap_addr = malloc_asc_string(Oj.dump(variable_variation_map))
169
- asc_pin(varmap_addr)
170
- event_addr = malloc_asc_string(Oj.dump(event))
171
- @@stack_tracer = lambda { |message| raise message }
172
- @@instance.invoke("queueAggregateEvent", @sdkKeyAddr, event_addr, varmap_addr)
173
- ensure
174
- asc_unpin(varmap_addr)
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
- config_addr = malloc_asc_string(config)
188
- @@stack_tracer = lambda { |message| raise message }
189
- @@instance.invoke("setConfigData", @sdkKeyAddr, config_addr)
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
- options_json = Oj.dump(options)
195
- options_addr = malloc_asc_string(options_json)
196
- @@stack_tracer = lambda { |message| raise message }
197
- @@instance.invoke("initEventQueue", @sdkKeyAddr, options_addr)
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
- customdata_json = Oj.dump(customdata)
203
- customdata_addr = malloc_asc_string(customdata_json)
204
- @@stack_tracer = lambda { |message| raise message }
205
- @@instance.invoke("setClientCustomData", customdata_addr)
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
- platformdata_json = Oj.dump(platformdata)
213
- platformdata_addr = malloc_asc_string(platformdata_json)
214
- @@stack_tracer = lambda { |message| raise message }
215
- @@instance.invoke("setPlatformData", platformdata_addr)
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
- addr = malloc_asc_string(sdkKey)
220
- @sdkKeyAddr = addr
221
- asc_pin(addr)
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 = lambda { |message| raise message }
284
+ @@stack_tracer = @@stack_tracer_raise
238
285
  wasm_new = @@instance.export("__new").to_func
239
- utf8_bytes = string.encode("iso-8859-1").force_encoding("utf-8").bytes
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 = lambda { |message| raise message }
246
- @@memory.write(start_addr + (i * 2), [utf8_bytes[i]].pack('U'))
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
- @@stack_tracer = lambda { |message| raise message }
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
- result = ""
263
- i = 0
264
- while i < len
265
- @@stack_tracer = lambda { |message| raise message }
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
@@ -11,5 +11,5 @@ OpenAPI Generator version: 5.3.0
11
11
  =end
12
12
 
13
13
  module DevCycle
14
- VERSION = '2.0.2'
14
+ VERSION = '2.1.0'
15
15
  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.2
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-07 00:00:00.000000000 Z
11
+ date: 2023-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: typhoeus