ldclient-rb 5.4.3 → 5.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|