ldclient-rb 3.0.3 → 4.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.
@@ -0,0 +1,76 @@
1
+ require 'timecop'
2
+
3
+ describe LaunchDarkly::ExpiringCache do
4
+ subject { LaunchDarkly::ExpiringCache }
5
+
6
+ before(:each) do
7
+ Timecop.freeze(Time.now)
8
+ end
9
+
10
+ after(:each) do
11
+ Timecop.return
12
+ end
13
+
14
+ it "evicts entries based on TTL" do
15
+ c = subject.new(3, 300)
16
+ c[:a] = 1
17
+ c[:b] = 2
18
+
19
+ Timecop.freeze(Time.now + 330)
20
+
21
+ c[:c] = 3
22
+
23
+ expect(c[:a]).to be nil
24
+ expect(c[:b]).to be nil
25
+ expect(c[:c]).to eq 3
26
+ end
27
+
28
+ it "evicts entries based on max size" do
29
+ c = subject.new(2, 300)
30
+ c[:a] = 1
31
+ c[:b] = 2
32
+ c[:c] = 3
33
+
34
+ expect(c[:a]).to be nil
35
+ expect(c[:b]).to eq 2
36
+ expect(c[:c]).to eq 3
37
+ end
38
+
39
+ it "does not reset LRU on get" do
40
+ c = subject.new(2, 300)
41
+ c[:a] = 1
42
+ c[:b] = 2
43
+ c[:a]
44
+ c[:c] = 3
45
+
46
+ expect(c[:a]).to be nil
47
+ expect(c[:b]).to eq 2
48
+ expect(c[:c]).to eq 3
49
+ end
50
+
51
+ it "resets LRU on put" do
52
+ c = subject.new(2, 300)
53
+ c[:a] = 1
54
+ c[:b] = 2
55
+ c[:a] = 1
56
+ c[:c] = 3
57
+
58
+ expect(c[:a]).to eq 1
59
+ expect(c[:b]).to be nil
60
+ expect(c[:c]).to eq 3
61
+ end
62
+
63
+ it "resets TTL on put" do
64
+ c = subject.new(3, 300)
65
+ c[:a] = 1
66
+ c[:b] = 2
67
+
68
+ Timecop.freeze(Time.now + 330)
69
+ c[:a] = 1
70
+ c[:c] = 3
71
+
72
+ expect(c[:a]).to eq 1
73
+ expect(c[:b]).to be nil
74
+ expect(c[:c]).to eq 3
75
+ end
76
+ end
@@ -32,5 +32,6 @@
32
32
  true,
33
33
  false
34
34
  ],
35
+ "trackEvents": true,
35
36
  "deleted":false
36
37
  }
@@ -3,7 +3,12 @@ require "spec_helper"
3
3
 
4
4
  describe LaunchDarkly::LDClient do
5
5
  subject { LaunchDarkly::LDClient }
6
- let(:config) { LaunchDarkly::Config.new({offline: true}) }
6
+ let(:offline_config) { LaunchDarkly::Config.new({offline: true}) }
7
+ let(:offline_client) do
8
+ subject.new("secret", offline_config)
9
+ end
10
+ let(:update_processor) { NullUpdateProcessor.new }
11
+ let(:config) { LaunchDarkly::Config.new({send_events: false, update_processor: update_processor}) }
7
12
  let(:client) do
8
13
  subject.new("secret", config)
9
14
  end
@@ -24,11 +29,74 @@ describe LaunchDarkly::LDClient do
24
29
  JSON.parse(data, symbolize_names: true)
25
30
  end
26
31
 
32
+ def event_processor
33
+ client.instance_variable_get(:@event_processor)
34
+ end
35
+
27
36
  describe '#variation' do
28
37
  it "will return the default value if the client is offline" do
29
- result = client.variation(feature[:key], user, "default")
38
+ result = offline_client.variation("doesntmatter", user, "default")
30
39
  expect(result).to eq "default"
31
40
  end
41
+
42
+ it "queues a feature request event for an unknown feature" do
43
+ expect(event_processor).to receive(:add_event).with(hash_including(
44
+ kind: "feature", key: "badkey", user: user, value: "default", default: "default"
45
+ ))
46
+ client.variation("badkey", user, "default")
47
+ end
48
+
49
+ it "queues a feature request event for an existing feature" do
50
+ config.feature_store.init({ LaunchDarkly::FEATURES => {} })
51
+ config.feature_store.upsert(LaunchDarkly::FEATURES, feature)
52
+ expect(event_processor).to receive(:add_event).with(hash_including(
53
+ kind: "feature",
54
+ key: feature[:key],
55
+ version: feature[:version],
56
+ user: user,
57
+ variation: 0,
58
+ value: true,
59
+ default: "default",
60
+ trackEvents: true,
61
+ debugEventsUntilDate: nil
62
+ ))
63
+ client.variation(feature[:key], user, "default")
64
+ end
65
+
66
+ it "queues a feature event for an existing feature when user is nil" do
67
+ config.feature_store.init({ LaunchDarkly::FEATURES => {} })
68
+ config.feature_store.upsert(LaunchDarkly::FEATURES, feature)
69
+ expect(event_processor).to receive(:add_event).with(hash_including(
70
+ kind: "feature",
71
+ key: feature[:key],
72
+ version: feature[:version],
73
+ user: nil,
74
+ variation: nil,
75
+ value: "default",
76
+ default: "default",
77
+ trackEvents: true,
78
+ debugEventsUntilDate: nil
79
+ ))
80
+ client.variation(feature[:key], nil, "default")
81
+ end
82
+
83
+ it "queues a feature event for an existing feature when user key is nil" do
84
+ config.feature_store.init({ LaunchDarkly::FEATURES => {} })
85
+ config.feature_store.upsert(LaunchDarkly::FEATURES, feature)
86
+ bad_user = { name: "Bob" }
87
+ expect(event_processor).to receive(:add_event).with(hash_including(
88
+ kind: "feature",
89
+ key: feature[:key],
90
+ version: feature[:version],
91
+ user: bad_user,
92
+ variation: nil,
93
+ value: "default",
94
+ default: "default",
95
+ trackEvents: true,
96
+ debugEventsUntilDate: nil
97
+ ))
98
+ client.variation(feature[:key], bad_user, "default")
99
+ end
32
100
  end
33
101
 
34
102
  describe '#secure_mode_hash' do
@@ -40,22 +108,24 @@ describe LaunchDarkly::LDClient do
40
108
 
41
109
  describe '#track' do
42
110
  it "queues up an custom event" do
43
- expect(client.instance_variable_get(:@event_processor)).to receive(:add_event).with(hash_including(kind: "custom", key: "custom_event_name", user: user, data: 42))
111
+ expect(event_processor).to receive(:add_event).with(hash_including(kind: "custom", key: "custom_event_name", user: user, data: 42))
44
112
  client.track("custom_event_name", user, 42)
45
113
  end
114
+
46
115
  it "sanitizes the user in the event" do
47
- expect(client.instance_variable_get(:@event_processor)).to receive(:add_event).with(hash_including(user: sanitized_numeric_key_user))
116
+ expect(event_processor).to receive(:add_event).with(hash_including(user: sanitized_numeric_key_user))
48
117
  client.track("custom_event_name", numeric_key_user, nil)
49
118
  end
50
119
  end
51
120
 
52
121
  describe '#identify' do
53
122
  it "queues up an identify event" do
54
- expect(client.instance_variable_get(:@event_processor)).to receive(:add_event).with(hash_including(kind: "identify", key: user[:key], user: user))
123
+ expect(event_processor).to receive(:add_event).with(hash_including(kind: "identify", key: user[:key], user: user))
55
124
  client.identify(user)
56
125
  end
126
+
57
127
  it "sanitizes the user in the event" do
58
- expect(client.instance_variable_get(:@event_processor)).to receive(:add_event).with(hash_including(user: sanitized_numeric_key_user))
128
+ expect(event_processor).to receive(:add_event).with(hash_including(user: sanitized_numeric_key_user))
59
129
  client.identify(numeric_key_user)
60
130
  end
61
131
  end
@@ -72,24 +142,31 @@ describe LaunchDarkly::LDClient do
72
142
  end
73
143
 
74
144
  describe 'with send_events: false' do
75
- let(:config) { LaunchDarkly::Config.new({offline: true, send_events: false}) }
145
+ let(:config) { LaunchDarkly::Config.new({offline: true, send_events: false, update_processor: update_processor}) }
76
146
  let(:client) { subject.new("secret", config) }
77
147
 
78
- let(:queue) { client.instance_variable_get(:@event_processor).instance_variable_get(:@queue) }
148
+ it "uses a NullEventProcessor" do
149
+ ep = client.instance_variable_get(:@event_processor)
150
+ expect(ep).to be_a(LaunchDarkly::NullEventProcessor)
151
+ end
152
+ end
153
+
154
+ describe 'with send_events: true' do
155
+ let(:config_with_events) { LaunchDarkly::Config.new({offline: false, send_events: true, update_processor: update_processor}) }
156
+ let(:client_with_events) { subject.new("secret", config_with_events) }
79
157
 
80
- it "does not enqueue a feature event" do
81
- client.variation(feature[:key], user, "default")
82
- expect(queue.empty?).to be true
158
+ it "does not use a NullEventProcessor" do
159
+ ep = client_with_events.instance_variable_get(:@event_processor)
160
+ expect(ep).not_to be_a(LaunchDarkly::NullEventProcessor)
83
161
  end
162
+ end
84
163
 
85
- it "does not enqueue a custom event" do
86
- client.track("custom_event_name", user, 42)
87
- expect(queue.empty?).to be true
164
+ class NullUpdateProcessor
165
+ def start
88
166
  end
89
167
 
90
- it "does not enqueue an identify event" do
91
- client.identify(user)
92
- expect(queue.empty?).to be true
168
+ def initialized?
169
+ true
93
170
  end
94
171
  end
95
172
  end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+
3
+ describe LaunchDarkly::SimpleLRUCacheSet do
4
+ subject { LaunchDarkly::SimpleLRUCacheSet }
5
+
6
+ it "retains values up to capacity" do
7
+ lru = subject.new(3)
8
+ expect(lru.add("a")).to be false
9
+ expect(lru.add("b")).to be false
10
+ expect(lru.add("c")).to be false
11
+ expect(lru.add("a")).to be true
12
+ expect(lru.add("b")).to be true
13
+ expect(lru.add("c")).to be true
14
+ end
15
+
16
+ it "discards oldest value on overflow" do
17
+ lru = subject.new(2)
18
+ expect(lru.add("a")).to be false
19
+ expect(lru.add("b")).to be false
20
+ expect(lru.add("a")).to be true
21
+ expect(lru.add("c")).to be false # b is discarded as oldest
22
+ expect(lru.add("b")).to be false
23
+ end
24
+ end
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
- describe LaunchDarkly::EventSerializer do
4
- subject { LaunchDarkly::EventSerializer }
3
+ describe LaunchDarkly::UserFilter do
4
+ subject { LaunchDarkly::UserFilter }
5
5
 
6
6
  let(:base_config) { LaunchDarkly::Config.new }
7
7
  let(:config_with_all_attrs_private) { LaunchDarkly::Config.new({ all_attributes_private: true })}
@@ -45,68 +45,47 @@ describe LaunchDarkly::EventSerializer do
45
45
  { key: 'abc', anonymous: 'true', custom: { }, privateAttrs: [ 'bizzle', 'dizzle' ]}
46
46
  }
47
47
 
48
-
49
- def make_event(user)
50
- {
51
- creationDate: 1000000,
52
- key: 'xyz',
53
- kind: 'thing',
54
- user: user
55
- }
56
- end
57
-
58
- def parse_results(js)
59
- JSON.parse(js, symbolize_names: true)
60
- end
61
-
62
48
  describe "serialize_events" do
63
49
  it "includes all user attributes by default" do
64
- es = LaunchDarkly::EventSerializer.new(base_config)
65
- event = make_event(user)
66
- j = es.serialize_events([event])
67
- expect(parse_results(j)).to eq [event]
50
+ uf = LaunchDarkly::UserFilter.new(base_config)
51
+ result = uf.transform_user_props(user)
52
+ expect(result).to eq user
68
53
  end
69
54
 
70
55
  it "hides all except key if all_attributes_private is true" do
71
- es = LaunchDarkly::EventSerializer.new(config_with_all_attrs_private)
72
- event = make_event(user)
73
- j = es.serialize_events([event])
74
- expect(parse_results(j)).to eq [make_event(user_with_all_attrs_hidden)]
56
+ uf = LaunchDarkly::UserFilter.new(config_with_all_attrs_private)
57
+ result = uf.transform_user_props(user)
58
+ expect(result).to eq user_with_all_attrs_hidden
75
59
  end
76
60
 
77
61
  it "hides some attributes if private_attribute_names is set" do
78
- es = LaunchDarkly::EventSerializer.new(config_with_some_attrs_private)
79
- event = make_event(user)
80
- j = es.serialize_events([event])
81
- expect(parse_results(j)).to eq [make_event(user_with_some_attrs_hidden)]
62
+ uf = LaunchDarkly::UserFilter.new(config_with_some_attrs_private)
63
+ result = uf.transform_user_props(user)
64
+ expect(result).to eq user_with_some_attrs_hidden
82
65
  end
83
66
 
84
67
  it "hides attributes specified in per-user privateAttrs" do
85
- es = LaunchDarkly::EventSerializer.new(base_config)
86
- event = make_event(user_specifying_own_private_attr)
87
- j = es.serialize_events([event])
88
- expect(parse_results(j)).to eq [make_event(user_with_own_specified_attr_hidden)]
68
+ uf = LaunchDarkly::UserFilter.new(base_config)
69
+ result = uf.transform_user_props(user_specifying_own_private_attr)
70
+ expect(result).to eq user_with_own_specified_attr_hidden
89
71
  end
90
72
 
91
73
  it "looks at both per-user privateAttrs and global config" do
92
- es = LaunchDarkly::EventSerializer.new(config_with_some_attrs_private)
93
- event = make_event(user_specifying_own_private_attr)
94
- j = es.serialize_events([event])
95
- expect(parse_results(j)).to eq [make_event(user_with_all_attrs_hidden)]
74
+ uf = LaunchDarkly::UserFilter.new(config_with_some_attrs_private)
75
+ result = uf.transform_user_props(user_specifying_own_private_attr)
76
+ expect(result).to eq user_with_all_attrs_hidden
96
77
  end
97
78
 
98
79
  it "strips out any unknown top-level attributes" do
99
- es = LaunchDarkly::EventSerializer.new(base_config)
100
- event = make_event(user_with_unknown_top_level_attrs)
101
- j = es.serialize_events([event])
102
- expect(parse_results(j)).to eq [make_event(user)]
80
+ uf = LaunchDarkly::UserFilter.new(base_config)
81
+ result = uf.transform_user_props(user_with_unknown_top_level_attrs)
82
+ expect(result).to eq user
103
83
  end
104
84
 
105
85
  it "leaves the anonymous attribute as is" do
106
- es = LaunchDarkly::EventSerializer.new(config_with_all_attrs_private)
107
- event = make_event(anon_user)
108
- j = es.serialize_events([event])
109
- expect(parse_results(j)).to eq [make_event(anon_user_with_all_attrs_hidden)]
86
+ uf = LaunchDarkly::UserFilter.new(config_with_all_attrs_private)
87
+ result = uf.transform_user_props(anon_user)
88
+ expect(result).to eq anon_user_with_all_attrs_hidden
110
89
  end
111
90
  end
112
91
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ldclient-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.3
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - LaunchDarkly
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-23 00:00:00.000000000 Z
11
+ date: 2018-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.7'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '10.0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '10.0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: rspec
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -95,19 +81,47 @@ dependencies:
95
81
  - !ruby/object:Gem::Version
96
82
  version: 2.1.2
97
83
  - !ruby/object:Gem::Dependency
98
- name: moneta
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec_junit_formatter
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.3.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.3.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: timecop
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: 1.0.0
117
+ version: 0.9.1
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
- version: 1.0.0
124
+ version: 0.9.1
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: json
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -302,6 +316,7 @@ extensions:
302
316
  - ext/mkrf_conf.rb
303
317
  extra_rdoc_files: []
304
318
  files:
319
+ - ".circleci/config.yml"
305
320
  - ".gitignore"
306
321
  - ".hound.yml"
307
322
  - ".rspec"
@@ -314,28 +329,33 @@ files:
314
329
  - LICENSE.txt
315
330
  - README.md
316
331
  - Rakefile
317
- - circle.yml
318
332
  - ext/mkrf_conf.rb
319
333
  - ldclient-rb.gemspec
320
334
  - lib/ldclient-rb.rb
321
335
  - lib/ldclient-rb/cache_store.rb
322
336
  - lib/ldclient-rb/config.rb
323
337
  - lib/ldclient-rb/evaluation.rb
324
- - lib/ldclient-rb/event_serializer.rb
338
+ - lib/ldclient-rb/event_summarizer.rb
325
339
  - lib/ldclient-rb/events.rb
340
+ - lib/ldclient-rb/expiring_cache.rb
326
341
  - lib/ldclient-rb/in_memory_store.rb
327
342
  - lib/ldclient-rb/ldclient.rb
328
343
  - lib/ldclient-rb/memoized_value.rb
329
344
  - lib/ldclient-rb/newrelic.rb
345
+ - lib/ldclient-rb/non_blocking_thread_pool.rb
330
346
  - lib/ldclient-rb/polling.rb
331
347
  - lib/ldclient-rb/redis_store.rb
332
348
  - lib/ldclient-rb/requestor.rb
349
+ - lib/ldclient-rb/simple_lru_cache.rb
333
350
  - lib/ldclient-rb/stream.rb
351
+ - lib/ldclient-rb/user_filter.rb
334
352
  - lib/ldclient-rb/version.rb
335
353
  - scripts/release.sh
336
354
  - spec/config_spec.rb
337
355
  - spec/evaluation_spec.rb
338
- - spec/event_serializer_spec.rb
356
+ - spec/event_summarizer_spec.rb
357
+ - spec/events_spec.rb
358
+ - spec/expiring_cache_spec.rb
339
359
  - spec/feature_store_spec_base.rb
340
360
  - spec/fixtures/feature.json
341
361
  - spec/fixtures/feature1.json
@@ -348,9 +368,11 @@ files:
348
368
  - spec/redis_feature_store_spec.rb
349
369
  - spec/requestor_spec.rb
350
370
  - spec/segment_store_spec_base.rb
371
+ - spec/simple_lru_cache_spec.rb
351
372
  - spec/spec_helper.rb
352
373
  - spec/store_spec.rb
353
374
  - spec/stream_spec.rb
375
+ - spec/user_filter_spec.rb
354
376
  - spec/version_spec.rb
355
377
  homepage: https://github.com/launchdarkly/ruby-client
356
378
  licenses:
@@ -379,7 +401,9 @@ summary: LaunchDarkly SDK for Ruby
379
401
  test_files:
380
402
  - spec/config_spec.rb
381
403
  - spec/evaluation_spec.rb
382
- - spec/event_serializer_spec.rb
404
+ - spec/event_summarizer_spec.rb
405
+ - spec/events_spec.rb
406
+ - spec/expiring_cache_spec.rb
383
407
  - spec/feature_store_spec_base.rb
384
408
  - spec/fixtures/feature.json
385
409
  - spec/fixtures/feature1.json
@@ -392,7 +416,9 @@ test_files:
392
416
  - spec/redis_feature_store_spec.rb
393
417
  - spec/requestor_spec.rb
394
418
  - spec/segment_store_spec_base.rb
419
+ - spec/simple_lru_cache_spec.rb
395
420
  - spec/spec_helper.rb
396
421
  - spec/store_spec.rb
397
422
  - spec/stream_spec.rb
423
+ - spec/user_filter_spec.rb
398
424
  - spec/version_spec.rb