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
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hackle-ruby-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hackle
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2024-05-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -16,70 +16,98 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '2.0'
|
|
20
20
|
type: :development
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '2.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: rake
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0'
|
|
33
|
+
version: '13.0'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0'
|
|
40
|
+
version: '13.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
42
|
+
name: rspec
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
47
|
+
version: '3.0'
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
54
|
+
version: '3.0'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
56
|
+
name: rubocop
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
59
|
- - "~>"
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
61
|
+
version: '1.28'
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
68
|
+
version: '1.28'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
|
-
name:
|
|
70
|
+
name: simplecov
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0.21'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0.21'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: simplecov-cobertura
|
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
|
72
86
|
requirements:
|
|
73
|
-
- -
|
|
87
|
+
- - "~>"
|
|
74
88
|
- !ruby/object:Gem::Version
|
|
75
|
-
version:
|
|
89
|
+
version: '2.1'
|
|
76
90
|
type: :development
|
|
77
91
|
prerelease: false
|
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
93
|
requirements:
|
|
80
|
-
- -
|
|
94
|
+
- - "~>"
|
|
81
95
|
- !ruby/object:Gem::Version
|
|
82
|
-
version:
|
|
96
|
+
version: '2.1'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: simplecov-lcov
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: 0.7.0
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: 0.7.0
|
|
83
111
|
- !ruby/object:Gem::Dependency
|
|
84
112
|
name: concurrent-ruby
|
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -98,16 +126,16 @@ dependencies:
|
|
|
98
126
|
name: json
|
|
99
127
|
requirement: !ruby/object:Gem::Requirement
|
|
100
128
|
requirements:
|
|
101
|
-
- - "
|
|
129
|
+
- - "~>"
|
|
102
130
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '
|
|
131
|
+
version: '2.3'
|
|
104
132
|
type: :runtime
|
|
105
133
|
prerelease: false
|
|
106
134
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
135
|
requirements:
|
|
108
|
-
- - "
|
|
136
|
+
- - "~>"
|
|
109
137
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: '
|
|
138
|
+
version: '2.3'
|
|
111
139
|
- !ruby/object:Gem::Dependency
|
|
112
140
|
name: murmurhash3
|
|
113
141
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -122,46 +150,88 @@ dependencies:
|
|
|
122
150
|
- - "~>"
|
|
123
151
|
- !ruby/object:Gem::Version
|
|
124
152
|
version: '0.1'
|
|
125
|
-
description:
|
|
153
|
+
description: Ruby SDK for Hackle A/B Tests, Feature Flags, Remote Configs, and Analytics.
|
|
126
154
|
email:
|
|
127
155
|
- platform@hackle.io
|
|
128
156
|
executables: []
|
|
129
157
|
extensions: []
|
|
130
158
|
extra_rdoc_files: []
|
|
131
159
|
files:
|
|
132
|
-
- ".gitignore"
|
|
133
|
-
- ".rspec"
|
|
134
|
-
- ".travis.yml"
|
|
135
|
-
- Gemfile
|
|
136
|
-
- README.md
|
|
137
|
-
- Rakefile
|
|
138
|
-
- hackle-ruby-sdk.gemspec
|
|
139
160
|
- lib/hackle-ruby-sdk.rb
|
|
140
161
|
- lib/hackle.rb
|
|
141
162
|
- lib/hackle/client.rb
|
|
142
163
|
- lib/hackle/config.rb
|
|
143
|
-
- lib/hackle/decision
|
|
144
|
-
- lib/hackle/
|
|
145
|
-
- lib/hackle/
|
|
146
|
-
- lib/hackle/
|
|
147
|
-
- lib/hackle/
|
|
148
|
-
- lib/hackle/
|
|
149
|
-
- lib/hackle/
|
|
150
|
-
- lib/hackle/
|
|
151
|
-
- lib/hackle/
|
|
152
|
-
- lib/hackle/
|
|
153
|
-
- lib/hackle/
|
|
154
|
-
- lib/hackle/
|
|
155
|
-
- lib/hackle/
|
|
164
|
+
- lib/hackle/decision.rb
|
|
165
|
+
- lib/hackle/event.rb
|
|
166
|
+
- lib/hackle/internal/clock/clock.rb
|
|
167
|
+
- lib/hackle/internal/concurrent/executors.rb
|
|
168
|
+
- lib/hackle/internal/concurrent/schedule/scheduler.rb
|
|
169
|
+
- lib/hackle/internal/concurrent/schedule/timer_scheduler.rb
|
|
170
|
+
- lib/hackle/internal/config/parameter_config.rb
|
|
171
|
+
- lib/hackle/internal/core/hackle_core.rb
|
|
172
|
+
- lib/hackle/internal/evaluation/bucketer/bucketer.rb
|
|
173
|
+
- lib/hackle/internal/evaluation/evaluator/contextual/contextual_evaluator.rb
|
|
174
|
+
- lib/hackle/internal/evaluation/evaluator/delegating/delegating_evaluator.rb
|
|
175
|
+
- lib/hackle/internal/evaluation/evaluator/evaluator.rb
|
|
176
|
+
- lib/hackle/internal/evaluation/evaluator/experiment/experiment_evaluation_flow_factory.rb
|
|
177
|
+
- lib/hackle/internal/evaluation/evaluator/experiment/experiment_evaluator.rb
|
|
178
|
+
- lib/hackle/internal/evaluation/evaluator/experiment/experiment_flow_evaluator.rb
|
|
179
|
+
- lib/hackle/internal/evaluation/evaluator/experiment/experiment_resolver.rb
|
|
180
|
+
- lib/hackle/internal/evaluation/evaluator/remoteconfig/remote_config_determiner.rb
|
|
181
|
+
- lib/hackle/internal/evaluation/evaluator/remoteconfig/remote_config_evaluator.rb
|
|
182
|
+
- lib/hackle/internal/evaluation/flow/evaluation_flow.rb
|
|
183
|
+
- lib/hackle/internal/evaluation/flow/flow_evaluator.rb
|
|
184
|
+
- lib/hackle/internal/evaluation/match/condition/condition_matcher.rb
|
|
185
|
+
- lib/hackle/internal/evaluation/match/condition/condition_matcher_factory.rb
|
|
186
|
+
- lib/hackle/internal/evaluation/match/condition/experiment/experiment_condition_matcher.rb
|
|
187
|
+
- lib/hackle/internal/evaluation/match/condition/experiment/experiment_evaluator_matcher.rb
|
|
188
|
+
- lib/hackle/internal/evaluation/match/condition/segment/segment_condition_matcher.rb
|
|
189
|
+
- lib/hackle/internal/evaluation/match/condition/user/user_condition_matcher.rb
|
|
190
|
+
- lib/hackle/internal/evaluation/match/operator/operator_matcher.rb
|
|
191
|
+
- lib/hackle/internal/evaluation/match/operator/operator_matcher_factory.rb
|
|
192
|
+
- lib/hackle/internal/evaluation/match/target/target_matcher.rb
|
|
193
|
+
- lib/hackle/internal/evaluation/match/value/value_matcher.rb
|
|
194
|
+
- lib/hackle/internal/evaluation/match/value/value_matcher_factory.rb
|
|
195
|
+
- lib/hackle/internal/evaluation/match/value/value_operator_matcher.rb
|
|
196
|
+
- lib/hackle/internal/event/user_event.rb
|
|
197
|
+
- lib/hackle/internal/event/user_event_dispatcher.rb
|
|
198
|
+
- lib/hackle/internal/event/user_event_factory.rb
|
|
199
|
+
- lib/hackle/internal/event/user_event_processor.rb
|
|
200
|
+
- lib/hackle/internal/http/http.rb
|
|
201
|
+
- lib/hackle/internal/http/http_client.rb
|
|
202
|
+
- lib/hackle/internal/identifiers/identifier_builder.rb
|
|
203
|
+
- lib/hackle/internal/logger/logger.rb
|
|
204
|
+
- lib/hackle/internal/model/action.rb
|
|
205
|
+
- lib/hackle/internal/model/bucket.rb
|
|
206
|
+
- lib/hackle/internal/model/container.rb
|
|
207
|
+
- lib/hackle/internal/model/decision_reason.rb
|
|
208
|
+
- lib/hackle/internal/model/event_type.rb
|
|
209
|
+
- lib/hackle/internal/model/experiment.rb
|
|
210
|
+
- lib/hackle/internal/model/parameter_configuration.rb
|
|
211
|
+
- lib/hackle/internal/model/remote_config_parameter.rb
|
|
212
|
+
- lib/hackle/internal/model/sdk.rb
|
|
213
|
+
- lib/hackle/internal/model/segment.rb
|
|
214
|
+
- lib/hackle/internal/model/target.rb
|
|
215
|
+
- lib/hackle/internal/model/target_rule.rb
|
|
216
|
+
- lib/hackle/internal/model/targeting.rb
|
|
217
|
+
- lib/hackle/internal/model/value_type.rb
|
|
218
|
+
- lib/hackle/internal/model/variation.rb
|
|
219
|
+
- lib/hackle/internal/model/version.rb
|
|
220
|
+
- lib/hackle/internal/properties/properties_builder.rb
|
|
221
|
+
- lib/hackle/internal/user/hackle_user.rb
|
|
222
|
+
- lib/hackle/internal/user/hackle_user_resolver.rb
|
|
223
|
+
- lib/hackle/internal/workspace/http_workspace_fetcher.rb
|
|
224
|
+
- lib/hackle/internal/workspace/polling_workspace_fetcher.rb
|
|
225
|
+
- lib/hackle/internal/workspace/workspace.rb
|
|
226
|
+
- lib/hackle/internal/workspace/workspace_fetcher.rb
|
|
227
|
+
- lib/hackle/remote_config.rb
|
|
228
|
+
- lib/hackle/user.rb
|
|
156
229
|
- lib/hackle/version.rb
|
|
157
|
-
- lib/hackle/workspaces/http_workspace_fetcher.rb
|
|
158
|
-
- lib/hackle/workspaces/polling_workspace_fetcher.rb
|
|
159
|
-
- lib/hackle/workspaces/workspace.rb
|
|
160
230
|
homepage: https://github.com/hackle-io/hackle-ruby-sdk
|
|
161
231
|
licenses:
|
|
162
232
|
- Apache-2.0
|
|
163
233
|
metadata: {}
|
|
164
|
-
post_install_message:
|
|
234
|
+
post_install_message:
|
|
165
235
|
rdoc_options: []
|
|
166
236
|
require_paths:
|
|
167
237
|
- lib
|
|
@@ -176,8 +246,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
176
246
|
- !ruby/object:Gem::Version
|
|
177
247
|
version: '0'
|
|
178
248
|
requirements: []
|
|
179
|
-
rubygems_version: 3.0.3
|
|
180
|
-
signing_key:
|
|
249
|
+
rubygems_version: 3.0.3.1
|
|
250
|
+
signing_key:
|
|
181
251
|
specification_version: 4
|
|
182
|
-
summary: Hackle SDK
|
|
252
|
+
summary: Hackle Ruby SDK
|
|
183
253
|
test_files: []
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/README.md
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# Hackle::Ruby::Sdk
|
|
2
|
-
|
|
3
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/hackle`.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
Add this line to your application's Gemfile:
|
|
8
|
-
|
|
9
|
-
```ruby
|
|
10
|
-
gem 'hackle-ruby-sdk'
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
And then execute:
|
|
14
|
-
|
|
15
|
-
$ bundle
|
|
16
|
-
|
|
17
|
-
Or install it yourself as:
|
|
18
|
-
|
|
19
|
-
$ gem install hackle-ruby-sdk
|
|
20
|
-
|
|
21
|
-
## Usage
|
|
22
|
-
|
|
23
|
-
TODO: Write usage instructions here
|
|
24
|
-
|
|
25
|
-
## Development
|
|
26
|
-
|
|
27
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
28
|
-
|
|
29
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
30
|
-
|
|
31
|
-
## Contributing
|
|
32
|
-
|
|
33
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/hackle-ruby-sdk.
|
data/Rakefile
DELETED
data/hackle-ruby-sdk.gemspec
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
lib = File.expand_path('lib', __dir__)
|
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
-
require 'hackle/version'
|
|
6
|
-
|
|
7
|
-
Gem::Specification.new do |spec|
|
|
8
|
-
spec.name = 'hackle-ruby-sdk'
|
|
9
|
-
spec.version = Hackle::VERSION
|
|
10
|
-
spec.authors = ['Hackle']
|
|
11
|
-
spec.email = ['platform@hackle.io']
|
|
12
|
-
spec.summary = 'Hackle SDK for Ruby'
|
|
13
|
-
spec.description = 'Hackle SDK for Ruby'
|
|
14
|
-
spec.homepage = 'https://github.com/hackle-io/hackle-ruby-sdk'
|
|
15
|
-
spec.license = 'Apache-2.0'
|
|
16
|
-
|
|
17
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
|
|
18
|
-
spec.require_paths = ['lib']
|
|
19
|
-
|
|
20
|
-
spec.add_development_dependency 'bundler', '~> 1.17'
|
|
21
|
-
spec.add_development_dependency 'coveralls'
|
|
22
|
-
spec.add_development_dependency 'rake', '~> 10.0'
|
|
23
|
-
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
24
|
-
spec.add_development_dependency 'rubocop', '0.73.0'
|
|
25
|
-
|
|
26
|
-
spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
|
|
27
|
-
spec.add_runtime_dependency 'json', '>= 1.8'
|
|
28
|
-
spec.add_runtime_dependency 'murmurhash3', '~> 0.1'
|
|
29
|
-
end
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Hackle
|
|
4
|
-
class Decision
|
|
5
|
-
|
|
6
|
-
class NotAllocated < Decision
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
class ForcedAllocated < Decision
|
|
10
|
-
# @return [String]
|
|
11
|
-
attr_reader :variation_key
|
|
12
|
-
|
|
13
|
-
# @param variation_key [String]
|
|
14
|
-
def initialize(variation_key:)
|
|
15
|
-
@variation_key = variation_key
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
class NaturalAllocated < Decision
|
|
20
|
-
# @return [Variation]
|
|
21
|
-
attr_reader :variation
|
|
22
|
-
|
|
23
|
-
# @param variation [Variation]
|
|
24
|
-
def initialize(variation:)
|
|
25
|
-
@variation = variation
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
class Decider
|
|
31
|
-
def initialize
|
|
32
|
-
@bucketer = Bucketer.new
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# @param experiment [Experiment]
|
|
36
|
-
# @param user [User]
|
|
37
|
-
#
|
|
38
|
-
# @return [Decision]
|
|
39
|
-
def decide(experiment:, user:)
|
|
40
|
-
case experiment
|
|
41
|
-
when Experiment::Completed
|
|
42
|
-
Decision::ForcedAllocated.new(variation_key: experiment.winner_variation_key)
|
|
43
|
-
when Experiment::Running
|
|
44
|
-
decide_running(running_experiment: experiment, user: user)
|
|
45
|
-
else
|
|
46
|
-
NotAllocated.new
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# @param running_experiment [Experiment::Running]
|
|
51
|
-
# @param user [User]
|
|
52
|
-
#
|
|
53
|
-
# @return [Decision]
|
|
54
|
-
def decide_running(running_experiment:, user:)
|
|
55
|
-
|
|
56
|
-
overridden_variation = running_experiment.get_overridden_variation(user: user)
|
|
57
|
-
return Decision::ForcedAllocated.new(variation_key: overridden_variation.key) unless overridden_variation.nil?
|
|
58
|
-
|
|
59
|
-
allocated_slot = @bucketer.bucketing(bucket: running_experiment.bucket, user: user)
|
|
60
|
-
return Decision::NotAllocated.new if allocated_slot.nil?
|
|
61
|
-
|
|
62
|
-
allocated_variation = running_experiment.get_variation(variation_id: allocated_slot.variation_id)
|
|
63
|
-
return Decision::NotAllocated.new if allocated_variation.nil?
|
|
64
|
-
return Decision::NotAllocated.new if allocated_variation.dropped
|
|
65
|
-
|
|
66
|
-
Decision::NaturalAllocated.new(variation: allocated_variation)
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Hackle
|
|
4
|
-
class EventDispatcher
|
|
5
|
-
|
|
6
|
-
DEFAULT_DISPATCH_WORKER_SIZE = 2
|
|
7
|
-
DEFAULT_DISPATCH_QUEUE_CAPACITY = 50
|
|
8
|
-
|
|
9
|
-
def initialize(config:, sdk_info:)
|
|
10
|
-
@logger = config.logger
|
|
11
|
-
@client = HTTP.client(base_uri: config.event_uri)
|
|
12
|
-
@headers = HTTP.sdk_headers(sdk_info: sdk_info)
|
|
13
|
-
@dispatcher_executor = Concurrent::ThreadPoolExecutor.new(
|
|
14
|
-
min_threads: DEFAULT_DISPATCH_WORKER_SIZE,
|
|
15
|
-
max_threads: DEFAULT_DISPATCH_WORKER_SIZE,
|
|
16
|
-
max_queue: DEFAULT_DISPATCH_QUEUE_CAPACITY
|
|
17
|
-
)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def dispatch(events:)
|
|
21
|
-
payload = create_payload(events: events)
|
|
22
|
-
begin
|
|
23
|
-
@dispatcher_executor.post { dispatch_payload(payload: payload) }
|
|
24
|
-
rescue Concurrent::RejectedExecutionError
|
|
25
|
-
@logger.warn { 'Dispatcher executor queue is full. Event dispatch rejected' }
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def shutdown
|
|
30
|
-
@dispatcher_executor.shutdown
|
|
31
|
-
unless @dispatcher_executor.wait_for_termination(10)
|
|
32
|
-
@logger.warn { 'Failed to dispatch previously submitted events' }
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
private
|
|
37
|
-
|
|
38
|
-
def dispatch_payload(payload:)
|
|
39
|
-
request = Net::HTTP::Post.new('/api/v1/events', @headers)
|
|
40
|
-
request.content_type = 'application/json'
|
|
41
|
-
request.body = payload.to_json
|
|
42
|
-
|
|
43
|
-
response = @client.request(request)
|
|
44
|
-
|
|
45
|
-
status_code = response.code.to_i
|
|
46
|
-
HTTP.check_successful(status_code: status_code)
|
|
47
|
-
rescue => e
|
|
48
|
-
@logger.error { "Failed to dispatch events: #{e.inspect}" }
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def create_payload(events:)
|
|
52
|
-
exposure_events = []
|
|
53
|
-
track_events = []
|
|
54
|
-
events.each do |event|
|
|
55
|
-
case event
|
|
56
|
-
when UserEvent::Exposure
|
|
57
|
-
exposure_events << create_exposure_event(event)
|
|
58
|
-
when UserEvent::Track
|
|
59
|
-
track_events << create_track_event(event)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
{
|
|
63
|
-
exposureEvents: exposure_events,
|
|
64
|
-
trackEvents: track_events
|
|
65
|
-
}
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
#
|
|
69
|
-
# @param exposure [UserEvent::Exposure]
|
|
70
|
-
#
|
|
71
|
-
def create_exposure_event(exposure)
|
|
72
|
-
{
|
|
73
|
-
timestamp: exposure.timestamp,
|
|
74
|
-
userId: exposure.user.id,
|
|
75
|
-
experimentId: exposure.experiment.id,
|
|
76
|
-
experimentKey: exposure.experiment.key,
|
|
77
|
-
variationId: exposure.variation.id,
|
|
78
|
-
variationKey: exposure.variation.key
|
|
79
|
-
}
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
#
|
|
83
|
-
# @param track [UserEvent::Track]
|
|
84
|
-
#
|
|
85
|
-
def create_track_event(track)
|
|
86
|
-
{
|
|
87
|
-
timestamp: track.timestamp,
|
|
88
|
-
userId: track.user.id,
|
|
89
|
-
eventTypeId: track.event_type.id,
|
|
90
|
-
eventTypeKey: track.event_type.key,
|
|
91
|
-
value: track.event.value,
|
|
92
|
-
properties: track.event.properties
|
|
93
|
-
}
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
end
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Hackle
|
|
4
|
-
|
|
5
|
-
class EventProcessor
|
|
6
|
-
|
|
7
|
-
DEFAULT_FLUSH_INTERVAL = 10
|
|
8
|
-
|
|
9
|
-
# @param config [Config]
|
|
10
|
-
# @param event_dispatcher [EventDispatcher]
|
|
11
|
-
def initialize(config:, event_dispatcher:)
|
|
12
|
-
@logger = config.logger
|
|
13
|
-
@event_dispatcher = event_dispatcher
|
|
14
|
-
@message_processor = MessageProcessor.new(config: config, event_dispatcher: event_dispatcher)
|
|
15
|
-
@flush_task = Concurrent::TimerTask.new(execution_interval: DEFAULT_FLUSH_INTERVAL) { flush }
|
|
16
|
-
@consume_task = nil
|
|
17
|
-
@running = false
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def start!
|
|
21
|
-
return if @running
|
|
22
|
-
|
|
23
|
-
@consume_task = Thread.new { @message_processor.consuming_loop }
|
|
24
|
-
@flush_task.execute
|
|
25
|
-
@running = true
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def stop!
|
|
29
|
-
return unless @running
|
|
30
|
-
|
|
31
|
-
@logger.info { 'Shutting down Hackle event_processor' }
|
|
32
|
-
|
|
33
|
-
@message_processor.produce(message: Message::Shutdown.new, non_block: false)
|
|
34
|
-
@consume_task.join(10)
|
|
35
|
-
@flush_task.shutdown
|
|
36
|
-
@event_dispatcher.shutdown
|
|
37
|
-
|
|
38
|
-
@running = false
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# @param event [UserEvent]
|
|
42
|
-
def process(event:)
|
|
43
|
-
@message_processor.produce(message: Message::Event.new(event))
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def flush
|
|
47
|
-
@message_processor.produce(message: Message::Flush.new)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
class Message
|
|
51
|
-
class Event < Message
|
|
52
|
-
|
|
53
|
-
# @return [UserEvent]
|
|
54
|
-
attr_reader :event
|
|
55
|
-
|
|
56
|
-
# @param event [UserEvent]
|
|
57
|
-
def initialize(event)
|
|
58
|
-
@event = event
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
class Flush < Message
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
class Shutdown < Message
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
class MessageProcessor
|
|
70
|
-
|
|
71
|
-
DEFAULT_MESSAGE_QUEUE_CAPACITY = 1000
|
|
72
|
-
DEFAULT_MAX_EVENT_DISPATCH_SIZE = 500
|
|
73
|
-
|
|
74
|
-
def initialize(config:, event_dispatcher:)
|
|
75
|
-
@logger = config.logger
|
|
76
|
-
@event_dispatcher = event_dispatcher
|
|
77
|
-
@message_queue = SizedQueue.new(DEFAULT_MESSAGE_QUEUE_CAPACITY)
|
|
78
|
-
@random = Random.new
|
|
79
|
-
@consumed_events = []
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# @param message [Message]
|
|
83
|
-
# @param non_block [boolean]
|
|
84
|
-
def produce(message:, non_block: true)
|
|
85
|
-
@message_queue.push(message, non_block)
|
|
86
|
-
rescue ThreadError
|
|
87
|
-
if @random.rand(1..100) == 1 # log only 1% of the time
|
|
88
|
-
@logger.warn { 'Events are produced faster than can be consumed. Some events will be dropped.' }
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def consuming_loop
|
|
93
|
-
loop do
|
|
94
|
-
message = @message_queue.pop
|
|
95
|
-
case message
|
|
96
|
-
when Message::Event
|
|
97
|
-
consume_event(event: message.event)
|
|
98
|
-
when Message::Flush
|
|
99
|
-
dispatch_events
|
|
100
|
-
when Message::Shutdown
|
|
101
|
-
break
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
rescue => e
|
|
105
|
-
@logger.warn { "Uncaught exception in events message processor: #{e.inspect}" }
|
|
106
|
-
ensure
|
|
107
|
-
dispatch_events
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
private
|
|
111
|
-
|
|
112
|
-
# @param event [UserEvent]
|
|
113
|
-
def consume_event(event:)
|
|
114
|
-
@consumed_events << event
|
|
115
|
-
dispatch_events if @consumed_events.length >= DEFAULT_MAX_EVENT_DISPATCH_SIZE
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def dispatch_events
|
|
119
|
-
return if @consumed_events.empty?
|
|
120
|
-
|
|
121
|
-
@event_dispatcher.dispatch(events: @consumed_events)
|
|
122
|
-
@consumed_events = []
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
end
|