hackle-ruby-sdk 1.0.0 → 2.0.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 +4 -4
- data/lib/hackle/client.rb +186 -87
- data/lib/hackle/config.rb +59 -17
- data/lib/hackle/decision.rb +113 -0
- data/lib/hackle/event.rb +89 -0
- data/lib/hackle/internal/clock/clock.rb +47 -0
- data/lib/hackle/internal/concurrent/executors.rb +20 -0
- data/lib/hackle/internal/concurrent/schedule/scheduler.rb +12 -0
- data/lib/hackle/internal/concurrent/schedule/timer_scheduler.rb +30 -0
- data/lib/hackle/internal/config/parameter_config.rb +50 -0
- data/lib/hackle/internal/core/hackle_core.rb +182 -0
- data/lib/hackle/{decision → internal/evaluation/bucketer}/bucketer.rb +17 -15
- data/lib/hackle/internal/evaluation/evaluator/contextual/contextual_evaluator.rb +29 -0
- data/lib/hackle/internal/evaluation/evaluator/delegating/delegating_evaluator.rb +26 -0
- data/lib/hackle/internal/evaluation/evaluator/evaluator.rb +117 -0
- data/lib/hackle/internal/evaluation/evaluator/experiment/experiment_evaluation_flow_factory.rb +67 -0
- data/lib/hackle/internal/evaluation/evaluator/experiment/experiment_evaluator.rb +172 -0
- data/lib/hackle/internal/evaluation/evaluator/experiment/experiment_flow_evaluator.rb +241 -0
- data/lib/hackle/internal/evaluation/evaluator/experiment/experiment_resolver.rb +166 -0
- data/lib/hackle/internal/evaluation/evaluator/remoteconfig/remote_config_determiner.rb +48 -0
- data/lib/hackle/internal/evaluation/evaluator/remoteconfig/remote_config_evaluator.rb +174 -0
- data/lib/hackle/internal/evaluation/flow/evaluation_flow.rb +49 -0
- data/lib/hackle/internal/evaluation/flow/flow_evaluator.rb +11 -0
- data/lib/hackle/internal/evaluation/match/condition/condition_matcher.rb +11 -0
- data/lib/hackle/internal/evaluation/match/condition/condition_matcher_factory.rb +53 -0
- data/lib/hackle/internal/evaluation/match/condition/experiment/experiment_condition_matcher.rb +29 -0
- data/lib/hackle/internal/evaluation/match/condition/experiment/experiment_evaluator_matcher.rb +135 -0
- data/lib/hackle/internal/evaluation/match/condition/segment/segment_condition_matcher.rb +67 -0
- data/lib/hackle/internal/evaluation/match/condition/user/user_condition_matcher.rb +44 -0
- data/lib/hackle/internal/evaluation/match/operator/operator_matcher.rb +185 -0
- data/lib/hackle/internal/evaluation/match/operator/operator_matcher_factory.rb +31 -0
- data/lib/hackle/internal/evaluation/match/target/target_matcher.rb +31 -0
- data/lib/hackle/internal/evaluation/match/value/value_matcher.rb +96 -0
- data/lib/hackle/internal/evaluation/match/value/value_matcher_factory.rb +28 -0
- data/lib/hackle/internal/evaluation/match/value/value_operator_matcher.rb +59 -0
- data/lib/hackle/internal/event/user_event.rb +187 -0
- data/lib/hackle/internal/event/user_event_dispatcher.rb +156 -0
- data/lib/hackle/internal/event/user_event_factory.rb +58 -0
- data/lib/hackle/internal/event/user_event_processor.rb +181 -0
- data/lib/hackle/internal/http/http.rb +28 -0
- data/lib/hackle/internal/http/http_client.rb +48 -0
- data/lib/hackle/internal/identifiers/identifier_builder.rb +67 -0
- data/lib/hackle/internal/logger/logger.rb +31 -0
- data/lib/hackle/internal/model/action.rb +57 -0
- data/lib/hackle/internal/model/bucket.rb +58 -0
- data/lib/hackle/internal/model/container.rb +47 -0
- data/lib/hackle/internal/model/decision_reason.rb +31 -0
- data/lib/hackle/{models → internal/model}/event_type.rb +5 -8
- data/lib/hackle/internal/model/experiment.rb +194 -0
- data/lib/hackle/internal/model/parameter_configuration.rb +19 -0
- data/lib/hackle/internal/model/remote_config_parameter.rb +76 -0
- data/lib/hackle/internal/model/sdk.rb +23 -0
- data/lib/hackle/internal/model/segment.rb +61 -0
- data/lib/hackle/internal/model/target.rb +203 -0
- data/lib/hackle/internal/model/target_rule.rb +19 -0
- data/lib/hackle/internal/model/targeting.rb +45 -0
- data/lib/hackle/internal/model/value_type.rb +75 -0
- data/lib/hackle/internal/model/variation.rb +27 -0
- data/lib/hackle/internal/model/version.rb +153 -0
- data/lib/hackle/internal/properties/properties_builder.rb +101 -0
- data/lib/hackle/internal/user/hackle_user.rb +74 -0
- data/lib/hackle/internal/user/hackle_user_resolver.rb +27 -0
- data/lib/hackle/internal/workspace/http_workspace_fetcher.rb +50 -0
- data/lib/hackle/internal/workspace/polling_workspace_fetcher.rb +62 -0
- data/lib/hackle/internal/workspace/workspace.rb +353 -0
- data/lib/hackle/internal/workspace/workspace_fetcher.rb +18 -0
- data/lib/hackle/remote_config.rb +55 -0
- data/lib/hackle/user.rb +124 -0
- data/lib/hackle/version.rb +1 -11
- data/lib/hackle.rb +4 -69
- metadata +123 -53
- data/.gitignore +0 -11
- data/.rspec +0 -2
- data/.travis.yml +0 -7
- data/Gemfile +0 -6
- data/README.md +0 -33
- data/Rakefile +0 -6
- data/hackle-ruby-sdk.gemspec +0 -29
- data/lib/hackle/decision/decider.rb +0 -69
- data/lib/hackle/events/event_dispatcher.rb +0 -96
- data/lib/hackle/events/event_processor.rb +0 -126
- data/lib/hackle/events/user_event.rb +0 -61
- data/lib/hackle/http/http.rb +0 -37
- data/lib/hackle/models/bucket.rb +0 -26
- data/lib/hackle/models/event.rb +0 -26
- data/lib/hackle/models/experiment.rb +0 -69
- data/lib/hackle/models/slot.rb +0 -22
- data/lib/hackle/models/user.rb +0 -24
- data/lib/hackle/models/variation.rb +0 -21
- data/lib/hackle/workspaces/http_workspace_fetcher.rb +0 -24
- data/lib/hackle/workspaces/polling_workspace_fetcher.rb +0 -47
- data/lib/hackle/workspaces/workspace.rb +0 -100
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hackle
|
|
4
|
+
module OperatorMatcher
|
|
5
|
+
# @param value [String]
|
|
6
|
+
# @param match_value [String]
|
|
7
|
+
# @return [boolean]
|
|
8
|
+
def string_matches(value, match_value) end
|
|
9
|
+
|
|
10
|
+
# @param value [Numeric]
|
|
11
|
+
# @param match_value [Numeric]
|
|
12
|
+
# @return [boolean]
|
|
13
|
+
def number_matches(value, match_value) end
|
|
14
|
+
|
|
15
|
+
# @param value [boolean]
|
|
16
|
+
# @param match_value [boolean]
|
|
17
|
+
# @return [boolean]
|
|
18
|
+
def boolean_matches(value, match_value) end
|
|
19
|
+
|
|
20
|
+
# @param value [Version]
|
|
21
|
+
# @param match_value [Version]
|
|
22
|
+
# @return [boolean]
|
|
23
|
+
def version_matches(value, match_value) end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class InMatcher
|
|
27
|
+
include OperatorMatcher
|
|
28
|
+
|
|
29
|
+
def string_matches(value, match_value)
|
|
30
|
+
value == match_value
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def number_matches(value, match_value)
|
|
34
|
+
value == match_value
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def boolean_matches(value, match_value)
|
|
38
|
+
value == match_value
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def version_matches(value, match_value)
|
|
42
|
+
value == match_value
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class ContainsMatcher
|
|
47
|
+
include OperatorMatcher
|
|
48
|
+
|
|
49
|
+
def string_matches(value, match_value)
|
|
50
|
+
value.include?(match_value)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def number_matches(_value, _match_value)
|
|
54
|
+
false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def boolean_matches(_value, _match_value)
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def version_matches(_value, _match_value)
|
|
62
|
+
false
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class StartsWithMatcher
|
|
67
|
+
include OperatorMatcher
|
|
68
|
+
|
|
69
|
+
def string_matches(value, match_value)
|
|
70
|
+
value.start_with?(match_value)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def number_matches(_value, _match_value)
|
|
74
|
+
false
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def boolean_matches(_value, _match_value)
|
|
78
|
+
false
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def version_matches(_value, _match_value)
|
|
82
|
+
false
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class EndsWithMatcher
|
|
87
|
+
include OperatorMatcher
|
|
88
|
+
|
|
89
|
+
def string_matches(value, match_value)
|
|
90
|
+
value.end_with?(match_value)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def number_matches(_value, _match_value)
|
|
94
|
+
false
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def boolean_matches(_value, _match_value)
|
|
98
|
+
false
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def version_matches(_value, _match_value)
|
|
102
|
+
false
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
class GreaterThanMatcher
|
|
107
|
+
include OperatorMatcher
|
|
108
|
+
|
|
109
|
+
def string_matches(value, match_value)
|
|
110
|
+
value > match_value
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def number_matches(value, match_value)
|
|
114
|
+
value > match_value
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def boolean_matches(_value, _match_value)
|
|
118
|
+
false
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def version_matches(value, match_value)
|
|
122
|
+
value > match_value
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
class GreaterThanOrEqualToMatcher
|
|
127
|
+
include OperatorMatcher
|
|
128
|
+
|
|
129
|
+
def string_matches(value, match_value)
|
|
130
|
+
value >= match_value
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def number_matches(value, match_value)
|
|
134
|
+
value >= match_value
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def boolean_matches(_value, _match_value)
|
|
138
|
+
false
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def version_matches(value, match_value)
|
|
142
|
+
value >= match_value
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
class LessThanMatcher
|
|
147
|
+
include OperatorMatcher
|
|
148
|
+
|
|
149
|
+
def string_matches(value, match_value)
|
|
150
|
+
value < match_value
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def number_matches(value, match_value)
|
|
154
|
+
value < match_value
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def boolean_matches(_value, _match_value)
|
|
158
|
+
false
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def version_matches(value, match_value)
|
|
162
|
+
value < match_value
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
class LessThanOrEqualToMatcher
|
|
167
|
+
include OperatorMatcher
|
|
168
|
+
|
|
169
|
+
def string_matches(value, match_value)
|
|
170
|
+
value <= match_value
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def number_matches(value, match_value)
|
|
174
|
+
value <= match_value
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def boolean_matches(_value, _match_value)
|
|
178
|
+
false
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def version_matches(value, match_value)
|
|
182
|
+
value <= match_value
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'hackle/internal/model/target'
|
|
4
|
+
require 'hackle/internal/evaluation/match/operator/operator_matcher'
|
|
5
|
+
|
|
6
|
+
module Hackle
|
|
7
|
+
class OperatorMatcherFactory
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@matchers = {
|
|
11
|
+
TargetOperator::IN => InMatcher.new,
|
|
12
|
+
TargetOperator::CONTAINS => ContainsMatcher.new,
|
|
13
|
+
TargetOperator::STARTS_WITH => StartsWithMatcher.new,
|
|
14
|
+
TargetOperator::ENDS_WITH => EndsWithMatcher.new,
|
|
15
|
+
TargetOperator::GT => GreaterThanMatcher.new,
|
|
16
|
+
TargetOperator::GTE => GreaterThanOrEqualToMatcher.new,
|
|
17
|
+
TargetOperator::LT => LessThanMatcher.new,
|
|
18
|
+
TargetOperator::LTE => LessThanOrEqualToMatcher.new
|
|
19
|
+
}.freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @param operator [TargetOperator]
|
|
23
|
+
# @return [OperatorMatcher]
|
|
24
|
+
def get(operator)
|
|
25
|
+
matcher = @matchers[operator]
|
|
26
|
+
raise ArgumentError, "Unsupported TargetOperator [#{operator}]" unless matcher
|
|
27
|
+
|
|
28
|
+
matcher
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hackle
|
|
4
|
+
class TargetMatcher
|
|
5
|
+
|
|
6
|
+
# @param condition_matcher_factory [ConditionMatcherFactory]
|
|
7
|
+
def initialize(condition_matcher_factory:)
|
|
8
|
+
# @type [ConditionMatcherFactory]
|
|
9
|
+
@condition_matcher_factory = condition_matcher_factory
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @param request [EvaluatorRequest]
|
|
13
|
+
# @param context [EvaluatorContext]
|
|
14
|
+
# @param target [Target]
|
|
15
|
+
# @return [boolean]
|
|
16
|
+
def matches(request, context, target)
|
|
17
|
+
target.conditions.all? { |it| condition_matches(request, context, it) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
# @param request [EvaluatorRequest]
|
|
23
|
+
# @param context [EvaluatorContext]
|
|
24
|
+
# @param condition [TargetCondition]
|
|
25
|
+
# @return [boolean]
|
|
26
|
+
def condition_matches(request, context, condition)
|
|
27
|
+
condition_matcher = @condition_matcher_factory.get(condition.key.type)
|
|
28
|
+
condition_matcher.matches(request, context, condition)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'hackle/internal/model/version'
|
|
4
|
+
|
|
5
|
+
module Hackle
|
|
6
|
+
module ValueMatcher
|
|
7
|
+
# @param operator_matcher [OperatorMatcher]
|
|
8
|
+
# @param value [Object]
|
|
9
|
+
# @param match_value [Object]
|
|
10
|
+
# @return [boolean]
|
|
11
|
+
def matches(operator_matcher, value, match_value) end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class StringMatcher
|
|
15
|
+
include ValueMatcher
|
|
16
|
+
|
|
17
|
+
def matches(operator_matcher, value, match_value)
|
|
18
|
+
type_value = as_string(value)
|
|
19
|
+
type_match_value = as_string(match_value)
|
|
20
|
+
return false if type_value.nil? || type_match_value.nil?
|
|
21
|
+
|
|
22
|
+
operator_matcher.string_matches(type_value, type_match_value)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
# @param value [Object]
|
|
28
|
+
# @return [String, nil]
|
|
29
|
+
# noinspection RubyMismatchedReturnType
|
|
30
|
+
def as_string(value)
|
|
31
|
+
return value if value.is_a?(String)
|
|
32
|
+
return value.to_s if value.is_a?(Numeric)
|
|
33
|
+
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class NumberMatcher
|
|
39
|
+
include ValueMatcher
|
|
40
|
+
|
|
41
|
+
def matches(operator_matcher, value, match_value)
|
|
42
|
+
type_value = as_number(value)
|
|
43
|
+
type_match_value = as_number(match_value)
|
|
44
|
+
return false if type_value.nil? || type_match_value.nil?
|
|
45
|
+
|
|
46
|
+
operator_matcher.number_matches(type_value, type_match_value)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# @param value [Object]
|
|
52
|
+
# @return [Numeric, nil]
|
|
53
|
+
# noinspection RubyMismatchedReturnType
|
|
54
|
+
def as_number(value)
|
|
55
|
+
return value if value.is_a?(Numeric)
|
|
56
|
+
return Float(value, exception: false) if value.is_a?(String)
|
|
57
|
+
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class BooleanMatcher
|
|
63
|
+
include ValueMatcher
|
|
64
|
+
|
|
65
|
+
def matches(operator_matcher, value, match_value)
|
|
66
|
+
type_value = as_boolean(value)
|
|
67
|
+
type_match_value = as_boolean(match_value)
|
|
68
|
+
return false if type_value.nil? || type_match_value.nil?
|
|
69
|
+
|
|
70
|
+
operator_matcher.boolean_matches(type_value, type_match_value)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
# @param value [Object]
|
|
76
|
+
# @return [boolean, nil]
|
|
77
|
+
def as_boolean(value)
|
|
78
|
+
return true if value.is_a?(TrueClass)
|
|
79
|
+
return false if value.is_a?(FalseClass)
|
|
80
|
+
|
|
81
|
+
nil
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class VersionMatcher
|
|
86
|
+
include ValueMatcher
|
|
87
|
+
|
|
88
|
+
def matches(operator_matcher, value, match_value)
|
|
89
|
+
type_value = Version.parse_or_nil(value)
|
|
90
|
+
type_match_value = Version.parse_or_nil(match_value)
|
|
91
|
+
return false if type_value.nil? || type_match_value.nil?
|
|
92
|
+
|
|
93
|
+
operator_matcher.version_matches(type_value, type_match_value)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'hackle/internal/model/value_type'
|
|
4
|
+
require 'hackle/internal/evaluation/match/value/value_matcher'
|
|
5
|
+
|
|
6
|
+
module Hackle
|
|
7
|
+
class ValueMatcherFactory
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@matchers = {
|
|
11
|
+
ValueType::STRING => StringMatcher.new,
|
|
12
|
+
ValueType::NUMBER => NumberMatcher.new,
|
|
13
|
+
ValueType::BOOLEAN => BooleanMatcher.new,
|
|
14
|
+
ValueType::VERSION => VersionMatcher.new,
|
|
15
|
+
ValueType::JSON => StringMatcher.new
|
|
16
|
+
}.freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @param value_type [ValueType]
|
|
20
|
+
# @return [ValueMatcher]
|
|
21
|
+
def get(value_type)
|
|
22
|
+
matcher = @matchers[value_type]
|
|
23
|
+
raise ArgumentError, "Unsupported ValueType [#{value_type}]" unless matcher
|
|
24
|
+
|
|
25
|
+
matcher
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'hackle/internal/model/target'
|
|
4
|
+
|
|
5
|
+
module Hackle
|
|
6
|
+
class ValueOperatorMatcher
|
|
7
|
+
# @param value_matcher_factory [ValueMatcherFactory]
|
|
8
|
+
# @param operator_matcher_factory [OperatorMatcherFactory]
|
|
9
|
+
def initialize(value_matcher_factory:, operator_matcher_factory:)
|
|
10
|
+
# @type [ValueMatcherFactory]
|
|
11
|
+
@value_matcher_factory = value_matcher_factory
|
|
12
|
+
# @type [OperatorMatcherFactory]
|
|
13
|
+
@operator_matcher_factory = operator_matcher_factory
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @param value [Object]
|
|
17
|
+
# @param match [TargetMatch]
|
|
18
|
+
# @return [boolean]
|
|
19
|
+
def matches(value, match)
|
|
20
|
+
value_matcher = @value_matcher_factory.get(match.value_type)
|
|
21
|
+
operator_matcher = @operator_matcher_factory.get(match.operator)
|
|
22
|
+
|
|
23
|
+
matches = internal_matches(value, match, value_matcher, operator_matcher)
|
|
24
|
+
TargetMatchType.matches(match.type, matches)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
# @param value [Object]
|
|
30
|
+
# @param match [TargetMatch]
|
|
31
|
+
# @param value_matcher [ValueMatcher]
|
|
32
|
+
# @param operator_matcher [OperatorMatcher]
|
|
33
|
+
# @return [boolean]
|
|
34
|
+
def internal_matches(value, match, value_matcher, operator_matcher)
|
|
35
|
+
# noinspection RubyMismatchedArgumentType
|
|
36
|
+
return array_matches(value, match, value_matcher, operator_matcher) if value.is_a?(Array)
|
|
37
|
+
|
|
38
|
+
single_matches(value, match, value_matcher, operator_matcher)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @param value [Object]
|
|
42
|
+
# @param match [TargetMatch]
|
|
43
|
+
# @param value_matcher [ValueMatcher]
|
|
44
|
+
# @param operator_matcher [OperatorMatcher]
|
|
45
|
+
# @return [boolean]
|
|
46
|
+
def single_matches(value, match, value_matcher, operator_matcher)
|
|
47
|
+
match.values.any? { |it| value_matcher.matches(operator_matcher, value, it) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @param values [Array<Object>]
|
|
51
|
+
# @param match [TargetMatch]
|
|
52
|
+
# @param value_matcher [ValueMatcher]
|
|
53
|
+
# @param operator_matcher [OperatorMatcher]
|
|
54
|
+
# @return [boolean]
|
|
55
|
+
def array_matches(values, match, value_matcher, operator_matcher)
|
|
56
|
+
values.any? { |it| single_matches(it, match, value_matcher, operator_matcher) }
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
module Hackle
|
|
6
|
+
class UserEvent
|
|
7
|
+
|
|
8
|
+
# @return [String]
|
|
9
|
+
attr_reader :insert_id
|
|
10
|
+
|
|
11
|
+
# @return [Integer]
|
|
12
|
+
attr_reader :timestamp
|
|
13
|
+
|
|
14
|
+
# @return [HackleUser]
|
|
15
|
+
attr_reader :user
|
|
16
|
+
|
|
17
|
+
# @param insert_id [String]
|
|
18
|
+
# @param timestamp [Integer]
|
|
19
|
+
# @param user [HackleUser]
|
|
20
|
+
def initialize(insert_id:, timestamp:, user:)
|
|
21
|
+
@insert_id = insert_id
|
|
22
|
+
@timestamp = timestamp
|
|
23
|
+
@user = user
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
|
|
28
|
+
# @param evaluation [ExperimentEvaluation]
|
|
29
|
+
# @param properties [Hash{String => Object}]
|
|
30
|
+
# @param user [HackleUser]
|
|
31
|
+
# @param timestamp [Integer]
|
|
32
|
+
# @return [Hackle::ExposureEvent]
|
|
33
|
+
def exposure(evaluation, properties, user, timestamp)
|
|
34
|
+
ExposureEvent.new(
|
|
35
|
+
insert_id: SecureRandom.uuid,
|
|
36
|
+
timestamp: timestamp,
|
|
37
|
+
user: user,
|
|
38
|
+
experiment: evaluation.experiment,
|
|
39
|
+
variation_id: evaluation.variation_id,
|
|
40
|
+
variation_key: evaluation.variation_key,
|
|
41
|
+
decision_reason: evaluation.reason,
|
|
42
|
+
properties: properties
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @param event_type [EventType]
|
|
47
|
+
# @param event [Event]
|
|
48
|
+
# @param user [HackleUser]
|
|
49
|
+
# @param timestamp [Integer]
|
|
50
|
+
# @return [Hackle::TrackEvent]
|
|
51
|
+
def track(event_type, event, user, timestamp)
|
|
52
|
+
TrackEvent.new(
|
|
53
|
+
insert_id: SecureRandom.uuid,
|
|
54
|
+
timestamp: timestamp,
|
|
55
|
+
user: user,
|
|
56
|
+
event_type: event_type,
|
|
57
|
+
event: event
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @param evaluation [RemoteConfigEvaluation]
|
|
62
|
+
# @param properties [Hash{String => Object}]
|
|
63
|
+
# @param user [HackleUser]
|
|
64
|
+
# @param timestamp [Integer]
|
|
65
|
+
# @return [Hackle::RemoteConfigEvent]
|
|
66
|
+
def remote_config(evaluation, properties, user, timestamp)
|
|
67
|
+
RemoteConfigEvent.new(
|
|
68
|
+
insert_id: SecureRandom.uuid,
|
|
69
|
+
timestamp: timestamp,
|
|
70
|
+
user: user,
|
|
71
|
+
parameter: evaluation.parameter,
|
|
72
|
+
value_id: evaluation.value_id,
|
|
73
|
+
decision_reason: evaluation.reason,
|
|
74
|
+
properties: properties
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class ExposureEvent < UserEvent
|
|
81
|
+
|
|
82
|
+
# @return [Experiment]
|
|
83
|
+
attr_reader :experiment
|
|
84
|
+
|
|
85
|
+
# @return [Integer, nil]
|
|
86
|
+
attr_reader :variation_id
|
|
87
|
+
|
|
88
|
+
# @return [String]
|
|
89
|
+
attr_reader :variation_key
|
|
90
|
+
|
|
91
|
+
# @return [String]
|
|
92
|
+
attr_reader :decision_reason
|
|
93
|
+
|
|
94
|
+
# @return [Hash{String => Object}]
|
|
95
|
+
attr_reader :properties
|
|
96
|
+
|
|
97
|
+
# @param insert_id [String]
|
|
98
|
+
# @param timestamp [Integer]
|
|
99
|
+
# @param user [HackleUser]
|
|
100
|
+
# @param experiment [Experiment]
|
|
101
|
+
# @param variation_id [Integer, nil]
|
|
102
|
+
# @param variation_key [String]
|
|
103
|
+
# @param decision_reason [String]
|
|
104
|
+
# @param properties [Hash{String => Object}]
|
|
105
|
+
def initialize(
|
|
106
|
+
insert_id:,
|
|
107
|
+
timestamp:,
|
|
108
|
+
user:,
|
|
109
|
+
experiment:,
|
|
110
|
+
variation_id:,
|
|
111
|
+
variation_key:,
|
|
112
|
+
decision_reason:,
|
|
113
|
+
properties:
|
|
114
|
+
)
|
|
115
|
+
super(insert_id: insert_id, timestamp: timestamp, user: user)
|
|
116
|
+
@experiment = experiment
|
|
117
|
+
@variation_id = variation_id
|
|
118
|
+
@variation_key = variation_key
|
|
119
|
+
@decision_reason = decision_reason
|
|
120
|
+
@properties = properties
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class TrackEvent < UserEvent
|
|
125
|
+
|
|
126
|
+
# @return [EventType]
|
|
127
|
+
attr_reader :event_type
|
|
128
|
+
|
|
129
|
+
# @return [Event]
|
|
130
|
+
attr_reader :event
|
|
131
|
+
|
|
132
|
+
# @param insert_id [String]
|
|
133
|
+
# @param timestamp [Integer]
|
|
134
|
+
# @param user [HackleUser]
|
|
135
|
+
# @param event_type [EventType]
|
|
136
|
+
# @param event [Event]
|
|
137
|
+
def initialize(
|
|
138
|
+
insert_id:,
|
|
139
|
+
timestamp:,
|
|
140
|
+
user:,
|
|
141
|
+
event_type:,
|
|
142
|
+
event:
|
|
143
|
+
)
|
|
144
|
+
super(insert_id: insert_id, timestamp: timestamp, user: user)
|
|
145
|
+
@event_type = event_type
|
|
146
|
+
@event = event
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
class RemoteConfigEvent < UserEvent
|
|
151
|
+
|
|
152
|
+
# @return [RemoteConfigParameter]
|
|
153
|
+
attr_reader :parameter
|
|
154
|
+
|
|
155
|
+
# @return [Integer, nil]
|
|
156
|
+
attr_reader :value_id
|
|
157
|
+
|
|
158
|
+
# @return [String]
|
|
159
|
+
attr_reader :decision_reason
|
|
160
|
+
|
|
161
|
+
# @return [Hash{String => Object}]
|
|
162
|
+
attr_reader :properties
|
|
163
|
+
|
|
164
|
+
# @param insert_id [String]
|
|
165
|
+
# @param timestamp [Integer]
|
|
166
|
+
# @param user [HackleUser]
|
|
167
|
+
# @param parameter [RemoteConfigParameter]
|
|
168
|
+
# @param value_id [Integer, nil]
|
|
169
|
+
# @param decision_reason [String]
|
|
170
|
+
# @param properties [Hash{String => Object}]
|
|
171
|
+
def initialize(
|
|
172
|
+
insert_id:,
|
|
173
|
+
timestamp:,
|
|
174
|
+
user:,
|
|
175
|
+
parameter:,
|
|
176
|
+
value_id:,
|
|
177
|
+
decision_reason:,
|
|
178
|
+
properties:
|
|
179
|
+
)
|
|
180
|
+
super(insert_id: insert_id, timestamp: timestamp, user: user)
|
|
181
|
+
@parameter = parameter
|
|
182
|
+
@value_id = value_id
|
|
183
|
+
@decision_reason = decision_reason
|
|
184
|
+
@properties = properties
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|