ldclient-rb 5.4.3 → 5.5.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 +4 -4
- data/.circleci/config.yml +33 -6
- data/CHANGELOG.md +19 -0
- data/CONTRIBUTING.md +0 -12
- data/Gemfile.lock +22 -3
- data/README.md +41 -35
- data/ldclient-rb.gemspec +4 -3
- data/lib/ldclient-rb.rb +9 -1
- data/lib/ldclient-rb/cache_store.rb +1 -0
- data/lib/ldclient-rb/config.rb +201 -90
- data/lib/ldclient-rb/evaluation.rb +56 -8
- data/lib/ldclient-rb/event_summarizer.rb +3 -0
- data/lib/ldclient-rb/events.rb +16 -0
- data/lib/ldclient-rb/expiring_cache.rb +1 -0
- data/lib/ldclient-rb/file_data_source.rb +18 -13
- data/lib/ldclient-rb/flags_state.rb +3 -2
- data/lib/ldclient-rb/impl.rb +13 -0
- data/lib/ldclient-rb/impl/integrations/consul_impl.rb +158 -0
- data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +228 -0
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +155 -0
- data/lib/ldclient-rb/impl/store_client_wrapper.rb +47 -0
- data/lib/ldclient-rb/impl/store_data_set_sorter.rb +55 -0
- data/lib/ldclient-rb/in_memory_store.rb +15 -4
- data/lib/ldclient-rb/integrations.rb +55 -0
- data/lib/ldclient-rb/integrations/consul.rb +38 -0
- data/lib/ldclient-rb/integrations/dynamodb.rb +47 -0
- data/lib/ldclient-rb/integrations/redis.rb +55 -0
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +230 -0
- data/lib/ldclient-rb/interfaces.rb +153 -0
- data/lib/ldclient-rb/ldclient.rb +135 -77
- data/lib/ldclient-rb/memoized_value.rb +2 -0
- data/lib/ldclient-rb/newrelic.rb +1 -0
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +3 -3
- data/lib/ldclient-rb/polling.rb +1 -0
- data/lib/ldclient-rb/redis_store.rb +24 -190
- data/lib/ldclient-rb/requestor.rb +3 -2
- data/lib/ldclient-rb/simple_lru_cache.rb +1 -0
- data/lib/ldclient-rb/stream.rb +22 -10
- data/lib/ldclient-rb/user_filter.rb +1 -0
- data/lib/ldclient-rb/util.rb +1 -0
- data/lib/ldclient-rb/version.rb +1 -1
- data/scripts/gendocs.sh +12 -0
- data/spec/feature_store_spec_base.rb +173 -72
- data/spec/file_data_source_spec.rb +2 -2
- data/spec/http_util.rb +103 -0
- data/spec/in_memory_feature_store_spec.rb +1 -1
- data/spec/integrations/consul_feature_store_spec.rb +41 -0
- data/spec/integrations/dynamodb_feature_store_spec.rb +104 -0
- data/spec/integrations/store_wrapper_spec.rb +276 -0
- data/spec/ldclient_spec.rb +83 -4
- data/spec/redis_feature_store_spec.rb +25 -16
- data/spec/requestor_spec.rb +44 -38
- data/spec/stream_spec.rb +18 -18
- metadata +55 -33
- data/lib/sse_client.rb +0 -4
- data/lib/sse_client/backoff.rb +0 -38
- data/lib/sse_client/sse_client.rb +0 -171
- data/lib/sse_client/sse_events.rb +0 -67
- data/lib/sse_client/streaming_http.rb +0 -199
- data/spec/sse_client/sse_client_spec.rb +0 -177
- data/spec/sse_client/sse_events_spec.rb +0 -100
- data/spec/sse_client/sse_shared.rb +0 -82
- data/spec/sse_client/streaming_http_spec.rb +0 -263
@@ -0,0 +1,153 @@
|
|
1
|
+
|
2
|
+
module LaunchDarkly
|
3
|
+
#
|
4
|
+
# Mixins that define the required methods of various pluggable components used by the client.
|
5
|
+
#
|
6
|
+
module Interfaces
|
7
|
+
#
|
8
|
+
# Mixin that defines the required methods of a feature store implementation. The LaunchDarkly
|
9
|
+
# client uses the feature store to persist feature flags and related objects received from
|
10
|
+
# the LaunchDarkly service. Implementations must support concurrent access and updates.
|
11
|
+
# For more about how feature stores can be used, see:
|
12
|
+
# [Using a persistent feature store](https://docs.launchdarkly.com/v2.0/docs/using-a-persistent-feature-store).
|
13
|
+
#
|
14
|
+
# An entity that can be stored in a feature store is a hash that can be converted to and from
|
15
|
+
# JSON, and that has at a minimum the following properties: `:key`, a string that is unique
|
16
|
+
# among entities of the same kind; `:version`, an integer that is higher for newer data;
|
17
|
+
# `:deleted`, a boolean (optional, defaults to false) that if true means this is a
|
18
|
+
# placeholder for a deleted entity.
|
19
|
+
#
|
20
|
+
# To represent the different kinds of objects that can be stored, such as feature flags and
|
21
|
+
# segments, the SDK will provide a "kind" object; this is a hash with a single property,
|
22
|
+
# `:namespace`, which is a short string unique to that kind. This string can be used as a
|
23
|
+
# collection name or a key prefix.
|
24
|
+
#
|
25
|
+
# The default implementation is {LaunchDarkly::InMemoryFeatureStore}. Several implementations
|
26
|
+
# that use databases can be found in {LaunchDarkly::Integrations}. If you want to write a new
|
27
|
+
# implementation, see {LaunchDarkly::Integrations::Util} for tools that can make this task
|
28
|
+
# simpler.
|
29
|
+
#
|
30
|
+
module FeatureStore
|
31
|
+
#
|
32
|
+
# Initializes (or re-initializes) the store with the specified set of entities. Any
|
33
|
+
# existing entries will be removed. Implementations can assume that this data set is up to
|
34
|
+
# date-- there is no need to perform individual version comparisons between the existing
|
35
|
+
# objects and the supplied features.
|
36
|
+
#
|
37
|
+
# If possible, the store should update the entire data set atomically. If that is not possible,
|
38
|
+
# it should iterate through the outer hash and then the inner hash using the existing iteration
|
39
|
+
# order of those hashes (the SDK will ensure that the items were inserted into the hashes in
|
40
|
+
# the correct order), storing each item, and then delete any leftover items at the very end.
|
41
|
+
#
|
42
|
+
# @param all_data [Hash] a hash where each key is one of the data kind objects, and each
|
43
|
+
# value is in turn a hash of string keys to entities
|
44
|
+
# @return [void]
|
45
|
+
#
|
46
|
+
def init(all_data)
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Returns the entity to which the specified key is mapped, if any.
|
51
|
+
#
|
52
|
+
# @param kind [Object] the kind of entity to get
|
53
|
+
# @param key [String] the unique key of the entity to get
|
54
|
+
# @return [Hash] the entity; nil if the key was not found, or if the stored entity's
|
55
|
+
# `:deleted` property was true
|
56
|
+
#
|
57
|
+
def get(kind, key)
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# Returns all stored entities of the specified kind, not including deleted entities.
|
62
|
+
#
|
63
|
+
# @param kind [Object] the kind of entity to get
|
64
|
+
# @return [Hash] a hash where each key is the entity's `:key` property and each value
|
65
|
+
# is the entity
|
66
|
+
#
|
67
|
+
def all(kind)
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# Attempt to add an entity, or update an existing entity with the same key. An update
|
72
|
+
# should only succeed if the new item's `:version` is greater than the old one;
|
73
|
+
# otherwise, the method should do nothing.
|
74
|
+
#
|
75
|
+
# @param kind [Object] the kind of entity to add or update
|
76
|
+
# @param item [Hash] the entity to add or update
|
77
|
+
# @return [void]
|
78
|
+
#
|
79
|
+
def upsert(kind, item)
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# Attempt to delete an entity if it exists. Deletion should only succeed if the
|
84
|
+
# `version` parameter is greater than the existing entity's `:version`; otherwise, the
|
85
|
+
# method should do nothing.
|
86
|
+
#
|
87
|
+
# @param kind [Object] the kind of entity to delete
|
88
|
+
# @param key [String] the unique key of the entity
|
89
|
+
# @param version [Integer] the entity must have a lower version than this to be deleted
|
90
|
+
# @return [void]
|
91
|
+
#
|
92
|
+
def delete(kind, key, version)
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# Checks whether this store has been initialized. That means that `init` has been called
|
97
|
+
# either by this process, or (if the store can be shared) by another process. This
|
98
|
+
# method will be called frequently, so it should be efficient. You can assume that if it
|
99
|
+
# has returned true once, it can continue to return true, i.e. a store cannot become
|
100
|
+
# uninitialized again.
|
101
|
+
#
|
102
|
+
# @return [Boolean] true if the store is in an initialized state
|
103
|
+
#
|
104
|
+
def initialized?
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# Performs any necessary cleanup to shut down the store when the client is being shut down.
|
109
|
+
#
|
110
|
+
# @return [void]
|
111
|
+
#
|
112
|
+
def stop
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# Mixin that defines the required methods of a data source implementation. This is the
|
118
|
+
# component that delivers feature flag data from LaunchDarkly to the LDClient by putting
|
119
|
+
# the data in the {FeatureStore}. It is expected to run concurrently on its own thread.
|
120
|
+
#
|
121
|
+
# The client has its own standard implementation, which uses either a streaming connection or
|
122
|
+
# polling depending on your configuration. Normally you will not need to use another one
|
123
|
+
# except for testing purposes. {FileDataSource} provides one such test fixture.
|
124
|
+
#
|
125
|
+
module DataSource
|
126
|
+
#
|
127
|
+
# Checks whether the data source has finished initializing. Initialization is considered done
|
128
|
+
# once it has received one complete data set from LaunchDarkly.
|
129
|
+
#
|
130
|
+
# @return [Boolean] true if initialization is complete
|
131
|
+
#
|
132
|
+
def initialized?
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Puts the data source into an active state. Normally this means it will make its first
|
137
|
+
# connection attempt to LaunchDarkly. If `start` has already been called, calling it again
|
138
|
+
# should simply return the same value as the first call.
|
139
|
+
#
|
140
|
+
# @return [Concurrent::Event] an Event which will be set once initialization is complete
|
141
|
+
#
|
142
|
+
def start
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# Puts the data source into an inactive state and releases all of its resources.
|
147
|
+
# This state should be considered permanent (`start` does not have to work after `stop`).
|
148
|
+
#
|
149
|
+
def stop
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/lib/ldclient-rb/ldclient.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "ldclient-rb/impl/store_client_wrapper"
|
1
2
|
require "concurrent/atomics"
|
2
3
|
require "digest/sha1"
|
3
4
|
require "logger"
|
@@ -10,7 +11,6 @@ module LaunchDarkly
|
|
10
11
|
# A client for LaunchDarkly. Client instances are thread-safe. Users
|
11
12
|
# should create a single client instance for the lifetime of the application.
|
12
13
|
#
|
13
|
-
#
|
14
14
|
class LDClient
|
15
15
|
include Evaluation
|
16
16
|
#
|
@@ -18,15 +18,28 @@ module LaunchDarkly
|
|
18
18
|
# configuration parameter can also supplied to specify advanced options,
|
19
19
|
# but for most use cases, the default configuration is appropriate.
|
20
20
|
#
|
21
|
+
# The client will immediately attempt to connect to LaunchDarkly and retrieve
|
22
|
+
# your feature flag data. If it cannot successfully do so within the time limit
|
23
|
+
# specified by `wait_for_sec`, the constructor will return a client that is in
|
24
|
+
# an uninitialized state. See {#initialized?} for more details.
|
21
25
|
#
|
22
26
|
# @param sdk_key [String] the SDK key for your LaunchDarkly account
|
23
27
|
# @param config [Config] an optional client configuration object
|
28
|
+
# @param wait_for_sec [Float] maximum time (in seconds) to wait for initialization
|
24
29
|
#
|
25
30
|
# @return [LDClient] The LaunchDarkly client instance
|
31
|
+
#
|
26
32
|
def initialize(sdk_key, config = Config.default, wait_for_sec = 5)
|
27
33
|
@sdk_key = sdk_key
|
28
|
-
|
29
|
-
|
34
|
+
|
35
|
+
# We need to wrap the feature store object with a FeatureStoreClientWrapper in order to add
|
36
|
+
# some necessary logic around updates. Unfortunately, we have code elsewhere that accesses
|
37
|
+
# the feature store through the Config object, so we need to make a new Config that uses
|
38
|
+
# the wrapped store.
|
39
|
+
@store = Impl::FeatureStoreClientWrapper.new(config.feature_store)
|
40
|
+
updated_config = config.clone
|
41
|
+
updated_config.instance_variable_set(:@feature_store, @store)
|
42
|
+
@config = updated_config
|
30
43
|
|
31
44
|
if @config.offline? || !@config.send_events
|
32
45
|
@event_processor = NullEventProcessor.new
|
@@ -39,149 +52,193 @@ module LaunchDarkly
|
|
39
52
|
return # requestor and update processor are not used in this mode
|
40
53
|
end
|
41
54
|
|
42
|
-
|
43
|
-
|
55
|
+
data_source_or_factory = @config.data_source || self.method(:create_default_data_source)
|
56
|
+
if data_source_or_factory.respond_to? :call
|
57
|
+
@data_source = data_source_or_factory.call(sdk_key, @config)
|
44
58
|
else
|
45
|
-
|
46
|
-
@update_processor = factory.call(sdk_key, config)
|
59
|
+
@data_source = data_source_or_factory
|
47
60
|
end
|
48
61
|
|
49
|
-
ready = @
|
62
|
+
ready = @data_source.start
|
50
63
|
if wait_for_sec > 0
|
51
64
|
ok = ready.wait(wait_for_sec)
|
52
65
|
if !ok
|
53
66
|
@config.logger.error { "[LDClient] Timeout encountered waiting for LaunchDarkly client initialization" }
|
54
|
-
elsif !@
|
67
|
+
elsif !@data_source.initialized?
|
55
68
|
@config.logger.error { "[LDClient] LaunchDarkly client initialization failed" }
|
56
69
|
end
|
57
70
|
end
|
58
71
|
end
|
59
72
|
|
73
|
+
#
|
74
|
+
# Tells the client that all pending analytics events should be delivered as soon as possible.
|
75
|
+
#
|
76
|
+
# When the LaunchDarkly client generates analytics events (from {#variation}, {#variation_detail},
|
77
|
+
# {#identify}, or {#track}), they are queued on a worker thread. The event thread normally
|
78
|
+
# sends all queued events to LaunchDarkly at regular intervals, controlled by the
|
79
|
+
# {Config#flush_interval} option. Calling `flush` triggers a send without waiting for the
|
80
|
+
# next interval.
|
81
|
+
#
|
82
|
+
# Flushing is asynchronous, so this method will return before it is complete. However, if you
|
83
|
+
# call {#close}, events are guaranteed to be sent before that method returns.
|
84
|
+
#
|
60
85
|
def flush
|
61
86
|
@event_processor.flush
|
62
87
|
end
|
63
88
|
|
64
|
-
|
89
|
+
#
|
90
|
+
# @param key [String] the feature flag key
|
91
|
+
# @param user [Hash] the user properties
|
92
|
+
# @param default [Boolean] (false) the value to use if the flag cannot be evaluated
|
93
|
+
# @return [Boolean] the flag value
|
94
|
+
# @deprecated Use {#variation} instead.
|
95
|
+
#
|
96
|
+
def toggle?(key, user, default = false)
|
65
97
|
@config.logger.warn { "[LDClient] toggle? is deprecated. Use variation instead" }
|
66
98
|
variation(key, user, default)
|
67
99
|
end
|
68
100
|
|
101
|
+
#
|
102
|
+
# Creates a hash string that can be used by the JavaScript SDK to identify a user.
|
103
|
+
# For more information, see [Secure mode](https://docs.launchdarkly.com/docs/js-sdk-reference#section-secure-mode).
|
104
|
+
#
|
105
|
+
# @param user [Hash] the user properties
|
106
|
+
# @return [String] a hash string
|
107
|
+
#
|
69
108
|
def secure_mode_hash(user)
|
70
109
|
OpenSSL::HMAC.hexdigest("sha256", @sdk_key, user[:key].to_s)
|
71
110
|
end
|
72
111
|
|
73
|
-
#
|
112
|
+
#
|
113
|
+
# Returns whether the client has been initialized and is ready to serve feature flag requests.
|
114
|
+
#
|
115
|
+
# If this returns false, it means that the client did not succeed in connecting to
|
116
|
+
# LaunchDarkly within the time limit that you specified in the constructor. It could
|
117
|
+
# still succeed in connecting at a later time (on another thread), or it could have
|
118
|
+
# given up permanently (for instance, if your SDK key is invalid). In the meantime,
|
119
|
+
# any call to {#variation} or {#variation_detail} will behave as follows:
|
120
|
+
#
|
121
|
+
# 1. It will check whether the feature store already contains data (that is, you
|
122
|
+
# are using a database-backed store and it was populated by a previous run of this
|
123
|
+
# application). If so, it will use the last known feature flag data.
|
124
|
+
#
|
125
|
+
# 2. Failing that, it will return the value that you specified for the `default`
|
126
|
+
# parameter of {#variation} or {#variation_detail}.
|
127
|
+
#
|
74
128
|
# @return [Boolean] true if the client has been initialized
|
129
|
+
#
|
75
130
|
def initialized?
|
76
|
-
@config.offline? || @config.use_ldd? || @
|
131
|
+
@config.offline? || @config.use_ldd? || @data_source.initialized?
|
77
132
|
end
|
78
133
|
|
79
134
|
#
|
80
|
-
# Determines the variation of a feature flag to present to a user.
|
81
|
-
# the user hash should contain a +:key+ .
|
135
|
+
# Determines the variation of a feature flag to present to a user.
|
82
136
|
#
|
83
|
-
#
|
84
|
-
#
|
137
|
+
# At a minimum, the user hash should contain a `:key`, which should be the unique
|
138
|
+
# identifier for your user (or, for an anonymous user, a session identifier or
|
139
|
+
# cookie).
|
85
140
|
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
141
|
+
# Other supported user attributes include IP address, country code, and an arbitrary hash of
|
142
|
+
# custom attributes. For more about the supported user properties and how they work in
|
143
|
+
# LaunchDarkly, see [Targeting users](https://docs.launchdarkly.com/docs/targeting-users).
|
144
|
+
#
|
145
|
+
# The optional `:privateAttributeNames` user property allows you to specify a list of
|
146
|
+
# attribute names that should not be sent back to LaunchDarkly.
|
147
|
+
# [Private attributes](https://docs.launchdarkly.com/docs/private-user-attributes)
|
148
|
+
# can also be configured globally in {Config}.
|
90
149
|
#
|
91
|
-
#
|
150
|
+
# @example Basic user hash
|
151
|
+
# {key: "my-user-id"}
|
92
152
|
#
|
93
153
|
# @example More complete user hash
|
94
|
-
# {key: "user
|
154
|
+
# {key: "my-user-id", ip: "127.0.0.1", country: "US", custom: {customer_rank: 1000}}
|
95
155
|
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
# @example A user hash with custom attributes
|
99
|
-
# {key: "user@example.com", custom: {customer_rank: 1000, groups: ["google", "microsoft"]}}
|
100
|
-
#
|
101
|
-
# Attribute values in the custom hash can be integers, booleans, strings, or
|
102
|
-
# lists of integers, booleans, or strings.
|
156
|
+
# @example User with a private attribute
|
157
|
+
# {key: "my-user-id", email: "email@example.com", privateAttributeNames: ["email"]}
|
103
158
|
#
|
104
159
|
# @param key [String] the unique feature key for the feature flag, as shown
|
105
160
|
# on the LaunchDarkly dashboard
|
106
161
|
# @param user [Hash] a hash containing parameters for the end user requesting the flag
|
107
|
-
# @param default the default value of the flag
|
162
|
+
# @param default the default value of the flag; this is used if there is an error
|
163
|
+
# condition making it impossible to find or evaluate the flag
|
164
|
+
#
|
165
|
+
# @return the variation to show the user, or the default value if there's an an error
|
108
166
|
#
|
109
|
-
# @return the variation to show the user, or the
|
110
|
-
# default value if there's an an error
|
111
167
|
def variation(key, user, default)
|
112
168
|
evaluate_internal(key, user, default, false).value
|
113
169
|
end
|
114
170
|
|
115
171
|
#
|
116
|
-
# Determines the variation of a feature flag for a user, like
|
172
|
+
# Determines the variation of a feature flag for a user, like {#variation}, but also
|
117
173
|
# provides additional information about how this value was calculated.
|
118
174
|
#
|
119
|
-
# The return value of `variation_detail` is an
|
120
|
-
# three properties:
|
121
|
-
#
|
122
|
-
#
|
123
|
-
# of `variation`)
|
175
|
+
# The return value of `variation_detail` is an {EvaluationDetail} object, which has
|
176
|
+
# three properties: the result value, the positional index of this value in the flag's
|
177
|
+
# list of variations, and an object describing the main reason why this value was
|
178
|
+
# selected. See {EvaluationDetail} for more on these properties.
|
124
179
|
#
|
125
|
-
# `
|
126
|
-
#
|
180
|
+
# Calling `variation_detail` instead of `variation` also causes the "reason" data to
|
181
|
+
# be included in analytics events, if you are capturing detailed event data for this flag.
|
127
182
|
#
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
# * `'OFF'`: the flag was off and therefore returned its configured off value
|
132
|
-
# * `'FALLTHROUGH'`: the flag was on but the user did not match any targets or rules
|
133
|
-
# * `'TARGET_MATCH'`: the user key was specifically targeted for this flag
|
134
|
-
# * `'RULE_MATCH'`: the user matched one of the flag's rules; the `:ruleIndex` and
|
135
|
-
# `:ruleId` properties indicate the positional index and unique identifier of the rule
|
136
|
-
# * `'PREREQUISITE_FAILED`': the flag was considered off because it had at least one
|
137
|
-
# prerequisite flag that either was off or did not return the desired variation; the
|
138
|
-
# `:prerequisiteKey` property indicates the key of the prerequisite that failed
|
139
|
-
# * `'ERROR'`: the flag could not be evaluated, e.g. because it does not exist or due
|
140
|
-
# to an unexpected error, and therefore returned the default value; the `:errorKind`
|
141
|
-
# property describes the nature of the error, such as `'FLAG_NOT_FOUND'`
|
142
|
-
#
|
143
|
-
# The `reason` will also be included in analytics events, if you are capturing
|
144
|
-
# detailed event data for this flag.
|
183
|
+
# For more information, see the reference guide on
|
184
|
+
# [Evaluation reasons](https://docs.launchdarkly.com/v2.0/docs/evaluation-reasons).
|
145
185
|
#
|
146
186
|
# @param key [String] the unique feature key for the feature flag, as shown
|
147
187
|
# on the LaunchDarkly dashboard
|
148
188
|
# @param user [Hash] a hash containing parameters for the end user requesting the flag
|
149
|
-
# @param default the default value of the flag
|
189
|
+
# @param default the default value of the flag; this is used if there is an error
|
190
|
+
# condition making it impossible to find or evaluate the flag
|
150
191
|
#
|
151
|
-
# @return an
|
192
|
+
# @return [EvaluationDetail] an object describing the result
|
152
193
|
#
|
153
194
|
def variation_detail(key, user, default)
|
154
195
|
evaluate_internal(key, user, default, true)
|
155
196
|
end
|
156
197
|
|
157
198
|
#
|
158
|
-
# Registers the user
|
199
|
+
# Registers the user. This method simply creates an analytics event containing the user
|
200
|
+
# properties, so that LaunchDarkly will know about that user if it does not already.
|
159
201
|
#
|
160
|
-
#
|
202
|
+
# Calling {#variation} or {#variation_detail} also sends the user information to
|
203
|
+
# LaunchDarkly (if events are enabled), so you only need to use {#identify} if you
|
204
|
+
# want to identify the user without evaluating a flag.
|
161
205
|
#
|
206
|
+
# Note that event delivery is asynchronous, so the event may not actually be sent
|
207
|
+
# until later; see {#flush}.
|
208
|
+
#
|
209
|
+
# @param user [Hash] The user to register; this can have all the same user properties
|
210
|
+
# described in {#variation}
|
162
211
|
# @return [void]
|
212
|
+
#
|
163
213
|
def identify(user)
|
164
214
|
sanitize_user(user)
|
165
215
|
@event_processor.add_event(kind: "identify", key: user[:key], user: user)
|
166
216
|
end
|
167
217
|
|
168
218
|
#
|
169
|
-
# Tracks that a user performed an event
|
219
|
+
# Tracks that a user performed an event. This method creates a "custom" analytics event
|
220
|
+
# containing the specified event name (key), user properties, and optional data.
|
221
|
+
#
|
222
|
+
# Note that event delivery is asynchronous, so the event may not actually be sent
|
223
|
+
# until later; see {#flush}.
|
170
224
|
#
|
171
225
|
# @param event_name [String] The name of the event
|
172
|
-
# @param user [Hash] The user
|
226
|
+
# @param user [Hash] The user to register; this can have all the same user properties
|
227
|
+
# described in {#variation}
|
173
228
|
# @param data [Hash] A hash containing any additional data associated with the event
|
174
|
-
#
|
175
229
|
# @return [void]
|
230
|
+
#
|
176
231
|
def track(event_name, user, data)
|
177
232
|
sanitize_user(user)
|
178
233
|
@event_processor.add_event(kind: "custom", key: event_name, user: user, data: data)
|
179
234
|
end
|
180
235
|
|
181
236
|
#
|
182
|
-
# Returns all feature flag values for the given user.
|
183
|
-
#
|
184
|
-
#
|
237
|
+
# Returns all feature flag values for the given user.
|
238
|
+
#
|
239
|
+
# @deprecated Please use {#all_flags_state} instead. Current versions of the
|
240
|
+
# client-side SDK will not generate analytics events correctly if you pass the
|
241
|
+
# result of `all_flags`.
|
185
242
|
#
|
186
243
|
# @param user [Hash] The end user requesting the feature flags
|
187
244
|
# @return [Hash] a hash of feature flag keys to values
|
@@ -191,21 +248,21 @@ module LaunchDarkly
|
|
191
248
|
end
|
192
249
|
|
193
250
|
#
|
194
|
-
# Returns a FeatureFlagsState object that encapsulates the state of all feature flags for a given user,
|
251
|
+
# Returns a {FeatureFlagsState} object that encapsulates the state of all feature flags for a given user,
|
195
252
|
# including the flag values and also metadata that can be used on the front end. This method does not
|
196
253
|
# send analytics events back to LaunchDarkly.
|
197
254
|
#
|
198
255
|
# @param user [Hash] The end user requesting the feature flags
|
199
|
-
# @param options
|
256
|
+
# @param options [Hash] Optional parameters to control how the state is generated
|
200
257
|
# @option options [Boolean] :client_side_only (false) True if only flags marked for use with the
|
201
258
|
# client-side SDK should be included in the state. By default, all flags are included.
|
202
259
|
# @option options [Boolean] :with_reasons (false) True if evaluation reasons should be included
|
203
|
-
# in the state (see
|
260
|
+
# in the state (see {#variation_detail}). By default, they are not included.
|
204
261
|
# @option options [Boolean] :details_only_for_tracked_flags (false) True if any flag metadata that is
|
205
|
-
#
|
206
|
-
#
|
207
|
-
#
|
208
|
-
# @return [FeatureFlagsState] a FeatureFlagsState object which can be serialized to JSON
|
262
|
+
# normally only used for event generation - such as flag versions and evaluation reasons - should be
|
263
|
+
# omitted for any flag that does not have event tracking or debugging turned on. This reduces the size
|
264
|
+
# of the JSON data if you are passing the flag state to the front end.
|
265
|
+
# @return [FeatureFlagsState] a {FeatureFlagsState} object which can be serialized to JSON
|
209
266
|
#
|
210
267
|
def all_flags_state(user, options={})
|
211
268
|
return FeatureFlagsState.new(false) if @config.offline?
|
@@ -246,19 +303,19 @@ module LaunchDarkly
|
|
246
303
|
end
|
247
304
|
|
248
305
|
#
|
249
|
-
# Releases all network connections and other resources held by the client, making it no longer usable
|
306
|
+
# Releases all network connections and other resources held by the client, making it no longer usable.
|
250
307
|
#
|
251
308
|
# @return [void]
|
252
309
|
def close
|
253
310
|
@config.logger.info { "[LDClient] Closing LaunchDarkly client..." }
|
254
|
-
@
|
311
|
+
@data_source.stop
|
255
312
|
@event_processor.stop
|
256
313
|
@store.stop
|
257
314
|
end
|
258
315
|
|
259
316
|
private
|
260
317
|
|
261
|
-
def
|
318
|
+
def create_default_data_source(sdk_key, config)
|
262
319
|
if config.offline?
|
263
320
|
return NullUpdateProcessor.new
|
264
321
|
end
|
@@ -351,6 +408,7 @@ module LaunchDarkly
|
|
351
408
|
|
352
409
|
#
|
353
410
|
# Used internally when the client is offline.
|
411
|
+
# @private
|
354
412
|
#
|
355
413
|
class NullUpdateProcessor
|
356
414
|
def start
|