Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Custom Field Support #19

Merged
merged 1 commit into from Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
absmartly-sdk (1.0.8)
absmartly-sdk (1.1.0)
arraybuffer (~> 0.0.6)
faraday (~> 2.0)
faraday-retry (~> 2.0)
Expand Down
2 changes: 1 addition & 1 deletion lib/absmartly/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Absmartly
VERSION = "1.0.8"
VERSION = "1.1.0"
end
83 changes: 83 additions & 0 deletions lib/context.rb
Expand Up @@ -21,6 +21,7 @@ def self.create(clock, config, data_future, data_provider,
def initialize(clock, config, data_future, data_provider,
event_handler, event_logger, variable_parser, audience_matcher)
@index = []
@context_custom_fields = {}
@achievements = []
@assignment_cache = {}
@assignments = {}
Expand Down Expand Up @@ -203,6 +204,52 @@ def variable_value(key, default_value)
default_value
end

def custom_field_keys
check_ready?(true)
keys = []

@data.experiments.each do |experiment|
custom_field_values = experiment.custom_field_values
if custom_field_values != nil
custom_field_values.each do |custom_field|
keys.append(custom_field.name)
end
end
end

return keys.sort.uniq
end

def custom_field_value(experimentName, key)
check_ready?(true)

experiment_custom_fields = @context_custom_fields[experimentName]

if experiment_custom_fields != nil
field = experiment_custom_fields[key]
if field != nil
return field.value
end
end

return nil
end

def custom_field_type(experimentName, key)
check_ready?(true)

experiment_custom_fields = @context_custom_fields[experimentName]

if experiment_custom_fields != nil
field = experiment_custom_fields[key]
if field != nil
return field.type
end
end

return nil
end

def peek_variable_value(key, default_value)
check_ready?(true)

Expand Down Expand Up @@ -451,8 +498,11 @@ def assign_data(data)
@data = data
@index = {}
@index_variables = {}

if data && !data.experiments.nil? && !data.experiments.empty?
data.experiments.each do |experiment|
@experimentCustomFieldValues = {}

experiment_variables = ExperimentVariables.new
experiment_variables.data = experiment
experiment_variables.variables ||= []
Expand All @@ -467,7 +517,36 @@ def assign_data(data)
end
end

if !experiment.custom_field_values.nil?
experiment.custom_field_values.each do |custom_field_value|
value = ContextCustomFieldValues.new
value.type = custom_field_value.type

if !custom_field_value.value.nil?
custom_value = custom_field_value.value

if custom_field_value.type.start_with?("json")
value.value = @variable_parser.parse(self, experiment.name, custom_field_value.name, custom_value)

elsif custom_field_value.type.start_with?("boolean")
value.value = custom_value.to_bool

elsif custom_field_value.type.start_with?("number")
value.value = custom_value.to_i

else
value.value = custom_field_value.value
end

@experimentCustomFieldValues[custom_field_value.name] = value

end

end
end

@index[experiment.name] = experiment_variables
@context_custom_fields[experiment.name] = @experimentCustomFieldValues
end
end
end
Expand Down Expand Up @@ -544,5 +623,9 @@ class ExperimentVariables
attr_accessor :data, :variables
end

class ContextCustomFieldValues
attr_accessor :type, :value
end

class IllegalStateException < StandardError
end
31 changes: 31 additions & 0 deletions lib/json/custom_field_value.rb
@@ -0,0 +1,31 @@
# frozen_string_literal: true

class CustomFieldValue
attr_accessor :name, :type, :value

def initialize(name, value, type)
@name = name
@type = type
@value = value
end

def ==(o)
return true if self.object_id == o.object_id
return false if o.nil? || self.class != o.class

that = o
@name == that.name && @type == that.type && @value == that.value
end

def hash_code
{ name: @name, type: @type, value: @value }
end

def to_s
"CustomFieldValue{" +
"name='#{@name}'" +
", type='#{@type}'" +
", value='#{@value}'" +
"}"
end
end
13 changes: 10 additions & 3 deletions lib/json/experiment.rb
Expand Up @@ -3,18 +3,23 @@
require_relative "../string"
require_relative "experiment_application"
require_relative "experiment_variant"
require_relative "custom_field_value"

class Experiment
attr_accessor :id, :name, :unit_type, :iteration, :seed_hi, :seed_lo, :split,
:traffic_seed_hi, :traffic_seed_lo, :traffic_split, :full_on_variant,
:applications, :variants, :audience_strict, :audience
:applications, :variants, :audience_strict, :audience, :custom_field_values

def initialize(args = {})
args.each do |name, value|
if name == :applications
@applications = assign_to_klass(ExperimentApplication, value)
elsif name == :variants
@variants = assign_to_klass(ExperimentVariant, value)
elsif name == :customFieldValues
if value != nil
@custom_field_values = assign_to_klass(CustomFieldValue, value)
end
else
self.instance_variable_set("@#{name.to_s.underscore}", value)
end
Expand Down Expand Up @@ -42,7 +47,7 @@ def ==(o)
@unit_type == that.unit_type && @split == that.split &&
@traffic_split == that.traffic_split && @applications == that.applications &&
@variants == that.variants && @audience_strict == that.audience_strict &&
@audience == that.audience
@audience == that.audience && @custom_field_values == that.custom_field_values
end

def hash_code
Expand All @@ -57,7 +62,8 @@ def hash_code
traffic_seed_lo: @traffic_seed_lo,
full_on_variant: @full_on_variant,
audience_strict: @audience_strict,
audience: @audience
audience: @audience,
custom_field_values: @custom_field_values
}
end

Expand All @@ -78,6 +84,7 @@ def to_s
", variants=#{@variants.join}" +
", audienceStrict=#{@audience_strict}" +
", audience='#{@audience}'" +
", custom_field_values='#{@custom_field_values}'" +
"}"
end
end
6 changes: 3 additions & 3 deletions lib/json_expr/expr_evaluator.rb
Expand Up @@ -32,7 +32,7 @@ def evaluate(expr)
def boolean_convert(x)
if x.is_a?(TrueClass) || x.is_a?(FalseClass)
return x
elsif x.is_a?(Numeric) || !(x =~ NUMERIC_REGEX).nil?
elsif x.is_a?(Numeric) || !(x.to_s =~ NUMERIC_REGEX).nil?
return !x.to_f.zero?
elsif x.is_a?(String)
return x != "false" && x != "0" && x != ""
Expand All @@ -44,7 +44,7 @@ def boolean_convert(x)
def number_convert(x)
return if x.nil? || x.to_s.empty?

if x.is_a?(Numeric) || !(x =~ NUMERIC_REGEX).nil?
if x.is_a?(Numeric) || !(x.to_s =~ NUMERIC_REGEX).nil?
return x.to_f
elsif x.is_a?(TrueClass) || x.is_a?(FalseClass)
return x ? 1.0 : 0.0
Expand All @@ -57,7 +57,7 @@ def string_convert(x)
return x
elsif x.is_a?(TrueClass) || x.is_a?(FalseClass)
return x.to_s
elsif x.is_a?(Numeric) || !(x =~ NUMERIC_REGEX).nil?
elsif x.is_a?(Numeric) || !(x.to_s =~ NUMERIC_REGEX).nil?
return x == x.to_i ? x.to_i.to_s : x.to_s
end
nil
Expand Down
38 changes: 38 additions & 0 deletions spec/context_spec.rb
Expand Up @@ -698,6 +698,44 @@ def faraday_response(content)
expect(variable_experiments).to eq(context.variable_keys)
end

it "getCustomFieldKeys" do
context = create_context(data_future_ready)

expect(["country", "languages", "overrides"]).to eq(context.custom_field_keys)
end

it "getCustomFieldValues" do
context = create_context(data_future_ready)

expect(context.custom_field_value("not_found", "not_found")).to be_nil
expect(context.custom_field_value("exp_test_ab", key: "not_found")).to be_nil
expect(context.custom_field_value("exp_test_ab", "country")).to eq("US,PT,ES,DE,FR")
expect(context.custom_field_type("exp_test_ab", "country")).to eq("string")

data = {"123": 1, "456": 0}
expect(context.custom_field_value("exp_test_ab", "overrides")).to eq(data)
expect(context.custom_field_type("exp_test_ab", "overrides")).to eq("json")

expect(context.custom_field_value("exp_test_ab", "languages")).to be_nil
expect(context.custom_field_type("exp_test_ab", "languages")).to be_nil

expect(context.custom_field_value("exp_test_abc", "overrides")).to be_nil
expect(context.custom_field_type("exp_test_abc", "overrides")).to be_nil

expect(context.custom_field_value("exp_test_abc", "languages")).to eq("en-US,en-GB,pt-PT,pt-BR,es-ES,es-MX")
expect(context.custom_field_type("exp_test_abc", "languages")).to eq("string")

expect(context.custom_field_value("exp_test_no_custom_fields", "country")).to be_nil
expect(context.custom_field_type("exp_test_no_custom_fields", "country")).to be_nil

expect(context.custom_field_type("exp_test_no_custom_fields", "overrides")).to be_nil
expect(context.custom_field_value("exp_test_no_custom_fields", "overrides")).to be_nil

expect(context.custom_field_type("exp_test_no_custom_fields", "languages")).to be_nil
expect(context.custom_field_value("exp_test_no_custom_fields", "languages")).to be_nil

end

it "peek_treatmentReturnsOverrideVariant" do
context = create_ready_context

Expand Down
8 changes: 8 additions & 0 deletions spec/default_context_data_deserializer_spec.rb
Expand Up @@ -32,6 +32,10 @@
ExperimentVariant.new("A", nil),
ExperimentVariant.new("B", "{\"banner.border\":1,\"banner.size\":\"large\"}")
]
experiment0.custom_field_values = [
CustomFieldValue.new("country", "US,PT,ES,DE,FR", "string"),
CustomFieldValue.new("overrides", "{\"123\":1,\"456\":0}", "json"),
]
experiment0.audience_strict = false
experiment0.audience = nil

Expand All @@ -53,6 +57,10 @@
ExperimentVariant.new("B", "{\"button.color\":\"blue\"}"),
ExperimentVariant.new("C", "{\"button.color\":\"red\"}")
]
experiment1.custom_field_values = [
CustomFieldValue.new("country", "US,PT,ES,DE,FR", "string"),
CustomFieldValue.new("languages", "en-US,en-GB,pt-PT,pt-BR,es-ES,es-MX", "string"),
]
experiment1.audience_strict = false
experiment1.audience = ""

Expand Down
31 changes: 28 additions & 3 deletions spec/fixtures/resources/context.json
Expand Up @@ -33,7 +33,19 @@
"config":"{\"banner.border\":1,\"banner.size\":\"large\"}"
}
],
"audience": null
"audience": null,
"customFieldValues": [
{
"name": "country",
"value": "US,PT,ES,DE,FR",
"type": "string"
},
{
"name": "overrides",
"value": "{\"123\":1,\"456\":0}",
"type": "json"
}
]
},
{
"id":2,
Expand Down Expand Up @@ -73,7 +85,19 @@
"config":"{\"button.color\":\"red\"}"
}
],
"audience": ""
"audience": "",
"customFieldValues": [
{
"name": "country",
"value": "US,PT,ES,DE,FR",
"type": "string"
},
{
"name": "languages",
"value": "en-US,en-GB,pt-PT,pt-BR,es-ES,es-MX",
"type": "string"
}
]
},
{
"id":3,
Expand Down Expand Up @@ -113,7 +137,8 @@
"config":"{\"card.width\":\"75%\"}"
}
],
"audience": "{}"
"audience": "{}",
"customFieldValues": null
},
{
"id":4,
Expand Down