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