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 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