togglecraft 1.0.1 → 1.0.3

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: ffa98d6abdac97120d6cdfd774cd8da1e99fd1b88e2bfb2f978a500c50388f4e
4
- data.tar.gz: 5fcf387b554e526d25bc28c430650b5ee3849c1eff0523b11117fd570959a7eb
3
+ metadata.gz: 33360223d2f26e037faa039bc9c8033cda78f7597e90dac2b2998e9a921daab7
4
+ data.tar.gz: 8522409a6d3d4b8e6ed49a7db8da1ef8bba17f4c498523b9c9a2c0c48440ef6b
5
5
  SHA512:
6
- metadata.gz: f07746457dfdd5ecd50882359a8ffad9e107ee4f90ff21a599dc815794b7fb3a21b0a9245b4f5f4c64bc010afc6467856f52503b5318a31661deae4ac8c0a22f
7
- data.tar.gz: 1e00cbc1d6be11cc76186de12a926d4a23338ff5818a44ee5b86ecb24bcbda6ea2771a9fe485dada25362584a727b8d03d859cd482f4e2df5d63407ed9802abb
6
+ metadata.gz: 92920a17fdadae7951231d95455e1a4925d3c1be1db3beef2286c389208e6ff989126665605e5b7fcf49c7b9056a1a7785aaf62a5be9addd605cae36842734ee
7
+ data.tar.gz: 44f8e14db2f45b284396b774291394df7a5d904bd596f6d94c3d5dab0c65d9b6700585416a9f8b1f3cddf18e4389f66fae360f509e7368acd1f2e8164695fc50
@@ -13,11 +13,11 @@ module ToggleCraft
13
13
  def initialize(adapter: :memory, ttl: 300)
14
14
  @default_ttl = ttl
15
15
  @adapter = case adapter
16
- when :memory
16
+ when :memory
17
17
  CacheAdapters::MemoryAdapter.new
18
- else
18
+ else
19
19
  adapter
20
- end
20
+ end
21
21
  @cleanup_thread = nil
22
22
  start_cleanup
23
23
  end
@@ -26,7 +26,8 @@ module ToggleCraft
26
26
  enable_rollout_stage_polling: true,
27
27
  rollout_stage_check_interval: 60,
28
28
  fetch_jitter: 1500, # milliseconds
29
- api_domain: 'https://togglecraft.io'
29
+ api_domain: 'https://togglecraft.io',
30
+ heartbeat_domain: 'https://togglecraft.io' # Default heartbeat domain
30
31
  }.merge(options).merge(sdk_key: sdk_key)
31
32
 
32
33
  # Initialize components
@@ -334,7 +335,6 @@ module ToggleCraft
334
335
  max_reconnect_interval: @config[:max_reconnect_interval],
335
336
  max_reconnect_attempts: @config[:max_reconnect_attempts],
336
337
  slow_reconnect_interval: @config[:slow_reconnect_interval],
337
- heartbeat_domain: @config[:heartbeat_domain],
338
338
  debug: @config[:debug],
339
339
  on_message: method(:handle_flags_update),
340
340
  on_connect: method(:handle_connect),
@@ -393,7 +393,8 @@ module ToggleCraft
393
393
  @fetch_in_progress.make_true
394
394
 
395
395
  begin
396
- versioned_url = "#{@config[:api_domain]}/api/v1/flags/#{@project_key.get}/#{@environment_key.get}/v/#{version}"
396
+ # API domain always hardcoded to production
397
+ versioned_url = "https://togglecraft.io/api/v1/flags/#{@project_key.get}/#{@environment_key.get}/v/#{version}"
397
398
  log "Fetching flags from #{versioned_url}"
398
399
 
399
400
  response = HTTP.timeout(10).get(versioned_url)
@@ -428,6 +429,9 @@ module ToggleCraft
428
429
  # Update cache
429
430
  @cache&.set_all_flags(payload[:flags] || {})
430
431
 
432
+ # Clear evaluation cache for all updated flags
433
+ clear_evaluation_cache_for_flags(payload[:flags]&.keys || [])
434
+
431
435
  # Initialize rollout stage tracking
432
436
  initialize_rollout_stage_tracking
433
437
 
@@ -187,7 +187,7 @@ module ToggleCraft
187
187
 
188
188
  result = if result.nil?
189
189
  condition_result
190
- else
190
+ else
191
191
  # Apply combinator logic
192
192
  case previous_combinator
193
193
  when 'OR'
@@ -196,7 +196,7 @@ module ToggleCraft
196
196
  # Default to AND
197
197
  result && condition_result
198
198
  end
199
- end
199
+ end
200
200
 
201
201
  previous_combinator = condition[:combinator] || 'AND'
202
202
 
@@ -158,7 +158,6 @@ module ToggleCraft
158
158
  max_reconnect_interval: @config[:max_reconnect_interval],
159
159
  max_reconnect_attempts: @config[:max_reconnect_attempts],
160
160
  slow_reconnect_interval: @config[:slow_reconnect_interval],
161
- heartbeat_domain: @config[:heartbeat_domain],
162
161
  debug: @debug,
163
162
  on_message: method(:handle_message),
164
163
  on_connect: method(:handle_connect),
@@ -33,10 +33,10 @@ module ToggleCraft
33
33
  @should_reconnect = true
34
34
 
35
35
  # Callbacks
36
- @on_message = options[:on_message] || proc {}
37
- @on_connect = options[:on_connect] || proc {}
38
- @on_disconnect = options[:on_disconnect] || proc {}
39
- @on_error = options[:on_error] || proc {}
36
+ @on_message = options[:on_message] || proc { }
37
+ @on_connect = options[:on_connect] || proc { }
38
+ @on_disconnect = options[:on_disconnect] || proc { }
39
+ @on_error = options[:on_error] || proc { }
40
40
 
41
41
  # Threads
42
42
  @connection_thread = nil
@@ -204,8 +204,8 @@ module ToggleCraft
204
204
  log "Max fast reconnection attempts reached, switching to slow mode (every #{interval}s)"
205
205
  else
206
206
  @reconnect_attempts += 1
207
- interval = [@options[:reconnect_interval] * (2**(@reconnect_attempts - 1)),
208
- @options[:max_reconnect_interval]].min
207
+ interval = [ @options[:reconnect_interval] * (2**(@reconnect_attempts - 1)),
208
+ @options[:max_reconnect_interval] ].min
209
209
  log "Scheduling reconnection attempt #{@reconnect_attempts}/" \
210
210
  "#{@options[:max_reconnect_attempts]} in #{interval}s"
211
211
  end
@@ -263,7 +263,8 @@ module ToggleCraft
263
263
  end
264
264
 
265
265
  begin
266
- heartbeat_url = "#{@options[:heartbeat_domain]}/api/v1/heartbeat"
266
+ # Heartbeat always goes to production API (hardcoded)
267
+ heartbeat_url = "https://togglecraft.io/api/v1/heartbeat"
267
268
  log "Sending heartbeat to #{heartbeat_url}"
268
269
 
269
270
  response = HTTP.timeout(10)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ToggleCraft
4
- VERSION = '1.0.1'
4
+ VERSION = '1.0.3'
5
5
  end
@@ -30,12 +30,12 @@ RSpec.describe ToggleCraft::CacheAdapters::MemoryAdapter do
30
30
  adapter.set('string', 'text')
31
31
  adapter.set('number', 123)
32
32
  adapter.set('hash', { foo: 'bar' })
33
- adapter.set('array', [1, 2, 3])
33
+ adapter.set('array', [ 1, 2, 3 ])
34
34
 
35
35
  expect(adapter.get('string')).to eq('text')
36
36
  expect(adapter.get('number')).to eq(123)
37
37
  expect(adapter.get('hash')).to eq({ foo: 'bar' })
38
- expect(adapter.get('array')).to eq([1, 2, 3])
38
+ expect(adapter.get('array')).to eq([ 1, 2, 3 ])
39
39
  end
40
40
  end
41
41
 
@@ -48,13 +48,13 @@ RSpec.describe ToggleCraft::Cache do
48
48
  cache.set('string', 'text')
49
49
  cache.set('number', 123)
50
50
  cache.set('hash', { foo: 'bar' })
51
- cache.set('array', [1, 2, 3])
51
+ cache.set('array', [ 1, 2, 3 ])
52
52
  cache.set('boolean', true)
53
53
 
54
54
  expect(cache.get('string')).to eq('text')
55
55
  expect(cache.get('number')).to eq(123)
56
56
  expect(cache.get('hash')).to eq({ foo: 'bar' })
57
- expect(cache.get('array')).to eq([1, 2, 3])
57
+ expect(cache.get('array')).to eq([ 1, 2, 3 ])
58
58
  expect(cache.get('boolean')).to be true
59
59
  end
60
60
  end
@@ -203,7 +203,7 @@ RSpec.describe ToggleCraft::Client do
203
203
  it 'accepts context for evaluation' do
204
204
  context = { user: { id: '123', role: 'admin' } }
205
205
  result = client.enabled?('boolean-flag', context)
206
- expect([true, false]).to include(result)
206
+ expect([ true, false ]).to include(result)
207
207
  end
208
208
 
209
209
  it 'caches evaluation results when cache is enabled' do
@@ -261,7 +261,7 @@ RSpec.describe ToggleCraft::Client do
261
261
  it 'evaluates percentage flags' do
262
262
  context = { user: { id: '123' } }
263
263
  result = client.in_percentage?('percentage-flag', context)
264
- expect([true, false]).to include(result)
264
+ expect([ true, false ]).to include(result)
265
265
  end
266
266
 
267
267
  it 'returns default value for missing flags' do
@@ -297,7 +297,7 @@ RSpec.describe ToggleCraft::Client do
297
297
  expect(client.evaluate('boolean-flag')).to be true
298
298
  expect(%w[control variant-a]).to include(client.evaluate('multivariate-flag', { user: { id: '123' } }))
299
299
  result = client.evaluate('percentage-flag', { user: { id: '123' } })
300
- expect([true, false]).to include(result)
300
+ expect([ true, false ]).to include(result)
301
301
  end
302
302
 
303
303
  it 'returns default for missing flags' do
@@ -396,7 +396,7 @@ RSpec.describe ToggleCraft::Client do
396
396
  client.send(:emit, :reconnecting)
397
397
  client.send(:emit, :rollout_stage_changed, {})
398
398
 
399
- expect(events_received).to eq([:ready, :flags_updated, :error, :disconnected, :reconnecting, :rollout_stage_changed])
399
+ expect(events_received).to eq([ :ready, :flags_updated, :error, :disconnected, :reconnecting, :rollout_stage_changed ])
400
400
  end
401
401
 
402
402
  it 'handles errors in event listeners gracefully' do
@@ -41,7 +41,7 @@ RSpec.describe ToggleCraft::Evaluator do
41
41
  rules: [
42
42
  {
43
43
  conditions: [
44
- { attribute: 'user.role', operator: 'equals', values: ['admin'] }
44
+ { attribute: 'user.role', operator: 'equals', values: [ 'admin' ] }
45
45
  ],
46
46
  value: true
47
47
  }
@@ -110,7 +110,7 @@ RSpec.describe ToggleCraft::Evaluator do
110
110
  rules: [
111
111
  {
112
112
  conditions: [
113
- { attribute: 'user.plan', operator: 'equals', values: ['premium'] }
113
+ { attribute: 'user.plan', operator: 'equals', values: [ 'premium' ] }
114
114
  ],
115
115
  variant: 'premium-variant'
116
116
  }
@@ -179,7 +179,7 @@ RSpec.describe ToggleCraft::Evaluator do
179
179
  rules: [
180
180
  {
181
181
  conditions: [
182
- { attribute: 'user.beta_tester', operator: 'equals', values: ['true'] }
182
+ { attribute: 'user.beta_tester', operator: 'equals', values: [ 'true' ] }
183
183
  ],
184
184
  enabled: true
185
185
  }
@@ -301,8 +301,8 @@ RSpec.describe ToggleCraft::Evaluator do
301
301
  it 'returns true when all AND conditions pass' do
302
302
  rule = {
303
303
  conditions: [
304
- { attribute: 'user.role', operator: 'equals', values: ['admin'], combinator: 'AND' },
305
- { attribute: 'user.active', operator: 'equals', values: ['true'], combinator: 'AND' }
304
+ { attribute: 'user.role', operator: 'equals', values: [ 'admin' ], combinator: 'AND' },
305
+ { attribute: 'user.active', operator: 'equals', values: [ 'true' ], combinator: 'AND' }
306
306
  ]
307
307
  }
308
308
  context = { user: { role: 'admin', active: 'true' } }
@@ -313,8 +313,8 @@ RSpec.describe ToggleCraft::Evaluator do
313
313
  it 'returns false when any AND condition fails' do
314
314
  rule = {
315
315
  conditions: [
316
- { attribute: 'user.role', operator: 'equals', values: ['admin'], combinator: 'AND' },
317
- { attribute: 'user.active', operator: 'equals', values: ['true'], combinator: 'AND' }
316
+ { attribute: 'user.role', operator: 'equals', values: [ 'admin' ], combinator: 'AND' },
317
+ { attribute: 'user.active', operator: 'equals', values: [ 'true' ], combinator: 'AND' }
318
318
  ]
319
319
  }
320
320
  context = { user: { role: 'admin', active: 'false' } }
@@ -325,8 +325,8 @@ RSpec.describe ToggleCraft::Evaluator do
325
325
  it 'returns true when any OR condition passes' do
326
326
  rule = {
327
327
  conditions: [
328
- { attribute: 'user.role', operator: 'equals', values: ['admin'], combinator: 'OR' },
329
- { attribute: 'user.role', operator: 'equals', values: ['superuser'], combinator: 'OR' }
328
+ { attribute: 'user.role', operator: 'equals', values: [ 'admin' ], combinator: 'OR' },
329
+ { attribute: 'user.role', operator: 'equals', values: [ 'superuser' ], combinator: 'OR' }
330
330
  ]
331
331
  }
332
332
  context = { user: { role: 'superuser' } }
@@ -337,9 +337,9 @@ RSpec.describe ToggleCraft::Evaluator do
337
337
  it 'handles mixed AND/OR combinators' do
338
338
  rule = {
339
339
  conditions: [
340
- { attribute: 'user.role', operator: 'equals', values: ['admin'], combinator: 'OR' },
341
- { attribute: 'user.department', operator: 'equals', values: ['engineering'], combinator: 'AND' },
342
- { attribute: 'user.level', operator: 'gt', values: ['5'] }
340
+ { attribute: 'user.role', operator: 'equals', values: [ 'admin' ], combinator: 'OR' },
341
+ { attribute: 'user.department', operator: 'equals', values: [ 'engineering' ], combinator: 'AND' },
342
+ { attribute: 'user.level', operator: 'gt', values: [ '5' ] }
343
343
  ]
344
344
  }
345
345
 
@@ -312,7 +312,7 @@ RSpec.describe ToggleCraft::SSEConnection do
312
312
  )
313
313
 
314
314
  conn.instance_variable_get(:@on_message).call({ test: 'data' })
315
- expect(messages).to eq([{ test: 'data' }])
315
+ expect(messages).to eq([ { test: 'data' } ])
316
316
 
317
317
  conn.disconnect
318
318
  end
@@ -230,75 +230,75 @@ RSpec.describe ToggleCraft::Utils do
230
230
  describe '.evaluate_operator' do
231
231
  describe 'equals operator' do
232
232
  it 'returns true when values are equal (case-insensitive)' do
233
- expect(described_class.evaluate_operator('test', 'equals', ['test'])).to be true
234
- expect(described_class.evaluate_operator('Test', 'equals', ['test'])).to be true
235
- expect(described_class.evaluate_operator('TEST', 'equals', ['test'])).to be true
233
+ expect(described_class.evaluate_operator('test', 'equals', [ 'test' ])).to be true
234
+ expect(described_class.evaluate_operator('Test', 'equals', [ 'test' ])).to be true
235
+ expect(described_class.evaluate_operator('TEST', 'equals', [ 'test' ])).to be true
236
236
  end
237
237
 
238
238
  it 'returns false when values are not equal' do
239
- expect(described_class.evaluate_operator('test', 'equals', ['other'])).to be false
239
+ expect(described_class.evaluate_operator('test', 'equals', [ 'other' ])).to be false
240
240
  end
241
241
 
242
242
  it 'handles numeric values' do
243
- expect(described_class.evaluate_operator(123, 'equals', [123])).to be true
244
- expect(described_class.evaluate_operator(123, 'equals', ['123'])).to be true
243
+ expect(described_class.evaluate_operator(123, 'equals', [ 123 ])).to be true
244
+ expect(described_class.evaluate_operator(123, 'equals', [ '123' ])).to be true
245
245
  end
246
246
  end
247
247
 
248
248
  describe 'not_equals operator' do
249
249
  it 'returns false when values are equal' do
250
- expect(described_class.evaluate_operator('test', 'not_equals', ['test'])).to be false
250
+ expect(described_class.evaluate_operator('test', 'not_equals', [ 'test' ])).to be false
251
251
  end
252
252
 
253
253
  it 'returns true when values are not equal' do
254
- expect(described_class.evaluate_operator('test', 'not_equals', ['other'])).to be true
254
+ expect(described_class.evaluate_operator('test', 'not_equals', [ 'other' ])).to be true
255
255
  end
256
256
 
257
257
  it 'returns true for nil values' do
258
- expect(described_class.evaluate_operator(nil, 'not_equals', ['test'])).to be true
258
+ expect(described_class.evaluate_operator(nil, 'not_equals', [ 'test' ])).to be true
259
259
  end
260
260
  end
261
261
 
262
262
  describe 'contains operator' do
263
263
  it 'returns true when attribute contains value' do
264
- expect(described_class.evaluate_operator('hello world', 'contains', ['world'])).to be true
265
- expect(described_class.evaluate_operator('HELLO WORLD', 'contains', ['world'])).to be true
264
+ expect(described_class.evaluate_operator('hello world', 'contains', [ 'world' ])).to be true
265
+ expect(described_class.evaluate_operator('HELLO WORLD', 'contains', [ 'world' ])).to be true
266
266
  end
267
267
 
268
268
  it 'returns false when attribute does not contain value' do
269
- expect(described_class.evaluate_operator('hello world', 'contains', ['xyz'])).to be false
269
+ expect(described_class.evaluate_operator('hello world', 'contains', [ 'xyz' ])).to be false
270
270
  end
271
271
  end
272
272
 
273
273
  describe 'not_contains operator' do
274
274
  it 'returns false when attribute contains value' do
275
- expect(described_class.evaluate_operator('hello world', 'not_contains', ['world'])).to be false
275
+ expect(described_class.evaluate_operator('hello world', 'not_contains', [ 'world' ])).to be false
276
276
  end
277
277
 
278
278
  it 'returns true when attribute does not contain value' do
279
- expect(described_class.evaluate_operator('hello world', 'not_contains', ['xyz'])).to be true
279
+ expect(described_class.evaluate_operator('hello world', 'not_contains', [ 'xyz' ])).to be true
280
280
  end
281
281
  end
282
282
 
283
283
  describe 'starts_with operator' do
284
284
  it 'returns true when attribute starts with value' do
285
- expect(described_class.evaluate_operator('hello world', 'starts_with', ['hello'])).to be true
286
- expect(described_class.evaluate_operator('HELLO WORLD', 'starts_with', ['hello'])).to be true
285
+ expect(described_class.evaluate_operator('hello world', 'starts_with', [ 'hello' ])).to be true
286
+ expect(described_class.evaluate_operator('HELLO WORLD', 'starts_with', [ 'hello' ])).to be true
287
287
  end
288
288
 
289
289
  it 'returns false when attribute does not start with value' do
290
- expect(described_class.evaluate_operator('hello world', 'starts_with', ['world'])).to be false
290
+ expect(described_class.evaluate_operator('hello world', 'starts_with', [ 'world' ])).to be false
291
291
  end
292
292
  end
293
293
 
294
294
  describe 'ends_with operator' do
295
295
  it 'returns true when attribute ends with value' do
296
- expect(described_class.evaluate_operator('hello world', 'ends_with', ['world'])).to be true
297
- expect(described_class.evaluate_operator('HELLO WORLD', 'ends_with', ['world'])).to be true
296
+ expect(described_class.evaluate_operator('hello world', 'ends_with', [ 'world' ])).to be true
297
+ expect(described_class.evaluate_operator('HELLO WORLD', 'ends_with', [ 'world' ])).to be true
298
298
  end
299
299
 
300
300
  it 'returns false when attribute does not end with value' do
301
- expect(described_class.evaluate_operator('hello world', 'ends_with', ['hello'])).to be false
301
+ expect(described_class.evaluate_operator('hello world', 'ends_with', [ 'hello' ])).to be false
302
302
  end
303
303
  end
304
304
 
@@ -313,8 +313,8 @@ RSpec.describe ToggleCraft::Utils do
313
313
  end
314
314
 
315
315
  it 'handles numeric values' do
316
- expect(described_class.evaluate_operator(1, 'in', [1, 2, 3])).to be true
317
- expect(described_class.evaluate_operator(4, 'in', [1, 2, 3])).to be false
316
+ expect(described_class.evaluate_operator(1, 'in', [ 1, 2, 3 ])).to be true
317
+ expect(described_class.evaluate_operator(4, 'in', [ 1, 2, 3 ])).to be false
318
318
  end
319
319
  end
320
320
 
@@ -334,170 +334,170 @@ RSpec.describe ToggleCraft::Utils do
334
334
 
335
335
  describe 'gt operator' do
336
336
  it 'returns true when attribute is greater than value' do
337
- expect(described_class.evaluate_operator(10, 'gt', [5])).to be true
338
- expect(described_class.evaluate_operator(5.5, 'gt', [5])).to be true
337
+ expect(described_class.evaluate_operator(10, 'gt', [ 5 ])).to be true
338
+ expect(described_class.evaluate_operator(5.5, 'gt', [ 5 ])).to be true
339
339
  end
340
340
 
341
341
  it 'returns false when attribute is less than or equal to value' do
342
- expect(described_class.evaluate_operator(5, 'gt', [10])).to be false
343
- expect(described_class.evaluate_operator(5, 'gt', [5])).to be false
342
+ expect(described_class.evaluate_operator(5, 'gt', [ 10 ])).to be false
343
+ expect(described_class.evaluate_operator(5, 'gt', [ 5 ])).to be false
344
344
  end
345
345
 
346
346
  it 'handles string numeric values' do
347
- expect(described_class.evaluate_operator('10', 'gt', ['5'])).to be true
347
+ expect(described_class.evaluate_operator('10', 'gt', [ '5' ])).to be true
348
348
  end
349
349
  end
350
350
 
351
351
  describe 'gte operator' do
352
352
  it 'returns true when attribute is greater than or equal to value' do
353
- expect(described_class.evaluate_operator(10, 'gte', [5])).to be true
354
- expect(described_class.evaluate_operator(5, 'gte', [5])).to be true
353
+ expect(described_class.evaluate_operator(10, 'gte', [ 5 ])).to be true
354
+ expect(described_class.evaluate_operator(5, 'gte', [ 5 ])).to be true
355
355
  end
356
356
 
357
357
  it 'returns false when attribute is less than value' do
358
- expect(described_class.evaluate_operator(5, 'gte', [10])).to be false
358
+ expect(described_class.evaluate_operator(5, 'gte', [ 10 ])).to be false
359
359
  end
360
360
  end
361
361
 
362
362
  describe 'lt operator' do
363
363
  it 'returns true when attribute is less than value' do
364
- expect(described_class.evaluate_operator(5, 'lt', [10])).to be true
364
+ expect(described_class.evaluate_operator(5, 'lt', [ 10 ])).to be true
365
365
  end
366
366
 
367
367
  it 'returns false when attribute is greater than or equal to value' do
368
- expect(described_class.evaluate_operator(10, 'lt', [5])).to be false
369
- expect(described_class.evaluate_operator(5, 'lt', [5])).to be false
368
+ expect(described_class.evaluate_operator(10, 'lt', [ 5 ])).to be false
369
+ expect(described_class.evaluate_operator(5, 'lt', [ 5 ])).to be false
370
370
  end
371
371
  end
372
372
 
373
373
  describe 'lte operator' do
374
374
  it 'returns true when attribute is less than or equal to value' do
375
- expect(described_class.evaluate_operator(5, 'lte', [10])).to be true
376
- expect(described_class.evaluate_operator(5, 'lte', [5])).to be true
375
+ expect(described_class.evaluate_operator(5, 'lte', [ 10 ])).to be true
376
+ expect(described_class.evaluate_operator(5, 'lte', [ 5 ])).to be true
377
377
  end
378
378
 
379
379
  it 'returns false when attribute is greater than value' do
380
- expect(described_class.evaluate_operator(10, 'lte', [5])).to be false
380
+ expect(described_class.evaluate_operator(10, 'lte', [ 5 ])).to be false
381
381
  end
382
382
  end
383
383
 
384
384
  describe 'between operator' do
385
385
  it 'returns true when attribute is between min and max (inclusive)' do
386
- expect(described_class.evaluate_operator(5, 'between', [1, 10])).to be true
387
- expect(described_class.evaluate_operator(1, 'between', [1, 10])).to be true
388
- expect(described_class.evaluate_operator(10, 'between', [1, 10])).to be true
386
+ expect(described_class.evaluate_operator(5, 'between', [ 1, 10 ])).to be true
387
+ expect(described_class.evaluate_operator(1, 'between', [ 1, 10 ])).to be true
388
+ expect(described_class.evaluate_operator(10, 'between', [ 1, 10 ])).to be true
389
389
  end
390
390
 
391
391
  it 'returns false when attribute is outside range' do
392
- expect(described_class.evaluate_operator(0, 'between', [1, 10])).to be false
393
- expect(described_class.evaluate_operator(11, 'between', [1, 10])).to be false
392
+ expect(described_class.evaluate_operator(0, 'between', [ 1, 10 ])).to be false
393
+ expect(described_class.evaluate_operator(11, 'between', [ 1, 10 ])).to be false
394
394
  end
395
395
 
396
396
  it 'returns false when condition values are insufficient' do
397
- expect(described_class.evaluate_operator(5, 'between', [1])).to be false
397
+ expect(described_class.evaluate_operator(5, 'between', [ 1 ])).to be false
398
398
  end
399
399
 
400
400
  it 'handles decimal values' do
401
- expect(described_class.evaluate_operator(5.5, 'between', [5, 6])).to be true
401
+ expect(described_class.evaluate_operator(5.5, 'between', [ 5, 6 ])).to be true
402
402
  end
403
403
  end
404
404
 
405
405
  describe 'regex operator' do
406
406
  it 'returns true when attribute matches regex' do
407
- expect(described_class.evaluate_operator('test123', 'regex', ['^test\\d+$'])).to be true
408
- expect(described_class.evaluate_operator('user@example.com', 'regex', ['^[^@]+@[^@]+$'])).to be true
407
+ expect(described_class.evaluate_operator('test123', 'regex', [ '^test\\d+$' ])).to be true
408
+ expect(described_class.evaluate_operator('user@example.com', 'regex', [ '^[^@]+@[^@]+$' ])).to be true
409
409
  end
410
410
 
411
411
  it 'returns false when attribute does not match regex' do
412
- expect(described_class.evaluate_operator('test', 'regex', ['^\\d+$'])).to be false
412
+ expect(described_class.evaluate_operator('test', 'regex', [ '^\\d+$' ])).to be false
413
413
  end
414
414
 
415
415
  it 'returns false for invalid regex' do
416
- expect(described_class.evaluate_operator('test', 'regex', ['[invalid'])).to be false
416
+ expect(described_class.evaluate_operator('test', 'regex', [ '[invalid' ])).to be false
417
417
  end
418
418
 
419
419
  it 'handles complex patterns' do
420
- expect(described_class.evaluate_operator('192.168.1.1', 'regex', ['^\\d+\\.\\d+\\.\\d+\\.\\d+$'])).to be true
420
+ expect(described_class.evaluate_operator('192.168.1.1', 'regex', [ '^\\d+\\.\\d+\\.\\d+\\.\\d+$' ])).to be true
421
421
  end
422
422
  end
423
423
 
424
424
  describe 'semver operators' do
425
425
  describe 'semver_eq' do
426
426
  it 'returns true for equal versions' do
427
- expect(described_class.evaluate_operator('1.2.3', 'semver_eq', ['1.2.3'])).to be true
427
+ expect(described_class.evaluate_operator('1.2.3', 'semver_eq', [ '1.2.3' ])).to be true
428
428
  end
429
429
 
430
430
  it 'returns false for different versions' do
431
- expect(described_class.evaluate_operator('1.2.3', 'semver_eq', ['1.2.4'])).to be false
431
+ expect(described_class.evaluate_operator('1.2.3', 'semver_eq', [ '1.2.4' ])).to be false
432
432
  end
433
433
  end
434
434
 
435
435
  describe 'semver_gt' do
436
436
  it 'returns true when attribute version is greater' do
437
- expect(described_class.evaluate_operator('2.0.0', 'semver_gt', ['1.0.0'])).to be true
438
- expect(described_class.evaluate_operator('1.1.0', 'semver_gt', ['1.0.0'])).to be true
437
+ expect(described_class.evaluate_operator('2.0.0', 'semver_gt', [ '1.0.0' ])).to be true
438
+ expect(described_class.evaluate_operator('1.1.0', 'semver_gt', [ '1.0.0' ])).to be true
439
439
  end
440
440
 
441
441
  it 'returns false when attribute version is less or equal' do
442
- expect(described_class.evaluate_operator('1.0.0', 'semver_gt', ['2.0.0'])).to be false
443
- expect(described_class.evaluate_operator('1.0.0', 'semver_gt', ['1.0.0'])).to be false
442
+ expect(described_class.evaluate_operator('1.0.0', 'semver_gt', [ '2.0.0' ])).to be false
443
+ expect(described_class.evaluate_operator('1.0.0', 'semver_gt', [ '1.0.0' ])).to be false
444
444
  end
445
445
  end
446
446
 
447
447
  describe 'semver_gte' do
448
448
  it 'returns true when attribute version is greater or equal' do
449
- expect(described_class.evaluate_operator('2.0.0', 'semver_gte', ['1.0.0'])).to be true
450
- expect(described_class.evaluate_operator('1.0.0', 'semver_gte', ['1.0.0'])).to be true
449
+ expect(described_class.evaluate_operator('2.0.0', 'semver_gte', [ '1.0.0' ])).to be true
450
+ expect(described_class.evaluate_operator('1.0.0', 'semver_gte', [ '1.0.0' ])).to be true
451
451
  end
452
452
 
453
453
  it 'returns false when attribute version is less' do
454
- expect(described_class.evaluate_operator('1.0.0', 'semver_gte', ['2.0.0'])).to be false
454
+ expect(described_class.evaluate_operator('1.0.0', 'semver_gte', [ '2.0.0' ])).to be false
455
455
  end
456
456
  end
457
457
 
458
458
  describe 'semver_lt' do
459
459
  it 'returns true when attribute version is less' do
460
- expect(described_class.evaluate_operator('1.0.0', 'semver_lt', ['2.0.0'])).to be true
460
+ expect(described_class.evaluate_operator('1.0.0', 'semver_lt', [ '2.0.0' ])).to be true
461
461
  end
462
462
 
463
463
  it 'returns false when attribute version is greater or equal' do
464
- expect(described_class.evaluate_operator('2.0.0', 'semver_lt', ['1.0.0'])).to be false
465
- expect(described_class.evaluate_operator('1.0.0', 'semver_lt', ['1.0.0'])).to be false
464
+ expect(described_class.evaluate_operator('2.0.0', 'semver_lt', [ '1.0.0' ])).to be false
465
+ expect(described_class.evaluate_operator('1.0.0', 'semver_lt', [ '1.0.0' ])).to be false
466
466
  end
467
467
  end
468
468
 
469
469
  describe 'semver_lte' do
470
470
  it 'returns true when attribute version is less or equal' do
471
- expect(described_class.evaluate_operator('1.0.0', 'semver_lte', ['2.0.0'])).to be true
472
- expect(described_class.evaluate_operator('1.0.0', 'semver_lte', ['1.0.0'])).to be true
471
+ expect(described_class.evaluate_operator('1.0.0', 'semver_lte', [ '2.0.0' ])).to be true
472
+ expect(described_class.evaluate_operator('1.0.0', 'semver_lte', [ '1.0.0' ])).to be true
473
473
  end
474
474
 
475
475
  it 'returns false when attribute version is greater' do
476
- expect(described_class.evaluate_operator('2.0.0', 'semver_lte', ['1.0.0'])).to be false
476
+ expect(described_class.evaluate_operator('2.0.0', 'semver_lte', [ '1.0.0' ])).to be false
477
477
  end
478
478
  end
479
479
  end
480
480
 
481
481
  describe 'nil attribute handling' do
482
482
  it 'returns false for most operators when attribute is nil' do
483
- expect(described_class.evaluate_operator(nil, 'equals', ['test'])).to be false
484
- expect(described_class.evaluate_operator(nil, 'contains', ['test'])).to be false
485
- expect(described_class.evaluate_operator(nil, 'in', ['test'])).to be false
483
+ expect(described_class.evaluate_operator(nil, 'equals', [ 'test' ])).to be false
484
+ expect(described_class.evaluate_operator(nil, 'contains', [ 'test' ])).to be false
485
+ expect(described_class.evaluate_operator(nil, 'in', [ 'test' ])).to be false
486
486
  end
487
487
 
488
488
  it 'returns true for not_equals when attribute is nil' do
489
- expect(described_class.evaluate_operator(nil, 'not_equals', ['test'])).to be true
489
+ expect(described_class.evaluate_operator(nil, 'not_equals', [ 'test' ])).to be true
490
490
  end
491
491
 
492
492
  it 'returns true for not_in when attribute is nil' do
493
- expect(described_class.evaluate_operator(nil, 'not_in', ['test'])).to be true
493
+ expect(described_class.evaluate_operator(nil, 'not_in', [ 'test' ])).to be true
494
494
  end
495
495
  end
496
496
 
497
497
  describe 'unknown operator' do
498
498
  it 'returns false and warns for unknown operator' do
499
499
  expect do
500
- result = described_class.evaluate_operator('test', 'unknown_op', ['value'])
500
+ result = described_class.evaluate_operator('test', 'unknown_op', [ 'value' ])
501
501
  expect(result).to be false
502
502
  end.to output(/Unknown operator: unknown_op/).to_stderr
503
503
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: togglecraft
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - ToggleCraft