launchdarkly-server-sdk 8.8.3-java
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 +7 -0
- data/LICENSE.txt +13 -0
- data/README.md +61 -0
- data/lib/launchdarkly-server-sdk.rb +1 -0
- data/lib/ldclient-rb/cache_store.rb +45 -0
- data/lib/ldclient-rb/config.rb +658 -0
- data/lib/ldclient-rb/context.rb +565 -0
- data/lib/ldclient-rb/evaluation_detail.rb +387 -0
- data/lib/ldclient-rb/events.rb +642 -0
- data/lib/ldclient-rb/expiring_cache.rb +77 -0
- data/lib/ldclient-rb/flags_state.rb +88 -0
- data/lib/ldclient-rb/impl/big_segments.rb +117 -0
- data/lib/ldclient-rb/impl/broadcaster.rb +78 -0
- data/lib/ldclient-rb/impl/context.rb +96 -0
- data/lib/ldclient-rb/impl/context_filter.rb +166 -0
- data/lib/ldclient-rb/impl/data_source.rb +188 -0
- data/lib/ldclient-rb/impl/data_store.rb +109 -0
- data/lib/ldclient-rb/impl/dependency_tracker.rb +102 -0
- data/lib/ldclient-rb/impl/diagnostic_events.rb +129 -0
- data/lib/ldclient-rb/impl/evaluation_with_hook_result.rb +34 -0
- data/lib/ldclient-rb/impl/evaluator.rb +539 -0
- data/lib/ldclient-rb/impl/evaluator_bucketing.rb +86 -0
- data/lib/ldclient-rb/impl/evaluator_helpers.rb +50 -0
- data/lib/ldclient-rb/impl/evaluator_operators.rb +131 -0
- data/lib/ldclient-rb/impl/event_sender.rb +100 -0
- data/lib/ldclient-rb/impl/event_summarizer.rb +68 -0
- data/lib/ldclient-rb/impl/event_types.rb +136 -0
- data/lib/ldclient-rb/impl/flag_tracker.rb +58 -0
- data/lib/ldclient-rb/impl/integrations/consul_impl.rb +170 -0
- data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +300 -0
- data/lib/ldclient-rb/impl/integrations/file_data_source.rb +229 -0
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +306 -0
- data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +40 -0
- data/lib/ldclient-rb/impl/migrations/migrator.rb +287 -0
- data/lib/ldclient-rb/impl/migrations/tracker.rb +136 -0
- data/lib/ldclient-rb/impl/model/clause.rb +45 -0
- data/lib/ldclient-rb/impl/model/feature_flag.rb +254 -0
- data/lib/ldclient-rb/impl/model/preprocessed_data.rb +64 -0
- data/lib/ldclient-rb/impl/model/segment.rb +132 -0
- data/lib/ldclient-rb/impl/model/serialization.rb +72 -0
- data/lib/ldclient-rb/impl/repeating_task.rb +46 -0
- data/lib/ldclient-rb/impl/sampler.rb +25 -0
- data/lib/ldclient-rb/impl/store_client_wrapper.rb +141 -0
- data/lib/ldclient-rb/impl/store_data_set_sorter.rb +55 -0
- data/lib/ldclient-rb/impl/unbounded_pool.rb +34 -0
- data/lib/ldclient-rb/impl/util.rb +95 -0
- data/lib/ldclient-rb/impl.rb +13 -0
- data/lib/ldclient-rb/in_memory_store.rb +100 -0
- data/lib/ldclient-rb/integrations/consul.rb +45 -0
- data/lib/ldclient-rb/integrations/dynamodb.rb +92 -0
- data/lib/ldclient-rb/integrations/file_data.rb +108 -0
- data/lib/ldclient-rb/integrations/redis.rb +98 -0
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +663 -0
- data/lib/ldclient-rb/integrations/test_data.rb +213 -0
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +246 -0
- data/lib/ldclient-rb/integrations.rb +6 -0
- data/lib/ldclient-rb/interfaces.rb +974 -0
- data/lib/ldclient-rb/ldclient.rb +822 -0
- data/lib/ldclient-rb/memoized_value.rb +32 -0
- data/lib/ldclient-rb/migrations.rb +230 -0
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +46 -0
- data/lib/ldclient-rb/polling.rb +102 -0
- data/lib/ldclient-rb/reference.rb +295 -0
- data/lib/ldclient-rb/requestor.rb +102 -0
- data/lib/ldclient-rb/simple_lru_cache.rb +25 -0
- data/lib/ldclient-rb/stream.rb +196 -0
- data/lib/ldclient-rb/util.rb +132 -0
- data/lib/ldclient-rb/version.rb +3 -0
- data/lib/ldclient-rb.rb +27 -0
- metadata +400 -0
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'ldclient-rb/impl/integrations/test_data/test_data_source'
|
2
|
+
require 'ldclient-rb/impl/model/feature_flag'
|
3
|
+
require 'ldclient-rb/impl/model/segment'
|
4
|
+
require 'ldclient-rb/integrations/test_data/flag_builder'
|
5
|
+
|
6
|
+
require 'concurrent/atomics'
|
7
|
+
|
8
|
+
module LaunchDarkly
|
9
|
+
module Integrations
|
10
|
+
#
|
11
|
+
# A mechanism for providing dynamically updatable feature flag state in a simplified form to an SDK
|
12
|
+
# client in test scenarios.
|
13
|
+
#
|
14
|
+
# Unlike {LaunchDarkly::Integrations::FileData}, this mechanism does not use any external resources. It
|
15
|
+
# provides only the data that the application has put into it using the {#update} method.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# td = LaunchDarkly::Integrations::TestData.data_source
|
19
|
+
# td.update(td.flag("flag-key-1").variation_for_all(true))
|
20
|
+
# config = LaunchDarkly::Config.new(data_source: td)
|
21
|
+
# client = LaunchDarkly::LDClient.new('sdkKey', config)
|
22
|
+
# # flags can be updated at any time:
|
23
|
+
# td.update(td.flag("flag-key-2")
|
24
|
+
# .variation_for_key("user", some-user-key", true)
|
25
|
+
# .fallthrough_variation(false))
|
26
|
+
#
|
27
|
+
# The above example uses a simple boolean flag, but more complex configurations are possible using
|
28
|
+
# the methods of the {FlagBuilder} that is returned by {#flag}. {FlagBuilder}
|
29
|
+
# supports many of the ways a flag can be configured on the LaunchDarkly dashboard, but does not
|
30
|
+
# currently support 1. rule operators other than "in" and "not in", or 2. percentage rollouts.
|
31
|
+
#
|
32
|
+
# If the same `TestData` instance is used to configure multiple `LDClient` instances,
|
33
|
+
# any changes made to the data will propagate to all of the `LDClient`s.
|
34
|
+
#
|
35
|
+
# @since 6.3.0
|
36
|
+
#
|
37
|
+
class TestData
|
38
|
+
# Creates a new instance of the test data source.
|
39
|
+
#
|
40
|
+
# @return [TestData] a new configurable test data source
|
41
|
+
def self.data_source
|
42
|
+
self.new
|
43
|
+
end
|
44
|
+
|
45
|
+
# @private
|
46
|
+
def initialize
|
47
|
+
@flag_builders = Hash.new
|
48
|
+
@current_flags = Hash.new
|
49
|
+
@current_segments = Hash.new
|
50
|
+
@instances = Array.new
|
51
|
+
@instances_lock = Concurrent::ReadWriteLock.new
|
52
|
+
@lock = Concurrent::ReadWriteLock.new
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Called internally by the SDK to determine what arguments to pass to call
|
57
|
+
# You do not need to call this method.
|
58
|
+
#
|
59
|
+
# @private
|
60
|
+
def arity
|
61
|
+
2
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Called internally by the SDK to associate this test data source with an {@code LDClient} instance.
|
66
|
+
# You do not need to call this method.
|
67
|
+
#
|
68
|
+
# @private
|
69
|
+
def call(_, config)
|
70
|
+
impl = LaunchDarkly::Impl::Integrations::TestData::TestDataSource.new(config.feature_store, self)
|
71
|
+
@instances_lock.with_write_lock { @instances.push(impl) }
|
72
|
+
impl
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Creates or copies a {FlagBuilder} for building a test flag configuration.
|
77
|
+
#
|
78
|
+
# If this flag key has already been defined in this `TestData` instance, then the builder
|
79
|
+
# starts with the same configuration that was last provided for this flag.
|
80
|
+
#
|
81
|
+
# Otherwise, it starts with a new default configuration in which the flag has `true` and
|
82
|
+
# `false` variations, is `true` for all contexts when targeting is turned on and
|
83
|
+
# `false` otherwise, and currently has targeting turned on. You can change any of those
|
84
|
+
# properties, and provide more complex behavior, using the {FlagBuilder} methods.
|
85
|
+
#
|
86
|
+
# Once you have set the desired configuration, pass the builder to {#update}.
|
87
|
+
#
|
88
|
+
# @param key [String] the flag key
|
89
|
+
# @return [FlagBuilder] a flag configuration builder
|
90
|
+
#
|
91
|
+
def flag(key)
|
92
|
+
existing_builder = @lock.with_read_lock { @flag_builders[key] }
|
93
|
+
if existing_builder.nil?
|
94
|
+
FlagBuilder.new(key).boolean_flag
|
95
|
+
else
|
96
|
+
existing_builder.clone
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Updates the test data with the specified flag configuration.
|
102
|
+
#
|
103
|
+
# This has the same effect as if a flag were added or modified on the LaunchDarkly dashboard.
|
104
|
+
# It immediately propagates the flag change to any `LDClient` instance(s) that you have
|
105
|
+
# already configured to use this `TestData`. If no `LDClient` has been started yet,
|
106
|
+
# it simply adds this flag to the test data which will be provided to any `LDClient` that
|
107
|
+
# you subsequently configure.
|
108
|
+
#
|
109
|
+
# Any subsequent changes to this {FlagBuilder} instance do not affect the test data,
|
110
|
+
# unless you call {#update} again.
|
111
|
+
#
|
112
|
+
# @param flag_builder [FlagBuilder] a flag configuration builder
|
113
|
+
# @return [TestData] the TestData instance
|
114
|
+
#
|
115
|
+
def update(flag_builder)
|
116
|
+
new_flag = nil
|
117
|
+
@lock.with_write_lock do
|
118
|
+
@flag_builders[flag_builder.key] = flag_builder
|
119
|
+
version = 0
|
120
|
+
flag_key = flag_builder.key.to_sym
|
121
|
+
if @current_flags[flag_key]
|
122
|
+
version = @current_flags[flag_key][:version]
|
123
|
+
end
|
124
|
+
new_flag = Impl::Model.deserialize(FEATURES, flag_builder.build(version+1))
|
125
|
+
@current_flags[flag_key] = new_flag
|
126
|
+
end
|
127
|
+
update_item(FEATURES, new_flag)
|
128
|
+
self
|
129
|
+
end
|
130
|
+
|
131
|
+
#
|
132
|
+
# Copies a full feature flag data model object into the test data.
|
133
|
+
#
|
134
|
+
# It immediately propagates the flag change to any `LDClient` instance(s) that you have already
|
135
|
+
# configured to use this `TestData`. If no `LDClient` has been started yet, it simply adds
|
136
|
+
# this flag to the test data which will be provided to any LDClient that you subsequently
|
137
|
+
# configure.
|
138
|
+
#
|
139
|
+
# Use this method if you need to use advanced flag configuration properties that are not supported by
|
140
|
+
# the simplified {FlagBuilder} API. Otherwise it is recommended to use the regular {flag}/{update}
|
141
|
+
# mechanism to avoid dependencies on details of the data model.
|
142
|
+
#
|
143
|
+
# You cannot make incremental changes with {flag}/{update} to a flag that has been added in this way;
|
144
|
+
# you can only replace it with an entirely new flag configuration.
|
145
|
+
#
|
146
|
+
# @param flag [Hash] the flag configuration
|
147
|
+
# @return [TestData] the TestData instance
|
148
|
+
#
|
149
|
+
def use_preconfigured_flag(flag)
|
150
|
+
use_preconfigured_item(FEATURES, flag, @current_flags)
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
# Copies a full segment data model object into the test data.
|
155
|
+
#
|
156
|
+
# It immediately propagates the change to any `LDClient` instance(s) that you have already
|
157
|
+
# configured to use this `TestData`. If no `LDClient` has been started yet, it simply adds
|
158
|
+
# this segment to the test data which will be provided to any LDClient that you subsequently
|
159
|
+
# configure.
|
160
|
+
#
|
161
|
+
# This method is currently the only way to inject segment data, since there is no builder
|
162
|
+
# API for segments. It is mainly intended for the SDK's own tests of segment functionality,
|
163
|
+
# since application tests that need to produce a desired evaluation state could do so more easily
|
164
|
+
# by just setting flag values.
|
165
|
+
#
|
166
|
+
# @param segment [Hash] the segment configuration
|
167
|
+
# @return [TestData] the TestData instance
|
168
|
+
#
|
169
|
+
def use_preconfigured_segment(segment)
|
170
|
+
use_preconfigured_item(SEGMENTS, segment, @current_segments)
|
171
|
+
end
|
172
|
+
|
173
|
+
private def use_preconfigured_item(kind, item, current)
|
174
|
+
item = Impl::Model.deserialize(kind, item)
|
175
|
+
key = item.key.to_sym
|
176
|
+
@lock.with_write_lock do
|
177
|
+
old_item = current[key]
|
178
|
+
unless old_item.nil?
|
179
|
+
data = item.as_json
|
180
|
+
data[:version] = old_item.version + 1
|
181
|
+
item = Impl::Model.deserialize(kind, data)
|
182
|
+
end
|
183
|
+
current[key] = item
|
184
|
+
end
|
185
|
+
update_item(kind, item)
|
186
|
+
self
|
187
|
+
end
|
188
|
+
|
189
|
+
private def update_item(kind, item)
|
190
|
+
@instances_lock.with_read_lock do
|
191
|
+
@instances.each do | instance |
|
192
|
+
instance.upsert(kind, item)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# @private
|
198
|
+
def make_init_data
|
199
|
+
@lock.with_read_lock do
|
200
|
+
{
|
201
|
+
FEATURES => @current_flags.clone,
|
202
|
+
SEGMENTS => @current_segments.clone,
|
203
|
+
}
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# @private
|
208
|
+
def closed_instance(instance)
|
209
|
+
@instances_lock.with_write_lock { @instances.delete(instance) }
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
require "concurrent/atomics"
|
2
|
+
|
3
|
+
require "ldclient-rb/expiring_cache"
|
4
|
+
|
5
|
+
module LaunchDarkly
|
6
|
+
module Integrations
|
7
|
+
#
|
8
|
+
# Support code that may be helpful in creating integrations.
|
9
|
+
#
|
10
|
+
# @since 5.5.0
|
11
|
+
#
|
12
|
+
module Util
|
13
|
+
#
|
14
|
+
# CachingStoreWrapper is a partial implementation of the {LaunchDarkly::Interfaces::FeatureStore}
|
15
|
+
# pattern that delegates part of its behavior to another object, while providing optional caching
|
16
|
+
# behavior and other logic that would otherwise be repeated in every feature store implementation.
|
17
|
+
# This makes it easier to create new database integrations by implementing only the database-specific
|
18
|
+
# logic.
|
19
|
+
#
|
20
|
+
# The mixin {FeatureStoreCore} describes the methods that need to be supported by the inner
|
21
|
+
# implementation object.
|
22
|
+
#
|
23
|
+
class CachingStoreWrapper
|
24
|
+
include LaunchDarkly::Interfaces::FeatureStore
|
25
|
+
|
26
|
+
#
|
27
|
+
# Creates a new store wrapper instance.
|
28
|
+
#
|
29
|
+
# @param core [Object] an object that implements the {FeatureStoreCore} methods
|
30
|
+
# @param opts [Hash] a hash that may include cache-related options; all others will be ignored
|
31
|
+
# @option opts [Float] :expiration (15) cache TTL; zero means no caching
|
32
|
+
# @option opts [Integer] :capacity (1000) maximum number of items in the cache
|
33
|
+
#
|
34
|
+
def initialize(core, opts)
|
35
|
+
@core = core
|
36
|
+
|
37
|
+
expiration_seconds = opts[:expiration] || 15
|
38
|
+
if expiration_seconds > 0
|
39
|
+
capacity = opts[:capacity] || 1000
|
40
|
+
@cache = ExpiringCache.new(capacity, expiration_seconds)
|
41
|
+
else
|
42
|
+
@cache = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
@inited = Concurrent::AtomicBoolean.new(false)
|
46
|
+
@has_available_method = @core.respond_to? :available?
|
47
|
+
end
|
48
|
+
|
49
|
+
def monitoring_enabled?
|
50
|
+
@has_available_method
|
51
|
+
end
|
52
|
+
|
53
|
+
def available?
|
54
|
+
return false unless @has_available_method
|
55
|
+
|
56
|
+
@core.available?
|
57
|
+
end
|
58
|
+
|
59
|
+
def init(all_data)
|
60
|
+
@core.init_internal(all_data)
|
61
|
+
@inited.make_true
|
62
|
+
|
63
|
+
unless @cache.nil?
|
64
|
+
@cache.clear
|
65
|
+
all_data.each do |kind, items|
|
66
|
+
@cache[kind] = items_if_not_deleted(items)
|
67
|
+
items.each do |key, item|
|
68
|
+
@cache[item_cache_key(kind, key)] = [item]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def get(kind, key)
|
75
|
+
unless @cache.nil?
|
76
|
+
cache_key = item_cache_key(kind, key)
|
77
|
+
cached = @cache[cache_key] # note, item entries in the cache are wrapped in an array so we can cache nil values
|
78
|
+
return item_if_not_deleted(cached[0]) unless cached.nil?
|
79
|
+
end
|
80
|
+
|
81
|
+
item = @core.get_internal(kind, key)
|
82
|
+
|
83
|
+
unless @cache.nil?
|
84
|
+
@cache[cache_key] = [item]
|
85
|
+
end
|
86
|
+
|
87
|
+
item_if_not_deleted(item)
|
88
|
+
end
|
89
|
+
|
90
|
+
def all(kind)
|
91
|
+
unless @cache.nil?
|
92
|
+
items = @cache[all_cache_key(kind)]
|
93
|
+
return items unless items.nil?
|
94
|
+
end
|
95
|
+
|
96
|
+
items = items_if_not_deleted(@core.get_all_internal(kind))
|
97
|
+
@cache[all_cache_key(kind)] = items unless @cache.nil?
|
98
|
+
items
|
99
|
+
end
|
100
|
+
|
101
|
+
def upsert(kind, item)
|
102
|
+
new_state = @core.upsert_internal(kind, item)
|
103
|
+
|
104
|
+
unless @cache.nil?
|
105
|
+
@cache[item_cache_key(kind, item[:key])] = [new_state]
|
106
|
+
@cache.delete(all_cache_key(kind))
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def delete(kind, key, version)
|
111
|
+
upsert(kind, { key: key, version: version, deleted: true })
|
112
|
+
end
|
113
|
+
|
114
|
+
def initialized?
|
115
|
+
return true if @inited.value
|
116
|
+
|
117
|
+
if @cache.nil?
|
118
|
+
result = @core.initialized_internal?
|
119
|
+
else
|
120
|
+
result = @cache[inited_cache_key]
|
121
|
+
if result.nil?
|
122
|
+
result = @core.initialized_internal?
|
123
|
+
@cache[inited_cache_key] = result
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
@inited.make_true if result
|
128
|
+
result
|
129
|
+
end
|
130
|
+
|
131
|
+
def stop
|
132
|
+
@core.stop
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
# We use just one cache for 3 kinds of objects. Individual entities use a key like 'features:my-flag'.
|
138
|
+
def item_cache_key(kind, key)
|
139
|
+
kind[:namespace] + ":" + key.to_s
|
140
|
+
end
|
141
|
+
|
142
|
+
# The result of a call to get_all_internal is cached using the "kind" object as a key.
|
143
|
+
def all_cache_key(kind)
|
144
|
+
kind
|
145
|
+
end
|
146
|
+
|
147
|
+
# The result of initialized_internal? is cached using this key.
|
148
|
+
def inited_cache_key
|
149
|
+
"$inited"
|
150
|
+
end
|
151
|
+
|
152
|
+
def item_if_not_deleted(item)
|
153
|
+
(item.nil? || item[:deleted]) ? nil : item
|
154
|
+
end
|
155
|
+
|
156
|
+
def items_if_not_deleted(items)
|
157
|
+
items.select { |key, item| !item[:deleted] }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
#
|
162
|
+
# This module describes the methods that you must implement on your own object in order to
|
163
|
+
# use {CachingStoreWrapper}.
|
164
|
+
#
|
165
|
+
module FeatureStoreCore
|
166
|
+
#
|
167
|
+
# Initializes the store. This is the same as {LaunchDarkly::Interfaces::FeatureStore#init},
|
168
|
+
# but the wrapper will take care of updating the cache if caching is enabled.
|
169
|
+
#
|
170
|
+
# If possible, the store should update the entire data set atomically. If that is not possible,
|
171
|
+
# it should iterate through the outer hash and then the inner hash using the existing iteration
|
172
|
+
# order of those hashes (the SDK will ensure that the items were inserted into the hashes in
|
173
|
+
# the correct order), storing each item, and then delete any leftover items at the very end.
|
174
|
+
#
|
175
|
+
# @param all_data [Hash] a hash where each key is one of the data kind objects, and each
|
176
|
+
# value is in turn a hash of string keys to entities
|
177
|
+
# @return [void]
|
178
|
+
#
|
179
|
+
def init_internal(all_data)
|
180
|
+
end
|
181
|
+
|
182
|
+
#
|
183
|
+
# Retrieves a single entity. This is the same as {LaunchDarkly::Interfaces::FeatureStore#get}
|
184
|
+
# except that 1. the wrapper will take care of filtering out deleted entities by checking the
|
185
|
+
# `:deleted` property, so you can just return exactly what was in the data store, and 2. the
|
186
|
+
# wrapper will take care of checking and updating the cache if caching is enabled.
|
187
|
+
#
|
188
|
+
# @param kind [Object] the kind of entity to get
|
189
|
+
# @param key [String] the unique key of the entity to get
|
190
|
+
# @return [Hash] the entity; nil if the key was not found
|
191
|
+
#
|
192
|
+
def get_internal(kind, key)
|
193
|
+
end
|
194
|
+
|
195
|
+
#
|
196
|
+
# Retrieves all entities of the specified kind. This is the same as {LaunchDarkly::Interfaces::FeatureStore#all}
|
197
|
+
# except that 1. the wrapper will take care of filtering out deleted entities by checking the
|
198
|
+
# `:deleted` property, so you can just return exactly what was in the data store, and 2. the
|
199
|
+
# wrapper will take care of checking and updating the cache if caching is enabled.
|
200
|
+
#
|
201
|
+
# @param kind [Object] the kind of entity to get
|
202
|
+
# @return [Hash] a hash where each key is the entity's `:key` property and each value
|
203
|
+
# is the entity
|
204
|
+
#
|
205
|
+
def get_all_internal(kind)
|
206
|
+
end
|
207
|
+
|
208
|
+
#
|
209
|
+
# Attempts to add or update an entity. This is the same as {LaunchDarkly::Interfaces::FeatureStore#upsert}
|
210
|
+
# except that 1. the wrapper will take care of updating the cache if caching is enabled, and 2.
|
211
|
+
# the method is expected to return the final state of the entity (i.e. either the `item`
|
212
|
+
# parameter if the update succeeded, or the previously existing entity in the store if the
|
213
|
+
# update failed; this is used for the caching logic).
|
214
|
+
#
|
215
|
+
# Note that FeatureStoreCore does not have a `delete` method. This is because {CachingStoreWrapper}
|
216
|
+
# implements `delete` by simply calling `upsert` with an item whose `:deleted` property is true.
|
217
|
+
#
|
218
|
+
# @param kind [Object] the kind of entity to add or update
|
219
|
+
# @param item [Hash] the entity to add or update
|
220
|
+
# @return [Hash] the entity as it now exists in the store after the update
|
221
|
+
#
|
222
|
+
def upsert_internal(kind, item)
|
223
|
+
end
|
224
|
+
|
225
|
+
#
|
226
|
+
# Checks whether this store has been initialized. This is the same as
|
227
|
+
# {LaunchDarkly::Interfaces::FeatureStore#initialized?} except that there is less of a concern
|
228
|
+
# for efficiency, because the wrapper will use caching and memoization in order to call the method
|
229
|
+
# as little as possible.
|
230
|
+
#
|
231
|
+
# @return [Boolean] true if the store is in an initialized state
|
232
|
+
#
|
233
|
+
def initialized_internal?
|
234
|
+
end
|
235
|
+
|
236
|
+
#
|
237
|
+
# Performs any necessary cleanup to shut down the store when the client is being shut down.
|
238
|
+
#
|
239
|
+
# @return [void]
|
240
|
+
#
|
241
|
+
def stop
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
require "ldclient-rb/integrations/consul"
|
2
|
+
require "ldclient-rb/integrations/dynamodb"
|
3
|
+
require "ldclient-rb/integrations/file_data"
|
4
|
+
require "ldclient-rb/integrations/redis"
|
5
|
+
require "ldclient-rb/integrations/test_data"
|
6
|
+
require "ldclient-rb/integrations/util/store_wrapper"
|