devcycle-ruby-server-sdk 3.6.1 → 3.7.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.
@@ -0,0 +1,245 @@
1
+ require 'spec_helper'
2
+
3
+ describe DevCycle::Client do
4
+ let(:test_user) { DevCycle::User.new(user_id: 'test-user', email: 'test@example.com') }
5
+ let(:test_key) { 'test-variable' }
6
+ let(:test_default) { 'default-value' }
7
+ let(:options) { DevCycle::Options.new }
8
+
9
+ # Use unique SDK keys for each test to avoid WASM initialization conflicts
10
+ let(:valid_sdk_key) { "server-test-key-#{SecureRandom.hex(4)}" }
11
+ let(:client) { DevCycle::Client.new(valid_sdk_key, options) }
12
+
13
+ after(:each) do
14
+ client.close if client.respond_to?(:close)
15
+ end
16
+
17
+ describe 'eval hooks functionality' do
18
+ context 'hook management' do
19
+ it 'initializes with an empty eval hooks runner' do
20
+ expect(client.instance_variable_get(:@eval_hooks_runner)).to be_a(DevCycle::EvalHooksRunner)
21
+ expect(client.instance_variable_get(:@eval_hooks_runner).eval_hooks).to be_empty
22
+ end
23
+
24
+ it 'can add eval hooks' do
25
+ hook = DevCycle::EvalHook.new
26
+ client.add_eval_hook(hook)
27
+ expect(client.instance_variable_get(:@eval_hooks_runner).eval_hooks).to include(hook)
28
+ end
29
+
30
+ it 'can clear eval hooks' do
31
+ hook = DevCycle::EvalHook.new
32
+ client.add_eval_hook(hook)
33
+ expect(client.instance_variable_get(:@eval_hooks_runner).eval_hooks).not_to be_empty
34
+
35
+ client.clear_eval_hooks
36
+ expect(client.instance_variable_get(:@eval_hooks_runner).eval_hooks).to be_empty
37
+ end
38
+ end
39
+
40
+ context 'variable evaluation with hooks' do
41
+ it 'runs before hooks before variable evaluation' do
42
+ before_hook_called = false
43
+ hook = DevCycle::EvalHook.new(
44
+ before: ->(context) {
45
+ before_hook_called = true
46
+ expect(context.key).to eq(test_key)
47
+ expect(context.user).to eq(test_user)
48
+ expect(context.default_value).to eq(test_default)
49
+ context
50
+ }
51
+ )
52
+ client.add_eval_hook(hook)
53
+
54
+ result = client.variable(test_user, test_key, test_default)
55
+ expect(before_hook_called).to be true
56
+ expect(result.isDefaulted).to be true
57
+ expect(result.value).to eq(test_default)
58
+ end
59
+
60
+ it 'runs after hooks after successful variable evaluation' do
61
+ after_hook_called = false
62
+ hook = DevCycle::EvalHook.new(
63
+ after: ->(context) {
64
+ after_hook_called = true
65
+ expect(context.key).to eq(test_key)
66
+ expect(context.user).to eq(test_user)
67
+ expect(context.default_value).to eq(test_default)
68
+ }
69
+ )
70
+ client.add_eval_hook(hook)
71
+
72
+ result = client.variable(test_user, test_key, test_default)
73
+ expect(after_hook_called).to be true
74
+ expect(result.isDefaulted).to be true
75
+ expect(result.value).to eq(test_default)
76
+ end
77
+
78
+ it 'runs error hooks when variable evaluation fails' do
79
+ error_hook_called = false
80
+ hook = DevCycle::EvalHook.new(
81
+ error: ->(context, error) {
82
+ error_hook_called = true
83
+ expect(context.key).to eq(test_key)
84
+ expect(context.user).to eq(test_user)
85
+ expect(context.default_value).to eq(test_default)
86
+ }
87
+ )
88
+ client.add_eval_hook(hook)
89
+
90
+ # Force an error by making determine_variable_type raise an error
91
+ allow(client).to receive(:determine_variable_type).and_raise(StandardError, 'Variable type error')
92
+
93
+ client.variable(test_user, test_key, test_default)
94
+ expect(error_hook_called).to be true
95
+ end
96
+
97
+ it 'runs finally hooks regardless of success or failure' do
98
+ finally_hook_called = false
99
+ hook = DevCycle::EvalHook.new(
100
+ on_finally: ->(context) {
101
+ finally_hook_called = true
102
+ expect(context.key).to eq(test_key)
103
+ expect(context.user).to eq(test_user)
104
+ expect(context.default_value).to eq(test_default)
105
+ }
106
+ )
107
+ client.add_eval_hook(hook)
108
+
109
+ result = client.variable(test_user, test_key, test_default)
110
+ expect(finally_hook_called).to be true
111
+ expect(result.isDefaulted).to be true
112
+ expect(result.value).to eq(test_default)
113
+ end
114
+
115
+ it 'skips after hooks when before hook raises an error' do
116
+ before_hook_called = false
117
+ after_hook_called = false
118
+ error_hook_called = false
119
+ finally_hook_called = false
120
+
121
+ hook = DevCycle::EvalHook.new(
122
+ before: ->(context) {
123
+ before_hook_called = true
124
+ raise StandardError, 'Before hook error'
125
+ },
126
+ after: ->(context) {
127
+ after_hook_called = true
128
+ },
129
+ error: ->(context, error) {
130
+ error_hook_called = true
131
+ expect(error).to be_a(StandardError)
132
+ expect(error.message).to include('Before hook error')
133
+ },
134
+ on_finally: ->(context) {
135
+ finally_hook_called = true
136
+ }
137
+ )
138
+ client.add_eval_hook(hook)
139
+
140
+ client.variable(test_user, test_key, test_default)
141
+ expect(before_hook_called).to be true
142
+ expect(after_hook_called).to be false
143
+ expect(error_hook_called).to be true
144
+ expect(finally_hook_called).to be true
145
+ end
146
+
147
+ it 'runs multiple hooks in order' do
148
+ execution_order = []
149
+
150
+ hook1 = DevCycle::EvalHook.new(
151
+ before: ->(context) {
152
+ execution_order << 'hook1_before'
153
+ context
154
+ },
155
+ after: ->(context) {
156
+ execution_order << 'hook1_after'
157
+ },
158
+ on_finally: ->(context) {
159
+ execution_order << 'hook1_finally'
160
+ }
161
+ )
162
+
163
+ hook2 = DevCycle::EvalHook.new(
164
+ before: ->(context) {
165
+ execution_order << 'hook2_before'
166
+ context
167
+ },
168
+ after: ->(context) {
169
+ execution_order << 'hook2_after'
170
+ },
171
+ on_finally: ->(context) {
172
+ execution_order << 'hook2_finally'
173
+ }
174
+ )
175
+
176
+ client.add_eval_hook(hook1)
177
+ client.add_eval_hook(hook2)
178
+
179
+ result = client.variable(test_user, test_key, test_default)
180
+
181
+ expect(execution_order).to eq([
182
+ 'hook1_before', 'hook2_before',
183
+ 'hook1_after', 'hook2_after',
184
+ 'hook1_finally', 'hook2_finally'
185
+ ])
186
+ expect(result.isDefaulted).to be true
187
+ expect(result.value).to eq(test_default)
188
+ end
189
+
190
+ it 'allows before hooks to modify context' do
191
+ modified_context = nil
192
+ hook = DevCycle::EvalHook.new(
193
+ before: ->(context) {
194
+ # Modify the context
195
+ context.key = 'modified-key'
196
+ context.user = DevCycle::User.new(user_id: 'modified-user', email: 'modified@example.com')
197
+ context
198
+ },
199
+ after: ->(context) {
200
+ modified_context = context
201
+ }
202
+ )
203
+ client.add_eval_hook(hook)
204
+
205
+ result = client.variable(test_user, test_key, test_default)
206
+
207
+ expect(modified_context.key).to eq('modified-key')
208
+ expect(modified_context.user).to eq(DevCycle::User.new(user_id: 'modified-user', email: 'modified@example.com'))
209
+ expect(result.isDefaulted).to be true
210
+ expect(result.value).to eq(test_default)
211
+ end
212
+
213
+ it 'works with different variable types' do
214
+ # Test with boolean default
215
+ boolean_hook_called = false
216
+ boolean_hook = DevCycle::EvalHook.new(
217
+ after: ->(context) {
218
+ boolean_hook_called = true
219
+ }
220
+ )
221
+ client.add_eval_hook(boolean_hook)
222
+
223
+ boolean_result = client.variable(test_user, 'boolean-test', true)
224
+ expect(boolean_hook_called).to be true
225
+ expect(boolean_result.isDefaulted).to be true
226
+ expect(boolean_result.value).to eq(true)
227
+
228
+ # Test with number default
229
+ number_hook_called = false
230
+ number_hook = DevCycle::EvalHook.new(
231
+ after: ->(context) {
232
+ number_hook_called = true
233
+ }
234
+ )
235
+ client.add_eval_hook(number_hook)
236
+
237
+ number_result = client.variable(test_user, 'number-test', 42)
238
+ expect(number_hook_called).to be true
239
+ expect(number_result.isDefaulted).to be true
240
+ expect(number_result.value).to eq(42)
241
+ end
242
+
243
+ end
244
+ end
245
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devcycle-ruby-server-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.1
4
+ version: 3.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - DevCycleHQ
@@ -165,6 +165,8 @@ files:
165
165
  - lib/devcycle-ruby-server-sdk/api_client.rb
166
166
  - lib/devcycle-ruby-server-sdk/api_error.rb
167
167
  - lib/devcycle-ruby-server-sdk/configuration.rb
168
+ - lib/devcycle-ruby-server-sdk/eval_hooks_runner.rb
169
+ - lib/devcycle-ruby-server-sdk/eval_reasons.rb
168
170
  - lib/devcycle-ruby-server-sdk/localbucketing/bucketed_user_config.rb
169
171
  - lib/devcycle-ruby-server-sdk/localbucketing/bucketing-lib.release.wasm
170
172
  - lib/devcycle-ruby-server-sdk/localbucketing/config_manager.rb
@@ -179,6 +181,8 @@ files:
179
181
  - lib/devcycle-ruby-server-sdk/localbucketing/proto/variableForUserParams_pb.rb
180
182
  - lib/devcycle-ruby-server-sdk/localbucketing/update_wasm.sh
181
183
  - lib/devcycle-ruby-server-sdk/models/error_response.rb
184
+ - lib/devcycle-ruby-server-sdk/models/eval_hook.rb
185
+ - lib/devcycle-ruby-server-sdk/models/eval_hook_context.rb
182
186
  - lib/devcycle-ruby-server-sdk/models/event.rb
183
187
  - lib/devcycle-ruby-server-sdk/models/feature.rb
184
188
  - lib/devcycle-ruby-server-sdk/models/inline_response201.rb
@@ -190,6 +194,8 @@ files:
190
194
  - spec/api_client_spec.rb
191
195
  - spec/configuration_spec.rb
192
196
  - spec/devcycle_provider_spec.rb
197
+ - spec/eval_hooks_runner_spec.rb
198
+ - spec/eval_hooks_spec.rb
193
199
  - spec/spec_helper.rb
194
200
  homepage: https://devcycle.com
195
201
  licenses:
@@ -209,7 +215,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
215
  - !ruby/object:Gem::Version
210
216
  version: '0'
211
217
  requirements: []
212
- rubygems_version: 3.6.7
218
+ rubygems_version: 3.6.9
213
219
  specification_version: 4
214
220
  summary: DevCycle Bucketing API Ruby Gem
215
221
  test_files:
@@ -217,4 +223,6 @@ test_files:
217
223
  - spec/api_client_spec.rb
218
224
  - spec/configuration_spec.rb
219
225
  - spec/devcycle_provider_spec.rb
226
+ - spec/eval_hooks_runner_spec.rb
227
+ - spec/eval_hooks_spec.rb
220
228
  - spec/spec_helper.rb