Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
prepare 5.5.3 release (#127)
  • Loading branch information
eli-darkly committed Feb 14, 2019
1 parent 0514392 commit 3834230
Show file tree
Hide file tree
Showing 15 changed files with 469 additions and 182 deletions.
12 changes: 3 additions & 9 deletions Gemfile.lock
@@ -1,13 +1,10 @@
PATH
remote: .
specs:
ldclient-rb (5.4.3)
ldclient-rb (5.5.2)
concurrent-ruby (~> 1.0)
faraday (>= 0.9, < 2)
faraday-http-cache (>= 1.3.0, < 3)
json (>= 1.8, < 3)
ld-eventsource (~> 1.0)
net-http-persistent (>= 2.9, < 4.0)
semantic (~> 1.6)

GEM
Expand Down Expand Up @@ -35,11 +32,10 @@ GEM
docile (1.1.5)
faraday (0.15.4)
multipart-post (>= 1.2, < 3)
faraday-http-cache (2.0.0)
faraday (~> 0.8)
ffi (1.9.25)
ffi (1.9.25-java)
hitimes (1.3.0)
hitimes (1.3.1)
hitimes (1.3.1-java)
http_tools (0.4.5)
jmespath (1.4.0)
json (1.8.6)
Expand All @@ -53,8 +49,6 @@ GEM
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
multipart-post (2.0.0)
net-http-persistent (3.0.0)
connection_pool (~> 2.2)
rake (10.5.0)
rb-fsevent (0.10.3)
rb-inotify (0.9.10)
Expand Down
58 changes: 29 additions & 29 deletions README.md
Expand Up @@ -17,19 +17,19 @@ Quick setup

1. Install the Ruby SDK with `gem`

```shell
```shell
gem install ldclient-rb
```

2. Require the LaunchDarkly client:

```ruby
```ruby
require 'ldclient-rb'
```

3. Create a new LDClient with your SDK key:

```ruby
```ruby
client = LaunchDarkly::LDClient.new("your_sdk_key")
```

Expand All @@ -39,42 +39,42 @@ client = LaunchDarkly::LDClient.new("your_sdk_key")

2. Initialize the launchdarkly client in `config/initializers/launchdarkly.rb`:

```ruby
```ruby
Rails.configuration.ld_client = LaunchDarkly::LDClient.new("your_sdk_key")
```

3. You may want to include a function in your ApplicationController

```ruby
def launchdarkly_settings
if current_user.present?
{
key: current_user.id,
anonymous: false,
email: current_user.email,
custom: { groups: current_user.groups.pluck(:name) },
# Any other fields you may have
# e.g. lastName: current_user.last_name,
}
else
if Rails::VERSION::MAJOR <= 3
hash_key = request.session_options[:id]
else
hash_key = session.id
end
# session ids should be private to prevent session hijacking
hash_key = Digest::SHA256.base64digest hash_key
{
key: hash_key,
anonymous: true,
}
end
```ruby
def launchdarkly_settings
if current_user.present?
{
key: current_user.id,
anonymous: false,
email: current_user.email,
custom: { groups: current_user.groups.pluck(:name) },
# Any other fields you may have
# e.g. lastName: current_user.last_name,
}
else
if Rails::VERSION::MAJOR <= 3
hash_key = request.session_options[:id]
else
hash_key = session.id
end
# session ids should be private to prevent session hijacking
hash_key = Digest::SHA256.base64digest hash_key
{
key: hash_key,
anonymous: true,
}
end
end
```

4. In your controllers, access the client using

```ruby
```ruby
Rails.application.config.ld_client.variation('your.flag.key', launchdarkly_settings, false)
```

Expand Down
51 changes: 51 additions & 0 deletions azure-pipelines.yml
@@ -0,0 +1,51 @@
jobs:
- job: build
pool:
vmImage: 'vs2017-win2016'
steps:
- task: PowerShell@2
displayName: 'Setup Dynamo'
inputs:
targetType: inline
workingDirectory: $(System.DefaultWorkingDirectory)
script: |
iwr -outf dynamo.zip https://s3-us-west-2.amazonaws.com/dynamodb-local/dynamodb_local_latest.zip
mkdir dynamo
Expand-Archive -Path dynamo.zip -DestinationPath dynamo
cd dynamo
javaw -D"java.library.path=./DynamoDBLocal_lib" -jar DynamoDBLocal.jar
- task: PowerShell@2
displayName: 'Setup Consul'
inputs:
targetType: inline
workingDirectory: $(System.DefaultWorkingDirectory)
script: |
iwr -outf consul.zip https://releases.hashicorp.com/consul/1.4.2/consul_1.4.2_windows_amd64.zip
mkdir consul
Expand-Archive -Path consul.zip -DestinationPath consul
cd consul
sc.exe create "Consul" binPath="$(System.DefaultWorkingDirectory)/consul/consul.exe agent -dev"
sc.exe start "Consul"
- task: PowerShell@2
displayName: 'Setup Redis'
inputs:
targetType: inline
workingDirectory: $(System.DefaultWorkingDirectory)
script: |
iwr -outf redis.zip https://github.com/MicrosoftArchive/redis/releases/download/win-3.0.504/Redis-x64-3.0.504.zip
mkdir redis
Expand-Archive -Path redis.zip -DestinationPath redis
cd redis
./redis-server --service-install
./redis-server --service-start
- task: PowerShell@2
displayName: 'Setup SDK and Test'
inputs:
targetType: inline
workingDirectory: $(System.DefaultWorkingDirectory)
script: |
ruby -v
gem install bundler -v 1.17.3
bundle install
mkdir rspec
bundle exec rspec --format progress --format RspecJunitFormatter -o ./rspec/rspec.xml spec
3 changes: 0 additions & 3 deletions ldclient-rb.gemspec
Expand Up @@ -34,10 +34,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "listen", "~> 3.0" # see file_data_source.rb

spec.add_runtime_dependency "json", [">= 1.8", "< 3"]
spec.add_runtime_dependency "faraday", [">= 0.9", "< 2"]
spec.add_runtime_dependency "faraday-http-cache", [">= 1.3.0", "< 3"]
spec.add_runtime_dependency "semantic", "~> 1.6"
spec.add_runtime_dependency "net-http-persistent", [">= 2.9", "< 4.0"]
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
spec.add_runtime_dependency "ld-eventsource", '~> 1.0'
end
6 changes: 2 additions & 4 deletions lib/ldclient-rb/cache_store.rb
Expand Up @@ -2,11 +2,9 @@

module LaunchDarkly
#
# A thread-safe in-memory store suitable for use with the Faraday caching HTTP client. Uses the
# concurrent-ruby gem's Map as the underlying cache.
# A thread-safe in-memory store that uses the same semantics that Faraday would expect, although we
# no longer use Faraday. This is used by Requestor, when we are not in a Rails environment.
#
# @see https://github.com/plataformatec/faraday-http-cache
# @see https://github.com/ruby-concurrency
# @private
#
class ThreadSafeMemoryStore
Expand Down
22 changes: 4 additions & 18 deletions lib/ldclient-rb/config.rb
Expand Up @@ -53,7 +53,6 @@ def initialize(opts = {})
@use_ldd = opts.has_key?(:use_ldd) ? opts[:use_ldd] : Config.default_use_ldd
@offline = opts.has_key?(:offline) ? opts[:offline] : Config.default_offline
@poll_interval = opts.has_key?(:poll_interval) && opts[:poll_interval] > Config.default_poll_interval ? opts[:poll_interval] : Config.default_poll_interval
@proxy = opts[:proxy] || Config.default_proxy
@all_attributes_private = opts[:all_attributes_private] || false
@private_attribute_names = opts[:private_attribute_names] || []
@send_events = opts.has_key?(:send_events) ? opts[:send_events] : Config.default_send_events
Expand Down Expand Up @@ -153,9 +152,10 @@ def offline?
attr_reader :capacity

#
# A store for HTTP caching. This must support the semantics used by the
# [`faraday-http-cache`](https://github.com/plataformatec/faraday-http-cache) gem. Defaults
# to the Rails cache in a Rails environment, or a thread-safe in-memory store otherwise.
# A store for HTTP caching (used only in polling mode). This must support the semantics used by
# the [`faraday-http-cache`](https://github.com/plataformatec/faraday-http-cache) gem, although
# the SDK no longer uses Faraday. Defaults to the Rails cache in a Rails environment, or a
# thread-safe in-memory store otherwise.
# @return [Object]
#
attr_reader :cache_store
Expand Down Expand Up @@ -184,12 +184,6 @@ def offline?
#
attr_reader :feature_store

#
# The proxy configuration string.
# @return [String]
#
attr_reader :proxy

#
# True if all user attributes (other than the key) should be considered private. This means
# that the attribute values will not be sent to LaunchDarkly in analytics events and will not
Expand Down Expand Up @@ -336,14 +330,6 @@ def self.default_connect_timeout
2
end

#
# The default value for {#proxy}.
# @return [String] nil
#
def self.default_proxy
nil
end

#
# The default value for {#logger}.
# @return [Logger] the Rails logger if in Rails, or a default Logger at WARN level otherwise
Expand Down
51 changes: 31 additions & 20 deletions lib/ldclient-rb/events.rb
Expand Up @@ -3,7 +3,6 @@
require "concurrent/executors"
require "thread"
require "time"
require "faraday"

module LaunchDarkly
MAX_FLUSH_WORKERS = 5
Expand Down Expand Up @@ -115,11 +114,17 @@ class EventDispatcher
def initialize(queue, sdk_key, config, client)
@sdk_key = sdk_key
@config = config
@client = client ? client : Faraday.new

if client
@client = client
else
@client = Util.new_http_client(@config.events_uri, @config)
end

@user_keys = SimpleLRUCacheSet.new(config.user_keys_capacity)
@formatter = EventOutputFormatter.new(config)
@disabled = Concurrent::AtomicBoolean.new(false)
@last_known_past_time = Concurrent::AtomicFixnum.new(0)
@last_known_past_time = Concurrent::AtomicReference.new(0)

buffer = EventBuffer.new(config.capacity, config.logger)
flush_workers = NonBlockingThreadPool.new(MAX_FLUSH_WORKERS)
Expand Down Expand Up @@ -162,7 +167,10 @@ def main_loop(queue, buffer, flush_workers)
def do_shutdown(flush_workers)
flush_workers.shutdown
flush_workers.wait_for_termination
# There seems to be no such thing as "close" in Faraday: https://github.com/lostisland/faraday/issues/241
begin
@client.finish
rescue
end
end

def synchronize_for_testing(flush_workers)
Expand Down Expand Up @@ -246,16 +254,17 @@ def trigger_flush(buffer, flush_workers)
end

def handle_response(res)
if res.status >= 400
message = Util.http_error_message(res.status, "event delivery", "some events were dropped")
status = res.code.to_i
if status >= 400
message = Util.http_error_message(status, "event delivery", "some events were dropped")
@config.logger.error { "[LDClient] #{message}" }
if !Util.http_error_recoverable?(res.status)
if !Util.http_error_recoverable?(status)
@disabled.value = true
end
else
if !res.headers.nil? && res.headers.has_key?("Date")
if !res["date"].nil?
begin
res_time = (Time.httpdate(res.headers["Date"]).to_f * 1000).to_i
res_time = (Time.httpdate(res["date"]).to_f * 1000).to_i
@last_known_past_time.value = res_time
rescue ArgumentError
end
Expand Down Expand Up @@ -316,22 +325,24 @@ def run(sdk_key, config, client, payload, formatter)
sleep(1)
end
begin
client.start if !client.started?
config.logger.debug { "[LDClient] sending #{events_out.length} events: #{body}" }
res = client.post (config.events_uri + "/bulk") do |req|
req.headers["Authorization"] = sdk_key
req.headers["User-Agent"] = "RubyClient/" + LaunchDarkly::VERSION
req.headers["Content-Type"] = "application/json"
req.headers["X-LaunchDarkly-Event-Schema"] = CURRENT_SCHEMA_VERSION.to_s
req.body = body
req.options.timeout = config.read_timeout
req.options.open_timeout = config.connect_timeout
end
uri = URI(config.events_uri + "/bulk")
req = Net::HTTP::Post.new(uri)
req.content_type = "application/json"
req.body = body
req["Authorization"] = sdk_key
req["User-Agent"] = "RubyClient/" + LaunchDarkly::VERSION
req["X-LaunchDarkly-Event-Schema"] = CURRENT_SCHEMA_VERSION.to_s
req["Connection"] = "keep-alive"
res = client.request(req)
rescue StandardError => exn
config.logger.warn { "[LDClient] Error flushing events: #{exn.inspect}." }
next
end
if res.status < 200 || res.status >= 300
if Util.http_error_recoverable?(res.status)
status = res.code.to_i
if status < 200 || status >= 300
if Util.http_error_recoverable?(status)
next
end
end
Expand Down
5 changes: 2 additions & 3 deletions lib/ldclient-rb/polling.rb
Expand Up @@ -26,7 +26,7 @@ def start

def stop
if @stopped.make_true
if @worker && @worker.alive?
if @worker && @worker.alive? && @worker != Thread.current
@worker.run # causes the thread to wake up if it's currently in a sleep
@worker.join
end
Expand Down Expand Up @@ -63,8 +63,7 @@ def create_worker
stop
end
rescue StandardError => exn
@config.logger.error { "[LDClient] Exception while polling: #{exn.inspect}" }
# TODO: log_exception(__method__.to_s, exn)
Util.log_exception(@config.logger, "Exception while polling", exn)
end
delta = @config.poll_interval - (Time.now - started_at)
if delta > 0
Expand Down

0 comments on commit 3834230

Please sign in to comment.