absmartly-sdk 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|