ldclient-rb 2.5.0 → 3.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,95 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.shared_examples "segment_store" do |create_store_method|
4
+
5
+ let(:segment0) {
6
+ {
7
+ key: "test-segment",
8
+ version: 11,
9
+ salt: "718ea30a918a4eba8734b57ab1a93227",
10
+ rules: []
11
+ }
12
+ }
13
+ let(:key0) { segment0[:key].to_sym }
14
+
15
+ let!(:store) do
16
+ s = create_store_method.call()
17
+ s.init({ key0 => segment0 })
18
+ s
19
+ end
20
+
21
+ def new_version_plus(f, deltaVersion, attrs = {})
22
+ f1 = f.clone
23
+ f1[:version] = f[:version] + deltaVersion
24
+ f1.update(attrs)
25
+ f1
26
+ end
27
+
28
+
29
+ it "is initialized" do
30
+ expect(store.initialized?).to eq true
31
+ end
32
+
33
+ it "can get existing feature with symbol key" do
34
+ expect(store.get(key0)).to eq segment0
35
+ end
36
+
37
+ it "can get existing feature with string key" do
38
+ expect(store.get(key0.to_s)).to eq segment0
39
+ end
40
+
41
+ it "gets nil for nonexisting feature" do
42
+ expect(store.get('nope')).to be_nil
43
+ end
44
+
45
+ it "can get all features" do
46
+ feature1 = segment0.clone
47
+ feature1[:key] = "test-feature-flag1"
48
+ feature1[:version] = 5
49
+ feature1[:on] = false
50
+ store.upsert(:"test-feature-flag1", feature1)
51
+ expect(store.all).to eq ({ key0 => segment0, :"test-feature-flag1" => feature1 })
52
+ end
53
+
54
+ it "can add new feature" do
55
+ feature1 = segment0.clone
56
+ feature1[:key] = "test-feature-flag1"
57
+ feature1[:version] = 5
58
+ feature1[:on] = false
59
+ store.upsert(:"test-feature-flag1", feature1)
60
+ expect(store.get(:"test-feature-flag1")).to eq feature1
61
+ end
62
+
63
+ it "can update feature with newer version" do
64
+ f1 = new_version_plus(segment0, 1, { on: !segment0[:on] })
65
+ store.upsert(key0, f1)
66
+ expect(store.get(key0)).to eq f1
67
+ end
68
+
69
+ it "cannot update feature with same version" do
70
+ f1 = new_version_plus(segment0, 0, { on: !segment0[:on] })
71
+ store.upsert(key0, f1)
72
+ expect(store.get(key0)).to eq segment0
73
+ end
74
+
75
+ it "cannot update feature with older version" do
76
+ f1 = new_version_plus(segment0, -1, { on: !segment0[:on] })
77
+ store.upsert(key0, f1)
78
+ expect(store.get(key0)).to eq segment0
79
+ end
80
+
81
+ it "can delete feature with newer version" do
82
+ store.delete(key0, segment0[:version] + 1)
83
+ expect(store.get(key0)).to be_nil
84
+ end
85
+
86
+ it "cannot delete feature with same version" do
87
+ store.delete(key0, segment0[:version])
88
+ expect(store.get(key0)).to eq segment0
89
+ end
90
+
91
+ it "cannot delete feature with older version" do
92
+ store.delete(key0, segment0[:version] - 1)
93
+ expect(store.get(key0)).to eq segment0
94
+ end
95
+ end
@@ -3,20 +3,23 @@ require 'ostruct'
3
3
 
4
4
  describe LaunchDarkly::InMemoryFeatureStore do
5
5
  subject { LaunchDarkly::InMemoryFeatureStore }
6
+
7
+ include LaunchDarkly
8
+
6
9
  let(:store) { subject.new }
7
10
  let(:key) { :asdf }
8
- let(:feature) { { value: "qwer", version: 0 } }
11
+ let(:feature) { { key: "asdf", value: "qwer", version: 0 } }
9
12
 
10
13
  describe '#all' do
11
14
  it "will get all keys" do
12
- store.upsert(key, feature)
13
- data = store.all
15
+ store.upsert(LaunchDarkly::FEATURES, feature)
16
+ data = store.all(LaunchDarkly::FEATURES)
14
17
  expect(data).to eq(key => feature)
15
18
  end
16
19
  it "will not get deleted keys" do
17
- store.upsert(key, feature)
18
- store.delete(key, 1)
19
- data = store.all
20
+ store.upsert(LaunchDarkly::FEATURES, feature)
21
+ store.delete(LaunchDarkly::FEATURES, key, 1)
22
+ data = store.all(LaunchDarkly::FEATURES)
20
23
  expect(data).to eq({})
21
24
  end
22
25
  end
@@ -37,21 +40,33 @@ describe LaunchDarkly::StreamProcessor do
37
40
  let(:processor) { subject.new("sdk_key", config, requestor) }
38
41
 
39
42
  describe '#process_message' do
40
- let(:put_message) { OpenStruct.new({data: '{"key": {"value": "asdf"}}'}) }
41
- let(:patch_message) { OpenStruct.new({data: '{"path": "akey", "data": {"value": "asdf", "version": 1}}'}) }
42
- let(:delete_message) { OpenStruct.new({data: '{"path": "akey", "version": 2}'}) }
43
+ let(:put_message) { OpenStruct.new({data: '{"data":{"flags":{"asdf": {"key": "asdf"}},"segments":{"segkey": {"key": "segkey"}}}}'}) }
44
+ let(:patch_flag_message) { OpenStruct.new({data: '{"path": "/flags/key", "data": {"key": "asdf", "version": 1}}'}) }
45
+ let(:patch_seg_message) { OpenStruct.new({data: '{"path": "/segments/key", "data": {"key": "asdf", "version": 1}}'}) }
46
+ let(:delete_flag_message) { OpenStruct.new({data: '{"path": "/flags/key", "version": 2}'}) }
47
+ let(:delete_seg_message) { OpenStruct.new({data: '{"path": "/segments/key", "version": 2}'}) }
43
48
  it "will accept PUT methods" do
44
49
  processor.send(:process_message, put_message, LaunchDarkly::PUT)
45
- expect(processor.instance_variable_get(:@store).get("key")).to eq(value: "asdf")
50
+ expect(config.feature_store.get(LaunchDarkly::FEATURES, "asdf")).to eq(key: "asdf")
51
+ expect(config.feature_store.get(LaunchDarkly::SEGMENTS, "segkey")).to eq(key: "segkey")
52
+ end
53
+ it "will accept PATCH methods for flags" do
54
+ processor.send(:process_message, patch_flag_message, LaunchDarkly::PATCH)
55
+ expect(config.feature_store.get(LaunchDarkly::FEATURES, "asdf")).to eq(key: "asdf", version: 1)
56
+ end
57
+ it "will accept PATCH methods for segments" do
58
+ processor.send(:process_message, patch_seg_message, LaunchDarkly::PATCH)
59
+ expect(config.feature_store.get(LaunchDarkly::SEGMENTS, "asdf")).to eq(key: "asdf", version: 1)
46
60
  end
47
- it "will accept PATCH methods" do
48
- processor.send(:process_message, patch_message, LaunchDarkly::PATCH)
49
- expect(processor.instance_variable_get(:@store).get("key")).to eq(value: "asdf", version: 1)
61
+ it "will accept DELETE methods for flags" do
62
+ processor.send(:process_message, patch_flag_message, LaunchDarkly::PATCH)
63
+ processor.send(:process_message, delete_flag_message, LaunchDarkly::DELETE)
64
+ expect(config.feature_store.get(LaunchDarkly::FEATURES, "key")).to eq(nil)
50
65
  end
51
- it "will accept DELETE methods" do
52
- processor.send(:process_message, patch_message, LaunchDarkly::PATCH)
53
- processor.send(:process_message, delete_message, LaunchDarkly::DELETE)
54
- expect(processor.instance_variable_get(:@store).get("key")).to eq(nil)
66
+ it "will accept DELETE methods for segments" do
67
+ processor.send(:process_message, patch_seg_message, LaunchDarkly::PATCH)
68
+ processor.send(:process_message, delete_seg_message, LaunchDarkly::DELETE)
69
+ expect(config.feature_store.get(LaunchDarkly::SEGMENTS, "key")).to eq(nil)
55
70
  end
56
71
  it "will log a warning if the method is not recognized" do
57
72
  expect(processor.instance_variable_get(:@config).logger).to receive :warn
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: 2.5.0
4
+ version: 3.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-02-13 00:00:00.000000000 Z
11
+ date: 2018-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -323,12 +323,12 @@ files:
323
323
  - lib/ldclient-rb/evaluation.rb
324
324
  - lib/ldclient-rb/event_serializer.rb
325
325
  - lib/ldclient-rb/events.rb
326
- - lib/ldclient-rb/feature_store.rb
326
+ - lib/ldclient-rb/in_memory_store.rb
327
327
  - lib/ldclient-rb/ldclient.rb
328
328
  - lib/ldclient-rb/memoized_value.rb
329
329
  - lib/ldclient-rb/newrelic.rb
330
330
  - lib/ldclient-rb/polling.rb
331
- - lib/ldclient-rb/redis_feature_store.rb
331
+ - lib/ldclient-rb/redis_store.rb
332
332
  - lib/ldclient-rb/requestor.rb
333
333
  - lib/ldclient-rb/stream.rb
334
334
  - lib/ldclient-rb/version.rb
@@ -347,6 +347,7 @@ files:
347
347
  - spec/newrelic_spec.rb
348
348
  - spec/redis_feature_store_spec.rb
349
349
  - spec/requestor_spec.rb
350
+ - spec/segment_store_spec_base.rb
350
351
  - spec/spec_helper.rb
351
352
  - spec/store_spec.rb
352
353
  - spec/stream_spec.rb
@@ -390,6 +391,7 @@ test_files:
390
391
  - spec/newrelic_spec.rb
391
392
  - spec/redis_feature_store_spec.rb
392
393
  - spec/requestor_spec.rb
394
+ - spec/segment_store_spec_base.rb
393
395
  - spec/spec_helper.rb
394
396
  - spec/store_spec.rb
395
397
  - spec/stream_spec.rb
@@ -1,63 +0,0 @@
1
- require "concurrent/atomics"
2
-
3
- module LaunchDarkly
4
- class InMemoryFeatureStore
5
- def initialize
6
- @features = Hash.new
7
- @lock = Concurrent::ReadWriteLock.new
8
- @initialized = Concurrent::AtomicBoolean.new(false)
9
- end
10
-
11
- def get(key)
12
- @lock.with_read_lock do
13
- f = @features[key.to_sym]
14
- (f.nil? || f[:deleted]) ? nil : f
15
- end
16
- end
17
-
18
- def all
19
- @lock.with_read_lock do
20
- @features.select { |_k, f| not f[:deleted] }
21
- end
22
- end
23
-
24
- def delete(key, version)
25
- @lock.with_write_lock do
26
- old = @features[key.to_sym]
27
-
28
- if !old.nil? && old[:version] < version
29
- old[:deleted] = true
30
- old[:version] = version
31
- @features[key.to_sym] = old
32
- elsif old.nil?
33
- @features[key.to_sym] = { deleted: true, version: version }
34
- end
35
- end
36
- end
37
-
38
- def init(fs)
39
- @lock.with_write_lock do
40
- @features.replace(fs)
41
- @initialized.make_true
42
- end
43
- end
44
-
45
- def upsert(key, feature)
46
- @lock.with_write_lock do
47
- old = @features[key.to_sym]
48
-
49
- if old.nil? || old[:version] < feature[:version]
50
- @features[key.to_sym] = feature
51
- end
52
- end
53
- end
54
-
55
- def initialized?
56
- @initialized.value
57
- end
58
-
59
- def stop
60
- # nothing to do
61
- end
62
- end
63
- end