Skip to content

Commit e0229a1

Browse files
committed
updates
1 parent be83e72 commit e0229a1

6 files changed

Lines changed: 118 additions & 91 deletions

File tree

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,29 @@ require 'json'
8181
# Fetch datafile from URL
8282
datafile_url = 'https://cdn.yoursite.com/datafile.json'
8383
response = Net::HTTP.get_response(URI(datafile_url))
84-
datafile_content = JSON.parse(response.body)
84+
85+
# Parse JSON with symbolized keys (required)
86+
datafile_content = JSON.parse(response.body, symbolize_names: true)
8587

8688
# Create SDK instance
8789
f = Featurevisor.create_instance(
8890
datafile: datafile_content
8991
)
9092
```
9193

94+
**Important**: When parsing JSON datafiles, you must use `symbolize_names: true` to ensure proper key handling by the SDK.
95+
96+
Alternatively, you can pass a JSON string directly and the SDK will parse it automatically:
97+
98+
```ruby
99+
# Option 1: Parse JSON yourself (recommended)
100+
datafile_content = JSON.parse(json_string, symbolize_names: true)
101+
f = Featurevisor.create_instance(datafile: datafile_content)
102+
103+
# Option 2: Pass JSON string directly (automatic parsing)
104+
f = Featurevisor.create_instance(datafile: json_string)
105+
```
106+
92107
## Evaluation types
93108

94109
We can evaluate 3 types of values against a particular [feature](https://featurevisor.com/docs/features/):
@@ -334,9 +349,16 @@ f.set_sticky({
334349
You may also initialize the SDK without passing `datafile`, and set it later on:
335350

336351
```ruby
352+
# Parse with symbolized keys before setting
353+
datafile_content = JSON.parse(json_string, symbolize_names: true)
337354
f.set_datafile(datafile_content)
355+
356+
# Or pass JSON string directly for automatic parsing
357+
f.set_datafile(json_string)
338358
```
339359

360+
**Important**: When calling `set_datafile()`, ensure JSON is parsed with `symbolize_names: true` if you're parsing it yourself.
361+
340362
### Updating datafile
341363

342364
You can set the datafile as many times as you want in your application, which will result in emitting a [`datafile_set`](#datafile_set) event that you can listen and react to accordingly.

bin/commands/assess_distribution.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def build_datafile(environment)
147147
end
148148

149149
begin
150-
JSON.parse(stdout)
150+
JSON.parse(stdout, symbolize_names: true)
151151
rescue JSON::ParserError => e
152152
puts "Error: Failed to parse datafile JSON: #{e.message}"
153153
exit 1

bin/commands/benchmark.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def build_datafile(environment)
120120

121121
# Parse the JSON output
122122
begin
123-
JSON.parse(datafile_output)
123+
JSON.parse(datafile_output, symbolize_names: true)
124124
rescue JSON::ParserError => e
125125
puts "Error: Failed to parse datafile JSON: #{e.message}"
126126
puts "Command output: #{datafile_output}"

bin/commands/test.rb

Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ def run
1919

2020
# Get project configuration
2121
config = get_config
22-
environments = config["environments"] || []
22+
environments = config[:environments] || []
2323
segments_by_key = get_segments
2424

2525
# Use CLI schemaVersion option or fallback to config
2626
schema_version = @options.schema_version
2727
if schema_version.nil? || schema_version.empty?
28-
schema_version = config["schemaVersion"]
28+
schema_version = config[:schemaVersion]
2929
end
3030

3131
# Build datafiles for all environments
@@ -57,7 +57,7 @@ def get_config
5757
config_output = execute_command(command)
5858

5959
begin
60-
JSON.parse(config_output)
60+
JSON.parse(config_output, symbolize_names: true)
6161
rescue JSON::ParserError => e
6262
puts "Error: Failed to parse config JSON: #{e.message}"
6363
puts "Command output: #{config_output}"
@@ -71,11 +71,11 @@ def get_segments
7171
segments_output = execute_command(command)
7272

7373
begin
74-
segments = JSON.parse(segments_output)
74+
segments = JSON.parse(segments_output, symbolize_names: true)
7575
segments_by_key = {}
7676
segments.each do |segment|
77-
if segment["key"]
78-
segments_by_key[segment["key"]] = segment
77+
if segment[:key]
78+
segments_by_key[segment[:key]] = segment
7979
end
8080
end
8181
segments_by_key
@@ -110,7 +110,7 @@ def build_datafiles(environments, schema_version, inflate)
110110
datafile_output = execute_command(command)
111111

112112
begin
113-
datafile = JSON.parse(datafile_output)
113+
datafile = JSON.parse(datafile_output, symbolize_names: true)
114114
datafiles_by_environment[environment] = datafile
115115
rescue JSON::ParserError => e
116116
puts "Error: Failed to parse datafile JSON for #{environment}: #{e.message}"
@@ -147,7 +147,7 @@ def get_tests
147147
tests_output = execute_command(command)
148148

149149
begin
150-
JSON.parse(tests_output)
150+
JSON.parse(tests_output, symbolize_names: true)
151151
rescue JSON::ParserError => e
152152
puts "Error: Failed to parse tests JSON: #{e.message}"
153153
puts "Command output: #{tests_output}"
@@ -186,8 +186,8 @@ def run_tests(tests, sdk_instances_by_environment, datafiles_by_environment, seg
186186
failed_assertions_count = 0
187187

188188
tests.each do |test|
189-
test_key = test["key"]
190-
assertions = test["assertions"] || []
189+
test_key = test[:key]
190+
assertions = test[:assertions] || []
191191
results = ""
192192
test_has_error = false
193193
test_duration = 0.0
@@ -196,8 +196,8 @@ def run_tests(tests, sdk_instances_by_environment, datafiles_by_environment, seg
196196
if assertion.is_a?(Hash)
197197
test_result = nil
198198

199-
if test["feature"]
200-
environment = assertion["environment"]
199+
if test[:feature]
200+
environment = assertion[:environment]
201201
instance = sdk_instances_by_environment[environment]
202202

203203
# Show datafile if requested
@@ -209,7 +209,7 @@ def run_tests(tests, sdk_instances_by_environment, datafiles_by_environment, seg
209209
end
210210

211211
# If "at" parameter is provided, create a new instance with the specific hook
212-
if assertion["at"]
212+
if assertion[:at]
213213
datafile = datafiles_by_environment[environment]
214214

215215
instance = Featurevisor.create_instance(
@@ -221,7 +221,7 @@ def run_tests(tests, sdk_instances_by_environment, datafiles_by_environment, seg
221221
bucket_value: ->(options) do
222222
# Match JavaScript implementation: assertion.at * (MAX_BUCKETED_NUMBER / 100)
223223
# MAX_BUCKETED_NUMBER is 100000, so this becomes assertion.at * 1000
224-
at = assertion["at"]
224+
at = assertion[:at]
225225
if at.is_a?(Numeric)
226226
(at * 1000).to_i
227227
else
@@ -233,9 +233,9 @@ def run_tests(tests, sdk_instances_by_environment, datafiles_by_environment, seg
233233
)
234234
end
235235

236-
test_result = run_test_feature(assertion, test["feature"], instance, level)
237-
elsif test["segment"]
238-
segment_key = test["segment"]
236+
test_result = run_test_feature(assertion, test[:feature], instance, level)
237+
elsif test[:segment]
238+
segment_key = test[:segment]
239239
segment = segments_by_key[segment_key]
240240
if segment.is_a?(Hash)
241241
test_result = run_test_segment(assertion, segment, level)
@@ -281,8 +281,8 @@ def run_tests(tests, sdk_instances_by_environment, datafiles_by_environment, seg
281281
end
282282

283283
def run_test_feature(assertion, feature_key, instance, level)
284-
context = parse_context(assertion["context"])
285-
sticky = parse_sticky(assertion["sticky"])
284+
context = parse_context(assertion[:context])
285+
sticky = parse_sticky(assertion[:sticky])
286286

287287
# Set context and sticky for this assertion
288288
instance.set_context(context, false)
@@ -298,8 +298,8 @@ def run_test_feature(assertion, feature_key, instance, level)
298298
start_time = Time.now
299299

300300
# Test expectedToBeEnabled
301-
if assertion.key?("expectedToBeEnabled")
302-
expected_to_be_enabled = assertion["expectedToBeEnabled"]
301+
if assertion.key?(:expectedToBeEnabled)
302+
expected_to_be_enabled = assertion[:expectedToBeEnabled]
303303
is_enabled = instance.is_enabled(feature_key, context, override_options)
304304

305305
if is_enabled != expected_to_be_enabled
@@ -309,8 +309,8 @@ def run_test_feature(assertion, feature_key, instance, level)
309309
end
310310

311311
# Test expectedVariation
312-
if assertion.key?("expectedVariation")
313-
expected_variation = assertion["expectedVariation"]
312+
if assertion.key?(:expectedVariation)
313+
expected_variation = assertion[:expectedVariation]
314314
variation = instance.get_variation(feature_key, context, override_options)
315315

316316
variation_value = variation.nil? ? nil : variation
@@ -321,12 +321,12 @@ def run_test_feature(assertion, feature_key, instance, level)
321321
end
322322

323323
# Test expectedVariables
324-
if assertion["expectedVariables"]
325-
expected_variables = assertion["expectedVariables"]
324+
if assertion[:expectedVariables]
325+
expected_variables = assertion[:expectedVariables]
326326
expected_variables.each do |variable_key, expected_value|
327327
# Set default variable value for this specific variable
328-
if assertion["defaultVariableValues"] && assertion["defaultVariableValues"][variable_key]
329-
override_options[:default_variable_value] = assertion["defaultVariableValues"][variable_key]
328+
if assertion[:defaultVariableValues] && assertion[:defaultVariableValues][variable_key]
329+
override_options[:default_variable_value] = assertion[:defaultVariableValues][variable_key]
330330
end
331331

332332
actual_value = instance.get_variable(feature_key, variable_key, context, override_options)
@@ -365,13 +365,13 @@ def run_test_feature(assertion, feature_key, instance, level)
365365
end
366366

367367
# Test expectedEvaluations
368-
if assertion["expectedEvaluations"]
369-
expected_evaluations = assertion["expectedEvaluations"]
368+
if assertion[:expectedEvaluations]
369+
expected_evaluations = assertion[:expectedEvaluations]
370370

371371
# Test flag evaluations
372-
if expected_evaluations["flag"]
372+
if expected_evaluations[:flag]
373373
evaluation = instance.evaluate_flag(feature_key, context, override_options)
374-
expected_evaluations["flag"].each do |key, expected_value|
374+
expected_evaluations[:flag].each do |key, expected_value|
375375
actual_value = get_evaluation_value(evaluation, key)
376376
if !compare_values(actual_value, expected_value)
377377
has_error = true
@@ -381,9 +381,9 @@ def run_test_feature(assertion, feature_key, instance, level)
381381
end
382382

383383
# Test variation evaluations
384-
if expected_evaluations["variation"]
384+
if expected_evaluations[:variation]
385385
evaluation = instance.evaluate_variation(feature_key, context, override_options)
386-
expected_evaluations["variation"].each do |key, expected_value|
386+
expected_evaluations[:variation].each do |key, expected_value|
387387
actual_value = get_evaluation_value(evaluation, key)
388388
if !compare_values(actual_value, expected_value)
389389
has_error = true
@@ -393,8 +393,8 @@ def run_test_feature(assertion, feature_key, instance, level)
393393
end
394394

395395
# Test variable evaluations
396-
if expected_evaluations["variables"]
397-
expected_evaluations["variables"].each do |variable_key, expected_eval|
396+
if expected_evaluations[:variables]
397+
expected_evaluations[:variables].each do |variable_key, expected_eval|
398398
if expected_eval.is_a?(Hash)
399399
evaluation = instance.evaluate_variable(feature_key, variable_key, context, override_options)
400400
expected_eval.each do |key, expected_value|
@@ -410,10 +410,10 @@ def run_test_feature(assertion, feature_key, instance, level)
410410
end
411411

412412
# Test children
413-
if assertion["children"]
414-
assertion["children"].each do |child|
413+
if assertion[:children]
414+
assertion[:children].each do |child|
415415
if child.is_a?(Hash)
416-
child_context = parse_context(child["context"])
416+
child_context = parse_context(child[:context])
417417

418418
# Create override options for child with sticky values
419419
child_override_options = create_override_options(child)
@@ -456,8 +456,8 @@ def run_test_feature_child(assertion, feature_key, instance, level)
456456
start_time = Time.now
457457

458458
# Test expectedToBeEnabled
459-
if assertion.key?("expectedToBeEnabled")
460-
expected_to_be_enabled = assertion["expectedToBeEnabled"]
459+
if assertion.key?(:expectedToBeEnabled)
460+
expected_to_be_enabled = assertion[:expectedToBeEnabled]
461461
is_enabled = instance.is_enabled(feature_key, context, override_options)
462462

463463
if is_enabled != expected_to_be_enabled
@@ -467,8 +467,8 @@ def run_test_feature_child(assertion, feature_key, instance, level)
467467
end
468468

469469
# Test expectedVariation
470-
if assertion.key?("expectedVariation")
471-
expected_variation = assertion["expectedVariation"]
470+
if assertion.key?(:expectedVariation)
471+
expected_variation = assertion[:expectedVariation]
472472
variation = instance.get_variation(feature_key, context, override_options)
473473

474474
variation_value = variation.nil? ? nil : variation
@@ -479,12 +479,12 @@ def run_test_feature_child(assertion, feature_key, instance, level)
479479
end
480480

481481
# Test expectedVariables
482-
if assertion["expectedVariables"]
483-
expected_variables = assertion["expectedVariables"]
482+
if assertion[:expectedVariables]
483+
expected_variables = assertion[:expectedVariables]
484484
expected_variables.each do |variable_key, expected_value|
485485
# Set default variable value for this specific variable
486-
if assertion["defaultVariableValues"] && assertion["defaultVariableValues"][variable_key]
487-
override_options[:default_variable_value] = assertion["defaultVariableValues"][variable_key]
486+
if assertion[:defaultVariableValues] && assertion[:defaultVariableValues][variable_key]
487+
override_options[:default_variable_value] = assertion[:defaultVariableValues][variable_key]
488488
end
489489

490490
actual_value = instance.get_variable(feature_key, variable_key, context, override_options)
@@ -532,8 +532,8 @@ def run_test_feature_child(assertion, feature_key, instance, level)
532532
end
533533

534534
def run_test_segment(assertion, segment, level)
535-
context = parse_context(assertion["context"])
536-
conditions = segment["conditions"]
535+
context = parse_context(assertion[:context])
536+
conditions = segment[:conditions]
537537

538538
# Create a minimal datafile for segment testing
539539
datafile = {
@@ -553,8 +553,8 @@ def run_test_segment(assertion, segment, level)
553553
errors = ""
554554
start_time = Time.now
555555

556-
if assertion.key?("expectedToMatch")
557-
expected_to_match = assertion["expectedToMatch"]
556+
if assertion.key?(:expectedToMatch)
557+
expected_to_match = assertion[:expectedToMatch]
558558
actual = instance.instance_variable_get(:@datafile_reader).all_conditions_are_matched(conditions, context)
559559

560560
if actual != expected_to_match
@@ -589,16 +589,16 @@ def parse_sticky(sticky_data)
589589
if value.is_a?(Hash)
590590
evaluated_feature = {}
591591

592-
if value.key?("enabled")
593-
evaluated_feature[:enabled] = value["enabled"]
592+
if value.key?(:enabled)
593+
evaluated_feature[:enabled] = value[:enabled]
594594
end
595595

596-
if value.key?("variation")
597-
evaluated_feature[:variation] = value["variation"]
596+
if value.key?(:variation)
597+
evaluated_feature[:variation] = value[:variation]
598598
end
599599

600-
if value["variables"] && value["variables"].is_a?(Hash)
601-
evaluated_feature[:variables] = value["variables"].transform_keys(&:to_sym)
600+
if value[:variables] && value[:variables].is_a?(Hash)
601+
evaluated_feature[:variables] = value[:variables].transform_keys(&:to_sym)
602602
end
603603

604604
sticky_features[key.to_sym] = evaluated_feature
@@ -614,8 +614,8 @@ def parse_sticky(sticky_data)
614614
def create_override_options(assertion)
615615
options = {}
616616

617-
if assertion["defaultVariationValue"]
618-
options[:default_variation_value] = assertion["defaultVariationValue"]
617+
if assertion[:defaultVariationValue]
618+
options[:default_variation_value] = assertion[:defaultVariationValue]
619619
end
620620

621621
options

0 commit comments

Comments
 (0)