ldclient-rb 0.2.0 → 0.3.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/.gitignore +2 -1
- data/Gemfile +0 -1
- data/README.md +6 -1
- data/ldclient-rb.gemspec +7 -4
- data/lib/ldclient-rb.rb +2 -1
- data/lib/ldclient-rb/config.rb +53 -0
- data/lib/ldclient-rb/ldclient.rb +67 -21
- data/lib/ldclient-rb/stream.rb +166 -0
- data/lib/ldclient-rb/version.rb +1 -1
- metadata +50 -8
- data/Gemfile.lock +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: abbd939adce22e127e3b1dc732d5d24487457a79
|
4
|
+
data.tar.gz: a903a1c807ce38b74c207ef53196198d5d16f489
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85400dc792fbe9f4ddb982cc195b0342ac74fb2e97203a910242b4b72376d1fa28522a5468c6ee9861167a4fe6e06ab4aeeac131e3f043973d6fbe2130aa7754
|
7
|
+
data.tar.gz: 8c1a5de8549b1b26aa4aae92b716a06740e9bdd615c4a9bdc06d763800076e1a579068fc899fd1662ee600d1635f789eb73e5930214d8af1e8a57dfd5f78cbca
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -32,4 +32,9 @@ Your first feature flag
|
|
32
32
|
Learn more
|
33
33
|
-----------
|
34
34
|
|
35
|
-
Check out our [documentation](http://docs.launchdarkly.com) for in-depth instructions on configuring and using LaunchDarkly. You can also head straight to the [complete reference guide for this SDK](http://docs.launchdarkly.com/v1.0/docs/ruby-sdk-reference).
|
35
|
+
Check out our [documentation](http://docs.launchdarkly.com) for in-depth instructions on configuring and using LaunchDarkly. You can also head straight to the [complete reference guide for this SDK](http://docs.launchdarkly.com/v1.0/docs/ruby-sdk-reference).
|
36
|
+
|
37
|
+
Contributing
|
38
|
+
------------
|
39
|
+
|
40
|
+
We encourage pull-requests and other contributions from the community. We've also published an [SDK contributor's guide](http://docs.launchdarkly.com/v1.0/docs/sdk-contributors-guide) that provides a detailed explanation of how our SDKs work.
|
data/ldclient-rb.gemspec
CHANGED
@@ -6,11 +6,11 @@ require 'ldclient-rb/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "ldclient-rb"
|
8
8
|
spec.version = LaunchDarkly::VERSION
|
9
|
-
spec.authors = ["
|
10
|
-
spec.email = ["team@
|
9
|
+
spec.authors = ["LaunchDarkly"]
|
10
|
+
spec.email = ["team@launchdarkly.com"]
|
11
11
|
spec.summary = %q{LaunchDarkly SDK for Ruby}
|
12
12
|
spec.description = %q{Official LaunchDarkly SDK for Ruby}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://rubygems.org/gems/ldclient-rb"
|
14
14
|
spec.license = "Apache 2.0"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -25,5 +25,8 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_runtime_dependency "faraday", "~> 0.9"
|
26
26
|
spec.add_runtime_dependency "faraday-http-cache", "~> 0.4"
|
27
27
|
spec.add_runtime_dependency "thread_safe", "~> 0.3"
|
28
|
-
spec.add_runtime_dependency "net-http-persistent", "~> 2.9
|
28
|
+
spec.add_runtime_dependency "net-http-persistent", "~> 2.9"
|
29
|
+
spec.add_runtime_dependency "concurrent-ruby", "~> 0.9"
|
30
|
+
spec.add_runtime_dependency "hashdiff", "~> 0.2"
|
31
|
+
spec.add_runtime_dependency "ld-em-eventsource", "~> 0.2"
|
29
32
|
end
|
data/lib/ldclient-rb.rb
CHANGED
data/lib/ldclient-rb/config.rb
CHANGED
@@ -23,6 +23,7 @@ module LaunchDarkly
|
|
23
23
|
# @return [type] [description]
|
24
24
|
def initialize(opts = {})
|
25
25
|
@base_uri = (opts[:base_uri] || Config.default_base_uri).chomp("/")
|
26
|
+
@stream_uri = (opts[:stream_uri] || Config.default_stream_uri).chomp("/")
|
26
27
|
@capacity = opts[:capacity] || Config.default_capacity
|
27
28
|
@logger = opts[:logger] || Config.default_logger
|
28
29
|
@store = opts[:store] || Config.default_store
|
@@ -30,6 +31,9 @@ module LaunchDarkly
|
|
30
31
|
@connect_timeout = opts[:connect_timeout] || Config.default_connect_timeout
|
31
32
|
@read_timeout = opts[:read_timeout] || Config.default_read_timeout
|
32
33
|
@log_timings = opts[:log_timings] || Config.default_log_timings
|
34
|
+
@stream = opts[:stream] || Config.default_stream
|
35
|
+
@feature_store = opts[:feature_store] || Config.default_feature_store
|
36
|
+
@debug_stream = opts[:debug_stream] || Config.default_debug_stream
|
33
37
|
end
|
34
38
|
|
35
39
|
#
|
@@ -40,6 +44,32 @@ module LaunchDarkly
|
|
40
44
|
@base_uri
|
41
45
|
end
|
42
46
|
|
47
|
+
#
|
48
|
+
# The base URL for the LaunchDarkly streaming server.
|
49
|
+
#
|
50
|
+
# @return [String] The configured base URL for the LaunchDarkly streaming server.
|
51
|
+
def stream_uri
|
52
|
+
@stream_uri
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Whether streaming mode should be enabled. Streaming mode asynchronously updates
|
57
|
+
# feature flags in real-time using server-sent events.
|
58
|
+
#
|
59
|
+
# @return [Boolean] True if streaming mode should be enabled
|
60
|
+
def stream?
|
61
|
+
@stream
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Whether we should debug streaming mode. If set, the client will fetch features via polling
|
66
|
+
# and compare the retrieved feature with the value in the feature store
|
67
|
+
#
|
68
|
+
# @return [Boolean] True if we should debug streaming mode
|
69
|
+
def debug_stream?
|
70
|
+
@debug_stream
|
71
|
+
end
|
72
|
+
|
43
73
|
#
|
44
74
|
# The number of seconds between flushes of the event buffer. Decreasing the flush interval means
|
45
75
|
# that the event buffer is less likely to reach capacity.
|
@@ -100,6 +130,13 @@ module LaunchDarkly
|
|
100
130
|
@log_timings
|
101
131
|
end
|
102
132
|
|
133
|
+
#
|
134
|
+
# TODO docs
|
135
|
+
#
|
136
|
+
def feature_store
|
137
|
+
@feature_store
|
138
|
+
end
|
139
|
+
|
103
140
|
#
|
104
141
|
# The default LaunchDarkly client configuration. This configuration sets reasonable defaults for most users.
|
105
142
|
#
|
@@ -116,6 +153,10 @@ module LaunchDarkly
|
|
116
153
|
"https://app.launchdarkly.com"
|
117
154
|
end
|
118
155
|
|
156
|
+
def self.default_stream_uri
|
157
|
+
"https://stream.launchdarkly.com"
|
158
|
+
end
|
159
|
+
|
119
160
|
def self.default_store
|
120
161
|
defined?(Rails) && Rails.respond_to?(:cache) ? Rails.cache : ThreadSafeMemoryStore.new
|
121
162
|
end
|
@@ -140,5 +181,17 @@ module LaunchDarkly
|
|
140
181
|
false
|
141
182
|
end
|
142
183
|
|
184
|
+
def self.default_stream
|
185
|
+
false
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.default_feature_store
|
189
|
+
nil
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.default_debug_stream
|
193
|
+
false
|
194
|
+
end
|
195
|
+
|
143
196
|
end
|
144
197
|
end
|
data/lib/ldclient-rb/ldclient.rb
CHANGED
@@ -5,6 +5,7 @@ require 'thread'
|
|
5
5
|
require 'logger'
|
6
6
|
require 'net/http/persistent'
|
7
7
|
require 'benchmark'
|
8
|
+
require 'hashdiff'
|
8
9
|
|
9
10
|
module LaunchDarkly
|
10
11
|
|
@@ -38,6 +39,10 @@ module LaunchDarkly
|
|
38
39
|
end
|
39
40
|
@offline = false
|
40
41
|
|
42
|
+
if @config.stream?
|
43
|
+
@stream_processor = StreamProcessor.new(api_key, config)
|
44
|
+
end
|
45
|
+
|
41
46
|
@worker = create_worker()
|
42
47
|
end
|
43
48
|
|
@@ -51,7 +56,6 @@ module LaunchDarkly
|
|
51
56
|
rescue
|
52
57
|
end
|
53
58
|
|
54
|
-
|
55
59
|
if !events.empty?()
|
56
60
|
res = log_timings("Flush events") {
|
57
61
|
next @client.post (@config.base_uri + "/api/events/bulk") do |req|
|
@@ -124,12 +128,35 @@ module LaunchDarkly
|
|
124
128
|
return default
|
125
129
|
end
|
126
130
|
|
127
|
-
|
131
|
+
unless user
|
132
|
+
@config.logger.error("[LDClient] Must specify user")
|
133
|
+
return default
|
134
|
+
end
|
135
|
+
|
136
|
+
if @config.stream? and not @stream_processor.started?
|
137
|
+
@stream_processor.start
|
138
|
+
end
|
139
|
+
|
140
|
+
if @config.stream? and @stream_processor.initialized?
|
141
|
+
feature = get_flag_stream(key)
|
142
|
+
if @config.debug_stream?
|
143
|
+
polled = get_flag_int(key)
|
144
|
+
diff = HashDiff.diff(feature, polled)
|
145
|
+
if not diff.empty?
|
146
|
+
@config.logger.error("Streamed flag differs from polled flag " + diff.to_s)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
else
|
150
|
+
feature = get_flag_int(key)
|
151
|
+
end
|
152
|
+
value = evaluate(feature, user)
|
153
|
+
value.nil? ? default : value
|
154
|
+
|
128
155
|
add_event({:kind => 'feature', :key => key, :user => user, :value => value})
|
129
156
|
LDNewRelic.annotate_transaction(key, value)
|
130
157
|
return value
|
131
158
|
rescue StandardError => error
|
132
|
-
@config.logger.error("[LDClient] Unhandled exception in
|
159
|
+
@config.logger.error("[LDClient] Unhandled exception in toggle: (#{error.class.name}) #{error.to_s}\n\t#{error.backtrace.join("\n\t")}")
|
133
160
|
default
|
134
161
|
end
|
135
162
|
end
|
@@ -142,7 +169,7 @@ module LaunchDarkly
|
|
142
169
|
event[:creationDate] = (Time.now.to_f * 1000).to_i
|
143
170
|
@queue.push(event)
|
144
171
|
|
145
|
-
if
|
172
|
+
if !@worker.alive?
|
146
173
|
@worker = create_worker()
|
147
174
|
end
|
148
175
|
else
|
@@ -175,7 +202,7 @@ module LaunchDarkly
|
|
175
202
|
# Tracks that a user performed an event
|
176
203
|
#
|
177
204
|
# @param event_name [String] The name of the event
|
178
|
-
# @param user [Hash] The user that performed the event. This should be the same user hash used in calls to {#
|
205
|
+
# @param user [Hash] The user that performed the event. This should be the same user hash used in calls to {#toggle?}
|
179
206
|
# @param data [Hash] A hash containing any additional data associated with the event
|
180
207
|
#
|
181
208
|
# @return [void]
|
@@ -183,14 +210,37 @@ module LaunchDarkly
|
|
183
210
|
add_event({:kind => 'custom', :key => event_name, :user => user, :data => data })
|
184
211
|
end
|
185
212
|
|
186
|
-
|
213
|
+
#
|
214
|
+
# Returns the key of every feature
|
215
|
+
#
|
216
|
+
def feature_keys
|
217
|
+
get_features.map {|feature| feature[:key]}
|
218
|
+
end
|
187
219
|
|
188
|
-
|
189
|
-
|
190
|
-
|
220
|
+
#
|
221
|
+
# Returns all features
|
222
|
+
#
|
223
|
+
def get_features
|
224
|
+
res = @client.get (@config.base_uri + '/api/features') do |req|
|
225
|
+
req.headers['Authorization'] = 'api_key ' + @api_key
|
226
|
+
req.headers['User-Agent'] = 'RubyClient/' + LaunchDarkly::VERSION
|
227
|
+
req.options.timeout = @config.read_timeout
|
228
|
+
req.options.open_timeout = @config.connect_timeout
|
191
229
|
end
|
192
230
|
|
193
|
-
res
|
231
|
+
if res.status == 200 then
|
232
|
+
return JSON.parse(res.body, symbolize_names: true)[:items]
|
233
|
+
else
|
234
|
+
@config.logger.error("[LDClient] Unexpected status code #{res.status}")
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def get_flag_stream(key)
|
239
|
+
@stream_processor.get_feature(key)
|
240
|
+
end
|
241
|
+
|
242
|
+
def get_flag_int(key)
|
243
|
+
res = log_timings("Feature request") {
|
194
244
|
next @client.get (@config.base_uri + '/api/eval/features/' + key) do |req|
|
195
245
|
req.headers['Authorization'] = 'api_key ' + @api_key
|
196
246
|
req.headers['User-Agent'] = 'RubyClient/' + LaunchDarkly::VERSION
|
@@ -201,25 +251,21 @@ module LaunchDarkly
|
|
201
251
|
|
202
252
|
if res.status == 401
|
203
253
|
@config.logger.error("[LDClient] Invalid API key")
|
204
|
-
return
|
254
|
+
return nil
|
205
255
|
end
|
206
256
|
|
207
257
|
if res.status == 404
|
208
258
|
@config.logger.error("[LDClient] Unknown feature key: #{key}")
|
209
|
-
return
|
259
|
+
return nil
|
210
260
|
end
|
211
261
|
|
212
262
|
if res.status != 200
|
213
263
|
@config.logger.error("[LDClient] Unexpected status code #{res.status}")
|
214
|
-
return
|
264
|
+
return nil
|
215
265
|
end
|
216
266
|
|
217
267
|
|
218
|
-
|
219
|
-
|
220
|
-
val = evaluate(feature, user)
|
221
|
-
|
222
|
-
val == nil ? default : val
|
268
|
+
JSON.parse(res.body, :symbolize_names => true)
|
223
269
|
end
|
224
270
|
|
225
271
|
def param_for_user(feature, user)
|
@@ -289,13 +335,13 @@ module LaunchDarkly
|
|
289
335
|
end
|
290
336
|
|
291
337
|
def evaluate(feature, user)
|
292
|
-
|
338
|
+
if feature.nil? || !feature[:on]
|
293
339
|
return nil
|
294
340
|
end
|
295
341
|
|
296
342
|
param = param_for_user(feature, user)
|
297
343
|
|
298
|
-
if param
|
344
|
+
if param.nil?
|
299
345
|
return nil
|
300
346
|
end
|
301
347
|
|
@@ -344,7 +390,7 @@ module LaunchDarkly
|
|
344
390
|
return res
|
345
391
|
end
|
346
392
|
|
347
|
-
private :add_event, :get_flag_int, :param_for_user, :match_target?, :match_user?, :match_variation?, :evaluate, :create_worker, :log_timings
|
393
|
+
private :add_event, :get_flag_stream, :get_flag_int, :param_for_user, :match_target?, :match_user?, :match_variation?, :evaluate, :create_worker, :log_timings
|
348
394
|
|
349
395
|
end
|
350
396
|
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'concurrent/atomics'
|
2
|
+
require 'json'
|
3
|
+
require 'ld-em-eventsource'
|
4
|
+
|
5
|
+
module LaunchDarkly
|
6
|
+
|
7
|
+
PUT = "put"
|
8
|
+
PATCH = "patch"
|
9
|
+
DELETE = "delete"
|
10
|
+
|
11
|
+
class InMemoryFeatureStore
|
12
|
+
def initialize()
|
13
|
+
@features = Hash.new
|
14
|
+
@lock = Concurrent::ReadWriteLock.new
|
15
|
+
@initialized = Concurrent::AtomicBoolean.new(false)
|
16
|
+
end
|
17
|
+
|
18
|
+
def get(key)
|
19
|
+
@lock.with_read_lock {
|
20
|
+
f = @features[key.to_sym]
|
21
|
+
(f.nil? || f[:deleted]) ? nil : f
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def all()
|
26
|
+
@lock.with_read_lock {
|
27
|
+
@features.select {|k,f| not f[:deleted]}
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete(key, version)
|
32
|
+
@lock.with_write_lock {
|
33
|
+
old = @features[key.to_sym]
|
34
|
+
|
35
|
+
if old != nil and old[:version] < version
|
36
|
+
old[:deleted] = true
|
37
|
+
old[:version] = version
|
38
|
+
@features[key.to_sym] = old
|
39
|
+
elsif old.nil?
|
40
|
+
@features[key.to_sym] = {:deleted => true, :version => version}
|
41
|
+
end
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def init(fs)
|
46
|
+
@lock.with_write_lock {
|
47
|
+
@features.replace(fs)
|
48
|
+
@initialized.make_true
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def upsert(key, feature)
|
53
|
+
@lock.with_write_lock {
|
54
|
+
old = @features[key.to_sym]
|
55
|
+
|
56
|
+
if old.nil? or old[:version] < feature[:version]
|
57
|
+
@features[key.to_sym] = feature
|
58
|
+
end
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialized?()
|
63
|
+
@initialized.value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class StreamProcessor
|
68
|
+
def initialize(api_key, config)
|
69
|
+
@api_key = api_key
|
70
|
+
@config = config
|
71
|
+
@store = config.feature_store ? config.feature_store : InMemoryFeatureStore.new
|
72
|
+
@disconnected = Concurrent::AtomicReference.new(nil)
|
73
|
+
@started = Concurrent::AtomicBoolean.new(false)
|
74
|
+
end
|
75
|
+
|
76
|
+
def initialized?()
|
77
|
+
@store.initialized?
|
78
|
+
end
|
79
|
+
|
80
|
+
def started?()
|
81
|
+
@started.value
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_feature(key)
|
85
|
+
if not initialized?
|
86
|
+
throw :uninitialized
|
87
|
+
end
|
88
|
+
@store.get(key)
|
89
|
+
end
|
90
|
+
|
91
|
+
def start_reactor()
|
92
|
+
if defined?(Thin)
|
93
|
+
@config.logger.debug("Running in a Thin environment-- not starting EventMachine")
|
94
|
+
elsif EM.reactor_running?
|
95
|
+
@config.logger.debug("EventMachine already running")
|
96
|
+
else
|
97
|
+
@config.logger.debug("Starting EventMachine")
|
98
|
+
Thread.new { EM.run {} }
|
99
|
+
Thread.pass until EM.reactor_running?
|
100
|
+
end
|
101
|
+
EM.reactor_running?
|
102
|
+
end
|
103
|
+
|
104
|
+
def start()
|
105
|
+
# Try to start the reactor. If it's not started, we shouldn't start
|
106
|
+
# the stream processor
|
107
|
+
if not start_reactor
|
108
|
+
return
|
109
|
+
end
|
110
|
+
|
111
|
+
# If someone else booted the stream processor connection, just return
|
112
|
+
if not @started.make_true
|
113
|
+
return
|
114
|
+
end
|
115
|
+
|
116
|
+
# If we're the first and only thread to set started, boot
|
117
|
+
# the stream processor connection
|
118
|
+
EM.defer do
|
119
|
+
source = EM::EventSource.new(@config.stream_uri + "/features",
|
120
|
+
{},
|
121
|
+
{'Accept' => 'text/event-stream',
|
122
|
+
'Authorization' => 'api_key ' + @api_key,
|
123
|
+
'User-Agent' => 'RubyClient/' + LaunchDarkly::VERSION})
|
124
|
+
source.on PUT do |message|
|
125
|
+
features = JSON.parse(message, :symbolize_names => true)
|
126
|
+
@store.init(features)
|
127
|
+
set_connected
|
128
|
+
end
|
129
|
+
source.on PATCH do |message|
|
130
|
+
json = JSON.parse(message, :symbolize_names => true)
|
131
|
+
@store.upsert(json[:path][1..-1], json[:data])
|
132
|
+
set_connected
|
133
|
+
end
|
134
|
+
source.on DELETE do |message|
|
135
|
+
json = JSON.parse(message, :symbolize_names => true)
|
136
|
+
@store.delete(json[:path][1..-1], json[:version])
|
137
|
+
set_connected
|
138
|
+
end
|
139
|
+
source.error do |error|
|
140
|
+
@config.logger.error("[LDClient] Error subscribing to stream API: #{error}")
|
141
|
+
set_disconnected
|
142
|
+
end
|
143
|
+
source.inactivity_timeout = 0
|
144
|
+
source.start
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def set_disconnected()
|
149
|
+
@disconnected.set(Time.now)
|
150
|
+
end
|
151
|
+
|
152
|
+
def set_connected()
|
153
|
+
@disconnected.set(nil)
|
154
|
+
end
|
155
|
+
|
156
|
+
def should_fallback_update()
|
157
|
+
disc = @disconnected.get
|
158
|
+
disc != nil and disc < (Time.now - 120)
|
159
|
+
end
|
160
|
+
|
161
|
+
# TODO mark private methods
|
162
|
+
private :set_connected, :set_disconnected, :start_reactor
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
data/lib/ldclient-rb/version.rb
CHANGED
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: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- LaunchDarkly
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -100,24 +100,65 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 2.9
|
103
|
+
version: '2.9'
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 2.9
|
110
|
+
version: '2.9'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: concurrent-ruby
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.9'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.9'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: hashdiff
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.2'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.2'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: ld-em-eventsource
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0.2'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0.2'
|
111
153
|
description: Official LaunchDarkly SDK for Ruby
|
112
154
|
email:
|
113
|
-
- team@
|
155
|
+
- team@launchdarkly.com
|
114
156
|
executables: []
|
115
157
|
extensions: []
|
116
158
|
extra_rdoc_files: []
|
117
159
|
files:
|
118
160
|
- ".gitignore"
|
119
161
|
- Gemfile
|
120
|
-
- Gemfile.lock
|
121
162
|
- LICENSE.txt
|
122
163
|
- README.md
|
123
164
|
- Rakefile
|
@@ -127,8 +168,9 @@ files:
|
|
127
168
|
- lib/ldclient-rb/ldclient.rb
|
128
169
|
- lib/ldclient-rb/newrelic.rb
|
129
170
|
- lib/ldclient-rb/store.rb
|
171
|
+
- lib/ldclient-rb/stream.rb
|
130
172
|
- lib/ldclient-rb/version.rb
|
131
|
-
homepage:
|
173
|
+
homepage: https://rubygems.org/gems/ldclient-rb
|
132
174
|
licenses:
|
133
175
|
- Apache 2.0
|
134
176
|
metadata: {}
|
data/Gemfile.lock
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
ldclient-rb (0.1.0)
|
5
|
-
faraday (~> 0.9)
|
6
|
-
faraday-http-cache (~> 0.4)
|
7
|
-
json (~> 1.8)
|
8
|
-
net-http-persistent (~> 2.9.4)
|
9
|
-
thread_safe (~> 0.3)
|
10
|
-
|
11
|
-
GEM
|
12
|
-
remote: https://rubygems.org/
|
13
|
-
specs:
|
14
|
-
faraday (0.9.1)
|
15
|
-
multipart-post (>= 1.2, < 3)
|
16
|
-
faraday-http-cache (0.4.2)
|
17
|
-
faraday (~> 0.8)
|
18
|
-
json (1.8.2)
|
19
|
-
multipart-post (2.0.0)
|
20
|
-
net-http-persistent (2.9.4)
|
21
|
-
rake (10.4.2)
|
22
|
-
thread_safe (0.3.4)
|
23
|
-
|
24
|
-
PLATFORMS
|
25
|
-
ruby
|
26
|
-
|
27
|
-
DEPENDENCIES
|
28
|
-
bundler (~> 1.7)
|
29
|
-
ldclient-rb!
|
30
|
-
rake (~> 10.0)
|