absmartly-sdk 0.1.2
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +180 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +92 -0
- data/LICENSE.txt +21 -0
- data/README +180 -0
- data/Rakefile +12 -0
- data/example/example.rb +48 -0
- data/lib/a_b_smartly.rb +92 -0
- data/lib/a_b_smartly_config.rb +49 -0
- data/lib/absmartly/md5.rb +178 -0
- data/lib/absmartly/variant_assigner.rb +36 -0
- data/lib/absmartly/version.rb +5 -0
- data/lib/absmartly.rb +57 -0
- data/lib/audience_deserializer.rb +8 -0
- data/lib/audience_matcher.rb +38 -0
- data/lib/client.rb +80 -0
- data/lib/client_config.rb +43 -0
- data/lib/context.rb +528 -0
- data/lib/context_config.rb +70 -0
- data/lib/context_data_deserializer.rb +8 -0
- data/lib/context_data_provider.rb +8 -0
- data/lib/context_event_handler.rb +8 -0
- data/lib/context_event_logger.rb +9 -0
- data/lib/context_event_serializer.rb +8 -0
- data/lib/default_audience_deserializer.rb +13 -0
- data/lib/default_context_data_deserializer.rb +16 -0
- data/lib/default_context_data_provider.rb +15 -0
- data/lib/default_context_event_handler.rb +15 -0
- data/lib/default_context_event_serializer.rb +58 -0
- data/lib/default_http_client.rb +61 -0
- data/lib/default_http_client_config.rb +19 -0
- data/lib/default_variable_parser.rb +13 -0
- data/lib/hashing.rb +9 -0
- data/lib/http_client.rb +20 -0
- data/lib/json/attribute.rb +30 -0
- data/lib/json/context_data.rb +31 -0
- data/lib/json/experiment.rb +83 -0
- data/lib/json/experiment_application.rb +27 -0
- data/lib/json/experiment_variant.rb +29 -0
- data/lib/json/exposure.rb +60 -0
- data/lib/json/goal_achievement.rb +32 -0
- data/lib/json/publish_event.rb +42 -0
- data/lib/json/unit.rb +30 -0
- data/lib/json_expr/evaluator.rb +27 -0
- data/lib/json_expr/expr_evaluator.rb +122 -0
- data/lib/json_expr/json_expr.rb +37 -0
- data/lib/json_expr/operator.rb +8 -0
- data/lib/json_expr/operators/and_combinator.rb +14 -0
- data/lib/json_expr/operators/binary_operator.rb +22 -0
- data/lib/json_expr/operators/boolean_combinator.rb +15 -0
- data/lib/json_expr/operators/equals_operator.rb +12 -0
- data/lib/json_expr/operators/greater_than_operator.rb +12 -0
- data/lib/json_expr/operators/greater_than_or_equal_operator.rb +12 -0
- data/lib/json_expr/operators/in_operator.rb +23 -0
- data/lib/json_expr/operators/less_than_operator.rb +12 -0
- data/lib/json_expr/operators/less_than_or_equal_operator.rb +12 -0
- data/lib/json_expr/operators/match_operator.rb +17 -0
- data/lib/json_expr/operators/nil_operator.rb +11 -0
- data/lib/json_expr/operators/not_operator.rb +11 -0
- data/lib/json_expr/operators/or_combinator.rb +14 -0
- data/lib/json_expr/operators/unary_operator.rb +13 -0
- data/lib/json_expr/operators/value_operator.rb +11 -0
- data/lib/json_expr/operators/var_operator.rb +21 -0
- data/lib/scheduled_executor_service.rb +8 -0
- data/lib/scheduled_thread_pool_executor.rb +14 -0
- data/lib/string.rb +30 -0
- data/lib/variable_parser.rb +8 -0
- data/lib/variant_assigner.rb +47 -0
- data/sig/absmartly.rbs +4 -0
- metadata +120 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "context_event_serializer"
|
4
|
+
|
5
|
+
class DefaultContextEventSerializer < ContextEventSerializer
|
6
|
+
def serialize(event)
|
7
|
+
units = event.units.nil? ? [] : event.units.map do |unit|
|
8
|
+
{
|
9
|
+
type: unit.type,
|
10
|
+
uid: unit.uid,
|
11
|
+
}
|
12
|
+
end
|
13
|
+
req = {}
|
14
|
+
unless units.empty?
|
15
|
+
req = {
|
16
|
+
publishedAt: event.published_at,
|
17
|
+
units: units,
|
18
|
+
hashed: event.hashed
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
req[:goals] = event.goals.map do |x|
|
23
|
+
{
|
24
|
+
name: x.name,
|
25
|
+
achievedAt: x.achieved_at,
|
26
|
+
properties: x.properties,
|
27
|
+
}
|
28
|
+
end unless event.goals.nil?
|
29
|
+
|
30
|
+
req[:exposures] = event.exposures.select { |x| !x.id.nil? }.map do |x|
|
31
|
+
{
|
32
|
+
id: x.id,
|
33
|
+
name: x.name,
|
34
|
+
unit: x.unit,
|
35
|
+
exposedAt: x.exposed_at.to_i,
|
36
|
+
variant: x.variant,
|
37
|
+
assigned: x.assigned,
|
38
|
+
eligible: x.eligible,
|
39
|
+
overridden: x.overridden,
|
40
|
+
fullOn: x.full_on,
|
41
|
+
custom: x.custom,
|
42
|
+
audienceMismatch: x.audience_mismatch
|
43
|
+
}
|
44
|
+
end unless event.exposures.nil?
|
45
|
+
|
46
|
+
req[:attributes] = event.attributes.map do |x|
|
47
|
+
{
|
48
|
+
name: x.name,
|
49
|
+
value: x.value,
|
50
|
+
setAt: x.set_at,
|
51
|
+
}
|
52
|
+
end unless event.attributes.nil?
|
53
|
+
|
54
|
+
return nil if req.empty?
|
55
|
+
|
56
|
+
req.to_json
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "uri"
|
5
|
+
require_relative "http_client"
|
6
|
+
|
7
|
+
class DefaultHttpClient < HttpClient
|
8
|
+
attr_accessor :config, :session
|
9
|
+
|
10
|
+
def self.create(config)
|
11
|
+
DefaultHttpClient.new(config)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(config)
|
15
|
+
@config = config
|
16
|
+
@session = Faraday.new("") do |f|
|
17
|
+
f.request :retry,
|
18
|
+
max: config.max_retries,
|
19
|
+
interval: config.retry_interval,
|
20
|
+
interval_randomness: 0.5,
|
21
|
+
backoff_factor: 2
|
22
|
+
f.options.timeout = config.connect_timeout
|
23
|
+
f.options.open_timeout = config.connection_request_timeout
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# def context_data
|
28
|
+
# end
|
29
|
+
|
30
|
+
def get(url, query, headers)
|
31
|
+
@session.get(url, query, headers)
|
32
|
+
end
|
33
|
+
|
34
|
+
def put(url, query, headers, body)
|
35
|
+
@session.put(add_tracking(url, query), body, headers)
|
36
|
+
end
|
37
|
+
|
38
|
+
def post(url, query, headers, body)
|
39
|
+
@session.post(add_tracking(url, query), body, headers)
|
40
|
+
end
|
41
|
+
|
42
|
+
def close
|
43
|
+
@session.close
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.default_response(status_code, status_message, content_type, content)
|
47
|
+
env = Faraday::Env.from(status: status_code, body: content || status_message,
|
48
|
+
response_headers: { "Content-Type" => content_type })
|
49
|
+
Faraday::Response.new(env)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def add_tracking(url, params)
|
54
|
+
parsed = URI.parse(url)
|
55
|
+
query = parsed.query ? CGI.parse(parsed.query) : {}
|
56
|
+
query = query.merge(params) if params && params.is_a?(Hash)
|
57
|
+
parsed.query = URI.encode_www_form(query)
|
58
|
+
str = parsed.to_s
|
59
|
+
str[-1] == "?" ? str.chop : str
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class DefaultHttpClientConfig
|
4
|
+
attr_accessor :connect_timeout,
|
5
|
+
:connection_request_timeout,
|
6
|
+
:retry_interval,
|
7
|
+
:max_retries
|
8
|
+
|
9
|
+
def self.create
|
10
|
+
DefaultHttpClientConfig.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@connect_timeout = 3.0
|
15
|
+
@connection_request_timeout = 3.0
|
16
|
+
@retry_interval = 0.5
|
17
|
+
@max_retries = 5
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "variable_parser"
|
4
|
+
|
5
|
+
class DefaultVariableParser < VariableParser
|
6
|
+
attr_accessor :reader, :log
|
7
|
+
|
8
|
+
def parse(context, experiment_name, variant_name, config)
|
9
|
+
JSON.parse(config, symbolize_names: true)
|
10
|
+
rescue JSON::ParserError
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
end
|
data/lib/hashing.rb
ADDED
data/lib/http_client.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class HttpClient
|
4
|
+
# @interface method
|
5
|
+
def response
|
6
|
+
raise NotImplementedError.new("You must implement response method.")
|
7
|
+
end
|
8
|
+
|
9
|
+
def get(url, query, headers)
|
10
|
+
raise NotImplementedError.new("You must implement get method.")
|
11
|
+
end
|
12
|
+
|
13
|
+
def post(url, query, headers, body)
|
14
|
+
raise NotImplementedError.new("You must implement post method.")
|
15
|
+
end
|
16
|
+
|
17
|
+
def put(url, query, headers, body)
|
18
|
+
raise NotImplementedError.new("You must implement put method.")
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Attribute
|
4
|
+
attr_accessor :name, :value, :set_at
|
5
|
+
|
6
|
+
def initialize(name = nil, value = nil, set_at = nil)
|
7
|
+
@name = name
|
8
|
+
@value = value
|
9
|
+
@set_at = set_at
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(o)
|
13
|
+
return true if self.object_id == o.object_id
|
14
|
+
return false if o.nil? || self.class != o.class
|
15
|
+
|
16
|
+
@name == o.name && @value == o.value && @set_at == o.set_at
|
17
|
+
end
|
18
|
+
|
19
|
+
def hash_code
|
20
|
+
{ name: @name, value: @value, set_at: @set_at }
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
"Attribute{" +
|
25
|
+
"name='" + @name + "'" +
|
26
|
+
", value=" + @value +
|
27
|
+
", setAt=" + @set_at +
|
28
|
+
"}"
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "experiment"
|
4
|
+
|
5
|
+
class ContextData
|
6
|
+
attr_accessor :experiments
|
7
|
+
|
8
|
+
def initialize(experiments = [])
|
9
|
+
@experiments = experiments.map do |experiment|
|
10
|
+
Experiment.new(experiment)
|
11
|
+
end unless experiments.nil?
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(o)
|
16
|
+
return true if self.object_id == o.object_id
|
17
|
+
return false if o.nil? || self.class != o.class
|
18
|
+
|
19
|
+
@experiments == o.experiments
|
20
|
+
end
|
21
|
+
|
22
|
+
def hash_code
|
23
|
+
{ name: @name, config: @config }
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"ContextData{" +
|
28
|
+
"experiments='" + @experiments.join +
|
29
|
+
"}"
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../string"
|
4
|
+
require_relative "experiment_application"
|
5
|
+
require_relative "experiment_variant"
|
6
|
+
|
7
|
+
class Experiment
|
8
|
+
attr_accessor :id, :name, :unit_type, :iteration, :seed_hi, :seed_lo, :split,
|
9
|
+
:traffic_seed_hi, :traffic_seed_lo, :traffic_split, :full_on_variant,
|
10
|
+
:applications, :variants, :audience_strict, :audience
|
11
|
+
|
12
|
+
def initialize(args = {})
|
13
|
+
args.each do |name, value|
|
14
|
+
if name == :applications
|
15
|
+
@applications = assign_to_klass(ExperimentApplication, value)
|
16
|
+
elsif name == :variants
|
17
|
+
@variants = assign_to_klass(ExperimentVariant, value)
|
18
|
+
else
|
19
|
+
self.instance_variable_set("@#{name.to_s.underscore}", value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
@audience_strict ||= false
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def assign_to_klass(klass, arr)
|
27
|
+
arr.map do |item|
|
28
|
+
return item if item.is_a?(klass)
|
29
|
+
|
30
|
+
klass.new(*item.values)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def ==(o)
|
35
|
+
return true if self.object_id == o.object_id
|
36
|
+
return false if o.nil? || self.class != o.class
|
37
|
+
|
38
|
+
that = o
|
39
|
+
@id == that.id && @iteration == that.iteration && @seed_hi == that.seed_hi && @seed_lo == that.seed_lo &&
|
40
|
+
@traffic_seed_hi == that.traffic_seed_hi && @traffic_seed_lo == that.traffic_seed_lo &&
|
41
|
+
@full_on_variant == that.full_on_variant && @name == that.name &&
|
42
|
+
@unit_type == that.unit_type && @split == that.split &&
|
43
|
+
@traffic_split == that.traffic_split && @applications == that.applications &&
|
44
|
+
@variants == that.variants && @audience_strict == that.audience_strict &&
|
45
|
+
@audience == that.audience
|
46
|
+
end
|
47
|
+
|
48
|
+
def hash_code
|
49
|
+
{
|
50
|
+
id: @id,
|
51
|
+
name: @name,
|
52
|
+
unit_type: @unit_type,
|
53
|
+
iteration: @iteration,
|
54
|
+
seed_hi: @seed_hi,
|
55
|
+
seed_lo: @seed_lo,
|
56
|
+
traffic_seed_hi: @traffic_seed_hi,
|
57
|
+
traffic_seed_lo: @traffic_seed_lo,
|
58
|
+
full_on_variant: @full_on_variant,
|
59
|
+
audience_strict: @audience_strict,
|
60
|
+
audience: @audience
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
"ContextExperiment{" +
|
66
|
+
"id=" + @id +
|
67
|
+
", name='" + @name + "'" +
|
68
|
+
", unitType='" + @unit_type + "'" +
|
69
|
+
", iteration=" + @iteration +
|
70
|
+
", seedHi=" + @seed_hi +
|
71
|
+
", seedLo=" + @seed_lo +
|
72
|
+
", split=" + @split.join +
|
73
|
+
", trafficSeedHi=" + @traffic_seed_hi +
|
74
|
+
", trafficSeedLo=" + @traffic_seed_lo +
|
75
|
+
", trafficSplit=" + @traffic_split.join +
|
76
|
+
", fullOnVariant=" + @full_on_variant +
|
77
|
+
", applications=" + @applications.join +
|
78
|
+
", variants=" + @variants.join +
|
79
|
+
", audienceStrict=" + @audience_strict +
|
80
|
+
", audience='" + @audience + "'" +
|
81
|
+
"}"
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ExperimentApplication
|
4
|
+
attr_accessor :name
|
5
|
+
|
6
|
+
def initialize(name = nil)
|
7
|
+
@name = name
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(o)
|
11
|
+
return true if self.object_id == o.object_id
|
12
|
+
return false if o.nil? || self.class != o.class
|
13
|
+
|
14
|
+
that = o
|
15
|
+
@name == that.name
|
16
|
+
end
|
17
|
+
|
18
|
+
def hash_code
|
19
|
+
{ name: @name }
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"ExperimentApplication{" +
|
24
|
+
"name='" + @name + "'" +
|
25
|
+
"}"
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ExperimentVariant
|
4
|
+
attr_accessor :name, :config
|
5
|
+
|
6
|
+
def initialize(name = nil, config)
|
7
|
+
@name = name
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(o)
|
12
|
+
return true if self.object_id == o.object_id
|
13
|
+
return false if o.nil? || self.class != o.class
|
14
|
+
|
15
|
+
that = o
|
16
|
+
@name == that.name && @config == that.config
|
17
|
+
end
|
18
|
+
|
19
|
+
def hash_code
|
20
|
+
{ name: @name, config: @config }
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
"ExperimentVariant{" +
|
25
|
+
"name='" + @name + "'" +
|
26
|
+
", config='" + @config + "'" +
|
27
|
+
"}"
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Exposure
|
4
|
+
attr_accessor :id, :name, :unit, :variant, :exposed_at, :assigned, :eligible,
|
5
|
+
:overridden, :full_on, :custom, :audience_mismatch
|
6
|
+
|
7
|
+
def initialize(id = nil, name = nil, unit = nil, variant = nil,
|
8
|
+
exposed_at = nil, assigned = nil, eligible = nil,
|
9
|
+
overridden = nil, full_on = nil, custom = nil,
|
10
|
+
audience_mismatch = nil)
|
11
|
+
@id = id
|
12
|
+
@name = name
|
13
|
+
@unit = unit
|
14
|
+
@variant = variant
|
15
|
+
@exposed_at = exposed_at
|
16
|
+
@assigned = assigned
|
17
|
+
@eligible = eligible
|
18
|
+
@overridden = overridden
|
19
|
+
@full_on = full_on
|
20
|
+
@custom = custom
|
21
|
+
@audience_mismatch = audience_mismatch
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(o)
|
25
|
+
return true if self.object_id == o.object_id
|
26
|
+
return false if o.nil? || self.class != o.class
|
27
|
+
|
28
|
+
@id == o.id && @name == o.name && @unit == o.unit &&
|
29
|
+
@variant == o.variant && @exposed_at == o.exposed_at &&
|
30
|
+
@assigned == o.assigned && @eligible == o.eligible &&
|
31
|
+
@overridden == o.overridden && @full_on == o.full_on &&
|
32
|
+
@custom == o.custom && @audience_mismatch == o.audience_mismatch
|
33
|
+
end
|
34
|
+
|
35
|
+
def hash_code
|
36
|
+
{
|
37
|
+
id: @id, name: @name, unit: @unit,
|
38
|
+
variant: @variant, exposed_at: @exposed_at,
|
39
|
+
assigned: @assigned, eligible: @eligible,
|
40
|
+
overridden: @overridden, full_on: @full_on,
|
41
|
+
custom: @custom, audience_mismatch: @audience_mismatch
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
"Exposure{" +
|
47
|
+
"id=" + @id +
|
48
|
+
"name='" + @name + "'" +
|
49
|
+
", unit=" + @unit +
|
50
|
+
", variant=" + @variant +
|
51
|
+
", exposed_at=" + @exposed_at +
|
52
|
+
", assigned=" + @assigned +
|
53
|
+
", eligible=" + @eligible +
|
54
|
+
", overridden=" + @overridden +
|
55
|
+
", full_on=" + @full_on +
|
56
|
+
", custom=" + @custom +
|
57
|
+
", audience_mismatch=" + @audience_mismatch +
|
58
|
+
"}"
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class GoalAchievement
|
4
|
+
attr_accessor :name, :achieved_at, :properties
|
5
|
+
|
6
|
+
def initialize(name = nil, achieved_at = nil, properties = nil)
|
7
|
+
@name = name
|
8
|
+
@achieved_at = achieved_at
|
9
|
+
@properties = properties
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(o)
|
13
|
+
return true if self.object_id == o.object_id
|
14
|
+
return false if o.nil? || self.class != o.class
|
15
|
+
|
16
|
+
that = o
|
17
|
+
@name == that.name && @achieved_at == that.achieved_at &&
|
18
|
+
@properties == that.properties
|
19
|
+
end
|
20
|
+
|
21
|
+
def hash_code
|
22
|
+
{ name: @name, achieved_at: @achieved_at, properties: @properties }
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
"GoalAchievement{" +
|
27
|
+
"name='" + @name + "'" +
|
28
|
+
", achieved_at='" + @achieved_at + "'" +
|
29
|
+
", properties='" + @properties.inspect + "'" +
|
30
|
+
"}"
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class PublishEvent
|
4
|
+
attr_accessor :hashed, :units, :published_at, :exposures, :goals, :attributes
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@published_at = 0
|
8
|
+
@hashed = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(o)
|
12
|
+
return true if self.object_id == o.object_id
|
13
|
+
return false if o.nil? || self.class != o.class
|
14
|
+
|
15
|
+
that = o
|
16
|
+
@hashed == that.hashed && @units == that.units &&
|
17
|
+
@published_at == that.published_at && @exposures == that.exposures &&
|
18
|
+
@goals == that.goals && @attributes == that.attributes
|
19
|
+
end
|
20
|
+
|
21
|
+
def hash_code
|
22
|
+
{
|
23
|
+
hashed: @hashed,
|
24
|
+
units: @units,
|
25
|
+
published_at: @published_at,
|
26
|
+
exposures: @exposures,
|
27
|
+
goals: @goals,
|
28
|
+
attributes: @attributes
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
"PublishEvent{" +
|
34
|
+
"hashedUnits=" + @hashed +
|
35
|
+
", units=" + @units.inspect +
|
36
|
+
", publishedAt=" + @published_at +
|
37
|
+
", exposures=" + @exposures.inspect +
|
38
|
+
", goals=" + @goals.inspect +
|
39
|
+
", attributes=" + @attributes.join +
|
40
|
+
"}"
|
41
|
+
end
|
42
|
+
end
|
data/lib/json/unit.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Unit
|
4
|
+
attr_accessor :type, :uid
|
5
|
+
|
6
|
+
def initialize(type = nil, uid = nil)
|
7
|
+
@type = type
|
8
|
+
@uid = uid
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(o)
|
12
|
+
return true if self.object_id == o.object_id
|
13
|
+
return false if o.nil? || self.class != o.class
|
14
|
+
|
15
|
+
@type == o.type && @uid == o.uid
|
16
|
+
end
|
17
|
+
|
18
|
+
def hash_code
|
19
|
+
{
|
20
|
+
type: @type, uid: @uid
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"Unit{" +
|
26
|
+
"type='" + @type + "'" +
|
27
|
+
", uid=" + @uid +
|
28
|
+
"}"
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Evaluator
|
4
|
+
def evaluate(expr)
|
5
|
+
raise NotImplementedError.new("You must implement evaluate method.")
|
6
|
+
end
|
7
|
+
|
8
|
+
def boolean_convert(_)
|
9
|
+
raise NotImplementedError.new("You must implement boolean convert method.")
|
10
|
+
end
|
11
|
+
|
12
|
+
def number_convert(_)
|
13
|
+
raise NotImplementedError.new("You must implement number convert method.")
|
14
|
+
end
|
15
|
+
|
16
|
+
def string_convert(_)
|
17
|
+
raise NotImplementedError.new("You must implement string convert method.")
|
18
|
+
end
|
19
|
+
|
20
|
+
def extract_var(_)
|
21
|
+
raise NotImplementedError.new("You must implement extract var method.")
|
22
|
+
end
|
23
|
+
|
24
|
+
def compare(_, _)
|
25
|
+
raise NotImplementedError.new("You must implement extract_var method.")
|
26
|
+
end
|
27
|
+
end
|