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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b68b0885c55e6e3f724d839500536a7022f6ec40d691b291125aa9e3b2d09669
4
- data.tar.gz: c45af7b53ca8e12b7b6d4d8f109d2f3b8c4ff67471736e952c7d8eed0aa32a08
3
+ metadata.gz: c3acf8324fc7606185240c3ea421048f87c8618a3944951ed71ed729ed4cde7a
4
+ data.tar.gz: 763e2632030ffbc90bd365614f158bc960c767e15bcf0d88afd2a2407ff56f24
5
5
  SHA512:
6
- metadata.gz: a906bf6a2b314c043b765dbffacd7bd08975b7c69e89ec28a72abbb312a091c4c2a9fa4615076ffe8c400ef4f8d539fe4977fbe31247866d728ca2f33dc88eff
7
- data.tar.gz: b7ba08571dba965a3af92c8188b52ce06547e298afff943387246e67565daf8070daf5fe67552c629086bfb563b4a6856d98b31b21b949792011aa580b3e1ed9
6
+ metadata.gz: 7830aef854188cded6ed20a3667d4fe6dbf2bcd4482e605b823c95023b7d9fb3f30a52d01543346adae4ed260290f25b71cd516a3172679e4209d54d342eac6b
7
+ data.tar.gz: be2f2cb6349a64b95216dd3868642284d917ea4c8db7d2991f550f9beb9e0ddb3f8dae91dc6aa5c817fbf0f2b5f562da1f95fcccee0bddceea67323b96ec783c
@@ -14,6 +14,7 @@ module DevCycle
14
14
  @sdkKey = sdkKey
15
15
  @dvc_options = dvc_options
16
16
  @logger = dvc_options.logger
17
+ @eval_hooks_runner = EvalHooksRunner.new
17
18
 
18
19
  if @dvc_options.enable_cloud_bucketing
19
20
  @api_client = ApiClient.default
@@ -172,35 +173,72 @@ module DevCycle
172
173
 
173
174
  validate_model(user)
174
175
 
175
- if @dvc_options.enable_cloud_bucketing
176
- data, _status_code, _headers = variable_with_http_info(key, user, default, opts)
177
- return data
176
+ # Create hook context
177
+ hook_context = HookContext.new(key: key, user: user, default_value: default)
178
+
179
+ before_hook_error = nil
180
+ # Run before hooks
181
+ begin
182
+ hook_context = @eval_hooks_runner.run_before_hooks(hook_context)
183
+ rescue BeforeHookError => e
184
+ before_hook_error = e
185
+ @logger.warn("Error in before hooks: #{e.message}")
178
186
  end
179
187
 
180
- value = default
181
- type = determine_variable_type(default)
182
- defaulted = true
183
- if local_bucketing_initialized? && @local_bucketing.has_config
184
- type_code = variable_type_code_from_type(type)
185
- variable_pb = variable_for_user_pb(user, key, type_code)
186
- unless variable_pb.nil?
187
- value = get_variable_value(variable_pb)
188
- defaulted = false
188
+ variable_result = nil
189
+
190
+ begin
191
+ if @dvc_options.enable_cloud_bucketing
192
+ data, _status_code, _headers = variable_with_http_info(key, user, default, opts)
193
+ variable_result = data
194
+ else
195
+ value = default
196
+ type = determine_variable_type(default)
197
+ defaulted = true
198
+ eval = { reason: DevCycle::EVAL_REASONS::DEFAULT, details: DevCycle::DEFAULT_REASON_DETAILS::USER_NOT_TARGETED }
199
+ if local_bucketing_initialized? && @local_bucketing.has_config
200
+ type_code = variable_type_code_from_type(type)
201
+ variable_pb = variable_for_user_pb(user, key, type_code)
202
+ unless variable_pb.nil?
203
+ value = get_variable_value(variable_pb)
204
+ defaulted = false
205
+ end
206
+ eval = get_eval_reason(variable_pb)
207
+ else
208
+ eval = { reason: DevCycle::EVAL_REASONS::DEFAULT, details: DevCycle::DEFAULT_REASON_DETAILS::MISSING_CONFIG }
209
+ @logger.warn("Local bucketing not initialized, returning default value for variable #{key}")
210
+ variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_defaulted], target: key, metaData: { evalReason: DevCycle::EVAL_REASONS::DEFAULT }})
211
+ bucketed_config = BucketedUserConfig.new({}, {}, {}, {}, {}, {}, [])
212
+ @event_queue.queue_aggregate_event(variable_event, bucketed_config)
213
+ end
214
+
215
+ variable_result = Variable.new({
216
+ key: key,
217
+ value: value,
218
+ type: type,
219
+ defaultValue: default,
220
+ isDefaulted: defaulted,
221
+ eval: eval
222
+ })
189
223
  end
190
- else
191
- @logger.warn("Local bucketing not initialized, returning default value for variable #{key}")
192
- variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_defaulted], target: key })
193
- bucketed_config = BucketedUserConfig.new({}, {}, {}, {}, {}, {}, [])
194
- @event_queue.queue_aggregate_event(variable_event, bucketed_config)
195
- end
196
-
197
- Variable.new({
198
- key: key,
199
- value: value,
200
- type: type,
201
- defaultValue: default,
202
- isDefaulted: defaulted
203
- })
224
+
225
+
226
+ # Run after hooks only if no before hook error occurred
227
+ if before_hook_error != nil
228
+ @logger.info("before_hook_error is not nil, skipping after hooks")
229
+ raise before_hook_error
230
+ else
231
+ @eval_hooks_runner.run_after_hooks(hook_context)
232
+ end
233
+ rescue => e
234
+ # Run error hooks
235
+ @eval_hooks_runner.run_error_hooks(hook_context, e)
236
+ ensure
237
+ # Run finally hooks in all cases
238
+ @eval_hooks_runner.run_finally_hooks(hook_context)
239
+ end
240
+
241
+ variable_result
204
242
  end
205
243
 
206
244
  def variable_for_user(user, key, variable_type_code)
@@ -526,6 +564,44 @@ module DevCycle
526
564
  raise ArgumentError.new("Invalid type code for variable: #{type_code}")
527
565
  end
528
566
  end
567
+
568
+ def get_variable_value(variable_pb)
569
+ case variable_pb.type
570
+ when :Boolean
571
+ variable_pb.boolValue
572
+ when :Number
573
+ variable_pb.doubleValue
574
+ when :String
575
+ variable_pb.stringValue
576
+ when :JSON
577
+ JSON.parse variable_pb.stringValue
578
+ end
579
+ end
580
+
581
+ def get_eval_reason(variable_pb)
582
+ if variable_pb.nil?
583
+ { reason: DevCycle::EVAL_REASONS::DEFAULT, details: DevCycle::DEFAULT_REASON_DETAILS::USER_NOT_TARGETED}
584
+ else
585
+ if variable_pb.eval.nil?
586
+ { reason: DevCycle::EVAL_REASONS::DEFAULT, details: DevCycle::DEFAULT_REASON_DETAILS::USER_NOT_TARGETED }
587
+ else
588
+ { reason: variable_pb.eval.reason, details: variable_pb.eval.details, target_id: variable_pb.eval.target_id }
589
+ end
590
+ end
591
+ end
592
+
593
+ # Adds an eval hook to the client
594
+ # @param [EvalHook] eval_hook The eval hook to add
595
+ # @return [void]
596
+ def add_eval_hook(eval_hook)
597
+ @eval_hooks_runner.add_hook(eval_hook)
598
+ end
599
+
600
+ # Clears all eval hooks from the client
601
+ # @return [void]
602
+ def clear_eval_hooks
603
+ @eval_hooks_runner.clear_hooks
604
+ end
529
605
  end
530
606
 
531
607
  # @deprecated Use `DevCycle::Client` instead.
@@ -48,15 +48,42 @@ module DevCycle
48
48
  raise ArgumentError, "Invalid context type, expected OpenFeature::SDK::EvaluationContext but got #{context.class}"
49
49
  end
50
50
  args = {}
51
- if context.field('user_id')
52
- args.merge!(user_id: context.field('user_id'))
53
- elsif context.field('targeting_key')
54
- args.merge!(user_id: context.field('targeting_key'))
51
+ user_id = nil
52
+ user_id_field = nil
53
+
54
+ # Priority order: targeting_key -> user_id -> userId
55
+ if context.field('targeting_key')
56
+ user_id = context.field('targeting_key')
57
+ user_id_field = 'targeting_key'
58
+ elsif context.field('user_id')
59
+ user_id = context.field('user_id')
60
+ user_id_field = 'user_id'
61
+ elsif context.field('userId')
62
+ user_id = context.field('userId')
63
+ user_id_field = 'userId'
55
64
  end
65
+
66
+ # Validate user_id is present and is a string
67
+ if user_id.nil?
68
+ raise ArgumentError, "User ID is required. Must provide one of: targeting_key, user_id, or userId"
69
+ end
70
+
71
+ unless user_id.is_a?(String)
72
+ raise ArgumentError, "User ID field '#{user_id_field}' must be a string, got #{user_id.class}"
73
+ end
74
+
75
+ # Check after type validation to avoid NoMethodError on non-strings
76
+ if user_id.empty?
77
+ raise ArgumentError, "User ID is required. Must provide one of: targeting_key, user_id, or userId"
78
+ end
79
+
80
+ args.merge!(user_id: user_id)
81
+
56
82
  customData = {}
57
83
  privateCustomData = {}
58
84
  context.fields.each do |field, value|
59
- if field === 'user_id' || field === 'targeting_key'
85
+ # Skip all user ID fields from custom data
86
+ if field === 'targeting_key' || field === 'user_id' || field === 'userId'
60
87
  next
61
88
  end
62
89
  if !(field === 'privateCustomData' || field === 'customData') && value.is_a?(Hash)
@@ -0,0 +1,135 @@
1
+ require 'devcycle-ruby-server-sdk/models/eval_hook'
2
+ require 'devcycle-ruby-server-sdk/models/eval_hook_context'
3
+
4
+ module DevCycle
5
+ # Custom error raised when a before hook fails
6
+ class BeforeHookError < StandardError
7
+ attr_reader :original_error, :hook_context
8
+
9
+ def initialize(message = nil, original_error = nil, hook_context = nil)
10
+ super(message || "Before hook execution failed")
11
+ @original_error = original_error
12
+ @hook_context = hook_context
13
+ end
14
+
15
+ def to_s
16
+ msg = super
17
+ msg += "\nOriginal error: #{@original_error.message}" if @original_error
18
+ msg
19
+ end
20
+ end
21
+
22
+ # Custom error raised when an after hook fails
23
+ class AfterHookError < StandardError
24
+ attr_reader :original_error, :hook_context
25
+
26
+ def initialize(message = nil, original_error = nil, hook_context = nil)
27
+ super(message || "After hook execution failed")
28
+ @original_error = original_error
29
+ @hook_context = hook_context
30
+ end
31
+
32
+ def to_s
33
+ msg = super
34
+ msg += "\nOriginal error: #{@original_error.message}" if @original_error
35
+ msg
36
+ end
37
+ end
38
+
39
+ class EvalHooksRunner
40
+ # @return [Array<EvalHook>] Array of eval hooks to run
41
+ attr_reader :eval_hooks
42
+
43
+ # Initializes the EvalHooksRunner with an optional array of eval hooks
44
+ # @param [Array<EvalHook>, nil] eval_hooks Array of eval hooks to run
45
+ def initialize(eval_hooks = [])
46
+ @eval_hooks = eval_hooks || []
47
+ end
48
+
49
+ # Runs all before hooks with the given context
50
+ # @param [HookContext] context The context to pass to the hooks
51
+ # @return [HookContext] The potentially modified context
52
+ # @raise [BeforeHookError] when a before hook fails
53
+ def run_before_hooks(context)
54
+ current_context = context
55
+
56
+ @eval_hooks.each do |hook|
57
+ next unless hook.before
58
+
59
+ begin
60
+ result = hook.before.call(current_context)
61
+ # If the hook returns a new context, use it for subsequent hooks
62
+ current_context = result if result.is_a?(DevCycle::HookContext)
63
+ rescue => e
64
+ # Raise BeforeHookError to allow client to handle and skip after hooks
65
+ raise BeforeHookError.new(e.message, e, current_context)
66
+ end
67
+ end
68
+
69
+ current_context
70
+ end
71
+
72
+ # Runs all after hooks with the given context
73
+ # @param [HookContext] context The context to pass to the hooks
74
+ # @return [void]
75
+ # @raise [AfterHookError] when an after hook fails
76
+ def run_after_hooks(context)
77
+ @eval_hooks.each do |hook|
78
+ next unless hook.after
79
+
80
+ begin
81
+ hook.after.call(context)
82
+ rescue => e
83
+ # Log error but continue with next hook
84
+ raise AfterHookError.new(e.message, e, context)
85
+ end
86
+ end
87
+ end
88
+
89
+ # Runs all finally hooks with the given context
90
+ # @param [HookContext] context The context to pass to the hooks
91
+ # @return [void]
92
+ def run_finally_hooks(context)
93
+ @eval_hooks.each do |hook|
94
+ next unless hook.on_finally
95
+
96
+ begin
97
+ hook.on_finally.call(context)
98
+ rescue => e
99
+ # Log error but don't re-raise to prevent blocking evaluation
100
+ warn "Error in finally hook: #{e.message}"
101
+ end
102
+ end
103
+ end
104
+
105
+ # Runs all error hooks with the given context and error
106
+ # @param [HookContext] context The context to pass to the hooks
107
+ # @param [Exception] error The error that occurred
108
+ # @return [void]
109
+ def run_error_hooks(context, error)
110
+ @eval_hooks.each do |hook|
111
+ next unless hook.error
112
+
113
+ begin
114
+ hook.error.call(context, error)
115
+ rescue => e
116
+ # Log error but don't re-raise to prevent blocking evaluation
117
+ warn "Error in error hook: #{e.message}"
118
+ end
119
+ end
120
+ end
121
+
122
+ # Adds an eval hook to the runner
123
+ # @param [EvalHook] eval_hook The eval hook to add
124
+ # @return [void]
125
+ def add_hook(eval_hook)
126
+ @eval_hooks << eval_hook
127
+ end
128
+
129
+ # Clears all eval hooks from the runner
130
+ # @return [void]
131
+ def clear_hooks
132
+ @eval_hooks.clear
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,13 @@
1
+ module DevCycle
2
+ # Evaluation reasons for successful evaluations
3
+ module EVAL_REASONS
4
+ DEFAULT = 'DEFAULT'
5
+ ERROR = 'ERROR'
6
+ end
7
+
8
+ # Default reason details
9
+ module DEFAULT_REASON_DETAILS
10
+ MISSING_CONFIG = 'Missing Config'
11
+ USER_NOT_TARGETED = 'User Not Targeted'
12
+ end
13
+ end
@@ -66,4 +66,12 @@ message SDKVariable_PB {
66
66
  double doubleValue = 5;
67
67
  string stringValue = 6;
68
68
  NullableString evalReason = 7;
69
+ NullableString _feature = 8;
70
+ EvalReason_PB eval = 9;
69
71
  }
72
+
73
+ message EvalReason_PB {
74
+ string reason = 1;
75
+ string details = 2;
76
+ string target_id = 3;
77
+ }
@@ -1,69 +1,34 @@
1
+ # frozen_string_literal: true
1
2
  # Generated by the protocol buffer compiler. DO NOT EDIT!
2
3
  # source: variableForUserParams.proto
3
4
 
4
5
  require 'google/protobuf'
5
6
 
6
- Google::Protobuf::DescriptorPool.generated_pool.build do
7
- add_file("variableForUserParams.proto", :syntax => :proto3) do
8
- add_message "proto.NullableString" do
9
- optional :value, :string, 1
10
- optional :isNull, :bool, 2
11
- end
12
- add_message "proto.NullableDouble" do
13
- optional :value, :double, 1
14
- optional :isNull, :bool, 2
15
- end
16
- add_message "proto.CustomDataValue" do
17
- optional :type, :enum, 1, "proto.CustomDataType"
18
- optional :boolValue, :bool, 2
19
- optional :doubleValue, :double, 3
20
- optional :stringValue, :string, 4
21
- end
22
- add_message "proto.NullableCustomData" do
23
- map :value, :string, :message, 1, "proto.CustomDataValue"
24
- optional :isNull, :bool, 2
25
- end
26
- add_message "proto.VariableForUserParams_PB" do
27
- optional :sdkKey, :string, 1
28
- optional :variableKey, :string, 2
29
- optional :variableType, :enum, 3, "proto.VariableType_PB"
30
- optional :user, :message, 4, "proto.DVCUser_PB"
31
- optional :shouldTrackEvent, :bool, 5
32
- end
33
- add_message "proto.DVCUser_PB" do
34
- optional :user_id, :string, 1
35
- optional :email, :message, 2, "proto.NullableString"
36
- optional :name, :message, 3, "proto.NullableString"
37
- optional :language, :message, 4, "proto.NullableString"
38
- optional :country, :message, 5, "proto.NullableString"
39
- optional :appBuild, :message, 6, "proto.NullableDouble"
40
- optional :appVersion, :message, 7, "proto.NullableString"
41
- optional :deviceModel, :message, 8, "proto.NullableString"
42
- optional :customData, :message, 9, "proto.NullableCustomData"
43
- optional :privateCustomData, :message, 10, "proto.NullableCustomData"
44
- end
45
- add_message "proto.SDKVariable_PB" do
46
- optional :_id, :string, 1
47
- optional :type, :enum, 2, "proto.VariableType_PB"
48
- optional :key, :string, 3
49
- optional :boolValue, :bool, 4
50
- optional :doubleValue, :double, 5
51
- optional :stringValue, :string, 6
52
- optional :evalReason, :message, 7, "proto.NullableString"
53
- end
54
- add_enum "proto.VariableType_PB" do
55
- value :Boolean, 0
56
- value :Number, 1
57
- value :String, 2
58
- value :JSON, 3
59
- end
60
- add_enum "proto.CustomDataType" do
61
- value :Bool, 0
62
- value :Num, 1
63
- value :Str, 2
64
- value :Null, 3
7
+
8
+ descriptor_data = "\n\x1bvariableForUserParams.proto\x12\x05proto\"/\n\x0eNullableString\x12\r\n\x05value\x18\x01 \x01(\t\x12\x0e\n\x06isNull\x18\x02 \x01(\x08\"/\n\x0eNullableDouble\x12\r\n\x05value\x18\x01 \x01(\x01\x12\x0e\n\x06isNull\x18\x02 \x01(\x08\"s\n\x0f\x43ustomDataValue\x12#\n\x04type\x18\x01 \x01(\x0e\x32\x15.proto.CustomDataType\x12\x11\n\tboolValue\x18\x02 \x01(\x08\x12\x13\n\x0b\x64oubleValue\x18\x03 \x01(\x01\x12\x13\n\x0bstringValue\x18\x04 \x01(\t\"\x9f\x01\n\x12NullableCustomData\x12\x33\n\x05value\x18\x01 \x03(\x0b\x32$.proto.NullableCustomData.ValueEntry\x12\x0e\n\x06isNull\x18\x02 \x01(\x08\x1a\x44\n\nValueEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12%\n\x05value\x18\x02 \x01(\x0b\x32\x16.proto.CustomDataValue:\x02\x38\x01\"\xa8\x01\n\x18VariableForUserParams_PB\x12\x0e\n\x06sdkKey\x18\x01 \x01(\t\x12\x13\n\x0bvariableKey\x18\x02 \x01(\t\x12,\n\x0cvariableType\x18\x03 \x01(\x0e\x32\x16.proto.VariableType_PB\x12\x1f\n\x04user\x18\x04 \x01(\x0b\x32\x11.proto.DVCUser_PB\x12\x18\n\x10shouldTrackEvent\x18\x05 \x01(\x08\"\x9e\x03\n\nDVCUser_PB\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12$\n\x05\x65mail\x18\x02 \x01(\x0b\x32\x15.proto.NullableString\x12#\n\x04name\x18\x03 \x01(\x0b\x32\x15.proto.NullableString\x12\'\n\x08language\x18\x04 \x01(\x0b\x32\x15.proto.NullableString\x12&\n\x07\x63ountry\x18\x05 \x01(\x0b\x32\x15.proto.NullableString\x12\'\n\x08\x61ppBuild\x18\x06 \x01(\x0b\x32\x15.proto.NullableDouble\x12)\n\nappVersion\x18\x07 \x01(\x0b\x32\x15.proto.NullableString\x12*\n\x0b\x64\x65viceModel\x18\x08 \x01(\x0b\x32\x15.proto.NullableString\x12-\n\ncustomData\x18\t \x01(\x0b\x32\x19.proto.NullableCustomData\x12\x34\n\x11privateCustomData\x18\n \x01(\x0b\x32\x19.proto.NullableCustomData\"\x85\x02\n\x0eSDKVariable_PB\x12\x0b\n\x03_id\x18\x01 \x01(\t\x12$\n\x04type\x18\x02 \x01(\x0e\x32\x16.proto.VariableType_PB\x12\x0b\n\x03key\x18\x03 \x01(\t\x12\x11\n\tboolValue\x18\x04 \x01(\x08\x12\x13\n\x0b\x64oubleValue\x18\x05 \x01(\x01\x12\x13\n\x0bstringValue\x18\x06 \x01(\t\x12)\n\nevalReason\x18\x07 \x01(\x0b\x32\x15.proto.NullableString\x12\'\n\x08_feature\x18\x08 \x01(\x0b\x32\x15.proto.NullableString\x12\"\n\x04\x65val\x18\t \x01(\x0b\x32\x14.proto.EvalReason_PB\"C\n\rEvalReason_PB\x12\x0e\n\x06reason\x18\x01 \x01(\t\x12\x0f\n\x07\x64\x65tails\x18\x02 \x01(\t\x12\x11\n\ttarget_id\x18\x03 \x01(\t*@\n\x0fVariableType_PB\x12\x0b\n\x07\x42oolean\x10\x00\x12\n\n\x06Number\x10\x01\x12\n\n\x06String\x10\x02\x12\x08\n\x04JSON\x10\x03*6\n\x0e\x43ustomDataType\x12\x08\n\x04\x42ool\x10\x00\x12\x07\n\x03Num\x10\x01\x12\x07\n\x03Str\x10\x02\x12\x08\n\x04Null\x10\x03\x62\x06proto3"
9
+
10
+ pool = Google::Protobuf::DescriptorPool.generated_pool
11
+
12
+ begin
13
+ pool.add_serialized_file(descriptor_data)
14
+ rescue TypeError => e
15
+ # Compatibility code: will be removed in the next major version.
16
+ require 'google/protobuf/descriptor_pb'
17
+ parsed = Google::Protobuf::FileDescriptorProto.decode(descriptor_data)
18
+ parsed.clear_dependency
19
+ serialized = parsed.class.encode(parsed)
20
+ file = pool.add_serialized_file(serialized)
21
+ warn "Warning: Protobuf detected an import path issue while loading generated file #{__FILE__}"
22
+ imports = [
23
+ ]
24
+ imports.each do |type_name, expected_filename|
25
+ import_file = pool.lookup(type_name).file_descriptor
26
+ if import_file.name != expected_filename
27
+ warn "- #{file.name} imports #{expected_filename}, but that import was loaded as #{import_file.name}"
65
28
  end
66
29
  end
30
+ warn "Each proto file must use a consistent fully-qualified name."
31
+ warn "This will become an error in the next major version."
67
32
  end
68
33
 
69
34
  module Proto
@@ -74,6 +39,7 @@ module Proto
74
39
  VariableForUserParams_PB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("proto.VariableForUserParams_PB").msgclass
75
40
  DVCUser_PB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("proto.DVCUser_PB").msgclass
76
41
  SDKVariable_PB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("proto.SDKVariable_PB").msgclass
42
+ EvalReason_PB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("proto.EvalReason_PB").msgclass
77
43
  VariableType_PB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("proto.VariableType_PB").enummodule
78
44
  CustomDataType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("proto.CustomDataType").enummodule
79
45
  end
@@ -1,5 +1,5 @@
1
1
  #!/bin/bash
2
- BUCKETING_LIB_VERSION="1.35.1"
2
+ BUCKETING_LIB_VERSION="1.41.0"
3
3
  WAT_DOWNLOAD=0
4
4
  rm bucketing-lib.release.wasm
5
- wget "https://unpkg.com/@devcycle/bucketing-assembly-script@$BUCKETING_LIB_VERSION/build/bucketing-lib.release.wasm"
5
+ wget "https://unpkg.com/@devcycle/bucketing-assembly-script@$BUCKETING_LIB_VERSION/build/bucketing-lib.release.wasm"
@@ -0,0 +1,28 @@
1
+ module DevCycle
2
+ class EvalHook
3
+ # Callback to be executed before evaluation
4
+ attr_accessor :before
5
+
6
+ # Callback to be executed after evaluation
7
+ attr_accessor :after
8
+
9
+ # Callback to be executed finally (always runs)
10
+ attr_accessor :on_finally
11
+
12
+ # Callback to be executed on error
13
+ attr_accessor :error
14
+
15
+ # Initializes the object with optional callback functions
16
+ # @param [Hash] callbacks Callback functions in the form of hash
17
+ # @option callbacks [Proc, nil] :before Callback to execute before evaluation
18
+ # @option callbacks [Proc, nil] :after Callback to execute after evaluation
19
+ # @option callbacks [Proc, nil] :on_finally Callback to execute finally (always runs)
20
+ # @option callbacks [Proc, nil] :error Callback to execute on error
21
+ def initialize(callbacks = {})
22
+ @before = callbacks[:before]
23
+ @after = callbacks[:after]
24
+ @on_finally = callbacks[:on_finally]
25
+ @error = callbacks[:error]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ module DevCycle
2
+ class HookContext
3
+ # The key of the variable being evaluated
4
+ attr_accessor :key
5
+
6
+ # The user for whom the variable is being evaluated
7
+ attr_accessor :user
8
+
9
+ # The default value for the variable
10
+ attr_accessor :default_value
11
+
12
+ # Initializes the object
13
+ # @param [String] key The key of the variable being evaluated
14
+ # @param [DevCycle::User] user The user for whom the variable is being evaluated
15
+ # @param [Object] default_value The default value for the variable
16
+ def initialize(key:, user:, default_value:)
17
+ @key = key
18
+ @user = user
19
+ @default_value = default_value
20
+ end
21
+ end
22
+ end
@@ -29,6 +29,9 @@ module DevCycle
29
29
 
30
30
  attr_accessor :defaultValue
31
31
 
32
+ # Variable evaluation details
33
+ attr_accessor :eval
34
+
32
35
  class EnumAttributeValidator
33
36
  attr_reader :datatype
34
37
  attr_reader :allowable_values
@@ -58,7 +61,8 @@ module DevCycle
58
61
  :'type' => :'type',
59
62
  :'value' => :'value',
60
63
  :'defaultValue' => :'defaultValue',
61
- :'isDefaulted' => :'isDefaulted'
64
+ :'isDefaulted' => :'isDefaulted',
65
+ :'eval' => :'eval'
62
66
  }
63
67
  end
64
68
 
@@ -120,6 +124,10 @@ module DevCycle
120
124
  if attributes.key?(:'defaultValue')
121
125
  self.defaultValue = attributes[:'defaultValue']
122
126
  end
127
+
128
+ if attributes.key?(:'eval')
129
+ self.eval = attributes[:'eval']
130
+ end
123
131
  end
124
132
 
125
133
  # Show invalid properties with the reasons. Usually used together with valid?
@@ -1,3 +1,3 @@
1
1
  module DevCycle
2
- VERSION = '3.6.1'
2
+ VERSION = '3.7.0'
3
3
  end
@@ -25,6 +25,12 @@ require 'devcycle-ruby-server-sdk/models/user'
25
25
  require 'devcycle-ruby-server-sdk/models/user_data_and_events_body'
26
26
  require 'devcycle-ruby-server-sdk/models/variable'
27
27
 
28
+ # Eval Hooks
29
+ require 'devcycle-ruby-server-sdk/eval_hooks_runner'
30
+ require 'devcycle-ruby-server-sdk/models/eval_hook'
31
+ require 'devcycle-ruby-server-sdk/models/eval_hook_context'
32
+ require 'devcycle-ruby-server-sdk/eval_reasons'
33
+
28
34
  # APIs
29
35
  require 'devcycle-ruby-server-sdk/api/client'
30
36
  require 'devcycle-ruby-server-sdk/api/dev_cycle_provider'
@@ -21,16 +21,18 @@ describe 'DevCycle::Client' do
21
21
  sdk_key = ENV["DEVCYCLE_SERVER_SDK_KEY"]
22
22
  if sdk_key.nil?
23
23
  puts("SDK KEY NOT SET - SKIPPING INIT")
24
- return
24
+ @skip_tests = true
25
+ else
26
+ # run before each test
27
+ options = DevCycle::Options.new(enable_cloud_bucketing: true)
28
+ @api_instance = DevCycle::Client.new(sdk_key, options)
29
+
30
+ @user = DevCycle::User.new({
31
+ user_id: 'test-user',
32
+ appVersion: '1.2.3'
33
+ })
34
+ @skip_tests = false
25
35
  end
26
- # run before each test
27
- options = DevCycle::Options.new(enable_cloud_bucketing: true)
28
- @api_instance = DevCycle::Client.new(sdk_key, options)
29
-
30
- @user = DevCycle::User.new({
31
- user_id: 'test-user',
32
- appVersion: '1.2.3'
33
- })
34
36
  end
35
37
 
36
38
  after do