diff --git a/.gitignore b/.gitignore index 10b25ba0..bb576123 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ *.a mkmf.log *.gem -Gemfile.lock \ No newline at end of file +Gemfile.lock +.DS_Store diff --git a/.rubocop.yml b/.rubocop.yml index 5bbd7cde..85b05f8b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -94,7 +94,7 @@ Rails/Delegate: Description: 'Prefer delegate method for delegations.' Enabled: false -Style/DeprecatedHashMethods: +Style/PreferredHashMethods: Description: 'Checks for use of deprecated Hash methods.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key' Enabled: false @@ -103,11 +103,6 @@ Style/Documentation: Description: 'Document classes and non-namespace modules.' Enabled: false -Style/DotPosition: - Description: 'Checks the position of the dot in multi-line method calls.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains' - EnforcedStyle: trailing - Style/DoubleNegation: Description: 'Checks for uses of double negation (!!).' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang' @@ -133,10 +128,6 @@ Style/EvenOdd: StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' Enabled: false -Style/ExtraSpacing: - Description: 'Do not use unnecessary spacing.' - Enabled: true - Style/FileName: Description: 'Use snake_case for source file names.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files' @@ -196,9 +187,9 @@ Style/LineEndConcatenation: Enabled: false Metrics/LineLength: - Description: 'Limit lines to 100 characters.' + Description: 'Limit lines to 150 characters.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits' - Max: 100 + Max: 150 Metrics/MethodLength: Description: 'Avoid methods longer than 10 lines of code.' @@ -210,13 +201,6 @@ Style/ModuleFunction: StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function' Enabled: false -Style/MultilineOperationIndentation: - Description: >- - Checks indentation of binary operations that span more than - one line. - Enabled: true - EnforcedStyle: indented - Style/NegatedIf: Description: >- Favor unless over if for negative conditions @@ -334,13 +318,14 @@ Style/StringLiterals: EnforcedStyle: double_quotes Enabled: true -Style/TrailingComma: - Description: 'Checks for trailing comma in parameter lists and literals.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' - EnforcedStyleForMultiline: comma - SupportedStyles: - - comma - - no_comma +Style/TrailingCommaInArguments: + Description: 'Checks for trailing comma in argument lists.' + StyleGuide: '#no-trailing-params-comma' + Enabled: true + +Style/TrailingCommaInLiteral: + Description: 'Checks for trailing comma in array and hash literals.' + StyleGuide: '#no-trailing-array-commas' Enabled: true Style/TrivialAccessors: @@ -372,6 +357,29 @@ Style/WordArray: StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w' Enabled: false +# Layout +Layout/DotPosition: + Description: 'Checks the position of the dot in multi-line method calls.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains' + EnforcedStyle: trailing + +Layout/ExtraSpacing: + Description: 'Do not use unnecessary spacing.' + Enabled: true + +Layout/MultilineOperationIndentation: + Description: >- + Checks indentation of binary operations that span more than + one line. + Enabled: true + EnforcedStyle: indented + +Layout/InitialIndentation: + Description: >- + Checks the indentation of the first non-blank non-comment line in a file. + Enabled: false + + # Lint Lint/AmbiguousOperator: @@ -434,11 +442,6 @@ Lint/InvalidCharacterLiteral: whitespace character. Enabled: false -Style/InitialIndentation: - Description: >- - Checks the indentation of the first non-blank non-comment line in a file. - Enabled: false - Lint/LiteralInCondition: Description: 'Checks of literals used in conditions.' Enabled: false @@ -560,10 +563,6 @@ Rails/Date: such as Date.today, Date.current etc. Enabled: false -Rails/DefaultScope: - Description: 'Checks if the argument passed to default_scope is a block.' - Enabled: false - Rails/FindBy: Description: 'Prefer find_by over where.first.' Enabled: false diff --git a/README.md b/README.md index 86153eca..6ae35805 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,9 @@ Quick setup 0. Install the Ruby SDK with `gem` ```shell -gem install ldclient-rb +gem install ldclient-rb --prerelease ``` +Note: The `--prerelease` flag is there to satisfy the dependency of celluloid 0.18pre which we have tested extensively and have found stable in our use case. Unfortunately, the upstream provider has not promoted this version to stable yet. See [here](https://github.com/celluloid/celluloid/issues/762) This is not required for use in a Gemfile. 1. Require the LaunchDarkly client: diff --git a/ext/mkrf_conf.rb b/ext/mkrf_conf.rb index bb77ec5b..23c2c7b6 100644 --- a/ext/mkrf_conf.rb +++ b/ext/mkrf_conf.rb @@ -1,11 +1,11 @@ -require 'rubygems' +require "rubygems" # From http://stackoverflow.com/questions/5830835/how-to-add-openssl-dependency-to-gemspec # the whole reason this file exists: to return an error if openssl # isn't installed. -require 'openssl' +require "openssl" f = File.open(File.join(File.dirname(__FILE__), "Rakefile"), "w") # create dummy rakefile to indicate success f.write("task :default\n") -f.close \ No newline at end of file +f.close diff --git a/ldclient-rb.gemspec b/ldclient-rb.gemspec index e44ec4f4..ee640e0a 100644 --- a/ldclient-rb.gemspec +++ b/ldclient-rb.gemspec @@ -1,8 +1,10 @@ # coding: utf-8 + lib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "ldclient-rb/version" +# rubocop:disable Metrics/BlockLength Gem::Specification.new do |spec| spec.name = "ldclient-rb" spec.version = LaunchDarkly::VERSION @@ -33,8 +35,8 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency "hashdiff", "~> 0.2" spec.add_runtime_dependency "ld-celluloid-eventsource", "~> 0.10.0" spec.add_runtime_dependency "celluloid", "~> 0.18.0.pre" # transitive dep; specified here for more control - - if RUBY_VERSION >= '2.2.2' + + if RUBY_VERSION >= "2.2.2" spec.add_runtime_dependency "nio4r", "< 3" # for maximum ruby version compatibility. else spec.add_runtime_dependency "nio4r", "~> 1.1" # for maximum ruby version compatibility. diff --git a/lib/ldclient-rb/config.rb b/lib/ldclient-rb/config.rb index 6f02266e..8945c44b 100644 --- a/lib/ldclient-rb/config.rb +++ b/lib/ldclient-rb/config.rb @@ -42,6 +42,7 @@ class Config # @option opts [Boolean] :stream (true) Whether or not the streaming API should be used to receive flag updates. # # @return [type] [description] + # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity def initialize(opts = {}) @base_uri = (opts[:base_uri] || Config.default_base_uri).chomp("/") @stream_uri = (opts[:stream_uri] || Config.default_stream_uri).chomp("/") diff --git a/lib/ldclient-rb/evaluation.rb b/lib/ldclient-rb/evaluation.rb index 90df5159..4a6c942f 100644 --- a/lib/ldclient-rb/evaluation.rb +++ b/lib/ldclient-rb/evaluation.rb @@ -1,20 +1,19 @@ require "date" module LaunchDarkly - module Evaluation BUILTINS = [:key, :ip, :country, :email, :firstName, :lastName, :avatar, :name, :anonymous] OPERATORS = { - in: + in: lambda do |a, b| a == b end, - endsWith: + endsWith: lambda do |a, b| (a.is_a? String) && (a.end_with? b) end, - startsWith: + startsWith: lambda do |a, b| (a.is_a? String) && (a.start_with? b) end, @@ -50,7 +49,7 @@ module Evaluation end if b.is_a? String b = DateTime.rfc3339(b).strftime('%Q').to_i - end + end (a.is_a? Numeric) ? a < b : false rescue => e false @@ -60,11 +59,11 @@ module Evaluation lambda do |a, b| begin if a.is_a? String - a = DateTime.rfc3339(a).strftime('%Q').to_i + a = DateTime.rfc3339(a).strftime("%Q").to_i end if b.is_a? String - b = DateTime.rfc3339(b).strftime('%Q').to_i - end + b = DateTime.rfc3339(b).strftime("%Q").to_i + end (a.is_a? Numeric) ? a > b : false rescue => e false @@ -93,15 +92,15 @@ def evaluate(flag, user, store) if flag[:on] res = eval_internal(flag, user, store, events) - return {value: res, events: events} if !res.nil? + return { value: res, events: events } if !res.nil? end if !flag[:offVariation].nil? && flag[:offVariation] < flag[:variations].length value = flag[:variations][flag[:offVariation]] - return {value: value, events: events} + return { value: value, events: events } end - {value: nil, events: events} + { value: nil, events: events } end def eval_internal(flag, user, store, events) @@ -109,7 +108,6 @@ def eval_internal(flag, user, store, events) # Evaluate prerequisites, if any if !flag[:prerequisites].nil? flag[:prerequisites].each do |prerequisite| - prereq_flag = store.get(prerequisite[:key]) if prereq_flag.nil? || !prereq_flag[:on] @@ -119,7 +117,7 @@ def eval_internal(flag, user, store, events) prereq_res = eval_internal(prereq_flag, user, store, events) variation = get_variation(prereq_flag, prerequisite[:variation]) events.push(kind: "feature", key: prereq_flag[:key], value: prereq_res, version: prereq_flag[:version], prereqOf: flag[:key]) - if prereq_res.nil? || prereq_res!= variation + if prereq_res.nil? || prereq_res != variation failed_prereq = true end rescue => exn @@ -149,7 +147,7 @@ def eval_rules(flag, user) end end end - end + end # Check custom rules if !flag[:rules].nil? @@ -202,7 +200,7 @@ def clause_match_user(clause, user) end maybe_negate(clause, match_any(op, val, clause[:values])) - end + end def variation_for_user(rule, user, flag) if !rule[:variation].nil? # fixed variation @@ -234,7 +232,7 @@ def bucket_user(user, key, bucket_by, salt) hash_key = "%s.%s.%s" % [key, salt, id_hash] hash_val = (Digest::SHA1.hexdigest(hash_key))[0..14] - hash_val.to_i(16) / Float(0xFFFFFFFFFFFFFFF) + hash_val.to_i(16) / Float(0xFFFFFFFFFFFFFFF) end def user_value(user, attribute) @@ -260,6 +258,4 @@ def match_any(op, value, values) return false end end - end - diff --git a/lib/ldclient-rb/events.rb b/lib/ldclient-rb/events.rb index b26f2722..1d54b845 100644 --- a/lib/ldclient-rb/events.rb +++ b/lib/ldclient-rb/events.rb @@ -2,7 +2,6 @@ require "faraday" module LaunchDarkly - class EventProcessor def initialize(sdk_key, config) @queue = Queue.new @@ -67,9 +66,8 @@ def add_event(event) else @config.logger.warn("[LDClient] Exceeded event queue capacity. Increase capacity to avoid dropping events.") end - end + end private :create_worker, :post_flushed_events - end -end \ No newline at end of file +end diff --git a/lib/ldclient-rb/feature_store.rb b/lib/ldclient-rb/feature_store.rb index 332ed804..39bc7e1f 100644 --- a/lib/ldclient-rb/feature_store.rb +++ b/lib/ldclient-rb/feature_store.rb @@ -1,7 +1,6 @@ require "concurrent/atomics" module LaunchDarkly - class InMemoryFeatureStore def initialize @features = Hash.new @@ -57,4 +56,4 @@ def initialized? @initialized.value end end -end \ No newline at end of file +end diff --git a/lib/ldclient-rb/ldclient.rb b/lib/ldclient-rb/ldclient.rb index 611bdc23..f057550a 100644 --- a/lib/ldclient-rb/ldclient.rb +++ b/lib/ldclient-rb/ldclient.rb @@ -32,7 +32,7 @@ def initialize(sdk_key, config = Config.default, wait_for_sec = 5) if !@config.offline? if @config.stream? @update_processor = StreamProcessor.new(sdk_key, config, requestor) - else + else @update_processor = PollingProcessor.new(config, requestor) end @update_processor.start @@ -42,7 +42,7 @@ def initialize(sdk_key, config = Config.default, wait_for_sec = 5) if !@config.offline? && wait_for_sec > 0 begin - WaitUtil.wait_for_condition("LaunchDarkly client initialization", :timeout_sec => wait_for_sec, :delay_sec => 0.1) do + WaitUtil.wait_for_condition("LaunchDarkly client initialization", timeout_sec: wait_for_sec, delay_sec: 0.1) do @update_processor.initialized? end rescue WaitUtil::TimeoutError @@ -61,7 +61,7 @@ def toggle?(key, user, default = False) end def secure_mode_hash(user) - OpenSSL::HMAC.hexdigest('sha256', @sdk_key, user[:key].to_s) + OpenSSL::HMAC.hexdigest("sha256", @sdk_key, user[:key].to_s) end # Returns whether the client has been initialized and is ready to serve feature flag requests @@ -107,13 +107,13 @@ def variation(key, user, default) unless user @config.logger.error("[LDClient] Must specify user") - @event_processor.add_event(kind: "feature", key: key, value: default, default: default, user: user) + @event_processor.add_event(kind: "feature", key: key, value: default, default: default, user: user) return default end if !@update_processor.initialized? @config.logger.error("[LDClient] Client has not finished initializing. Returning default value") - @event_processor.add_event(kind: "feature", key: key, value: default, default: default, user: user) + @event_processor.add_event(kind: "feature", key: key, value: default, default: default, user: user) return default end @@ -138,8 +138,8 @@ def variation(key, user, default) return res[:value] else @config.logger.debug("[LDClient] Result value is null in toggle") - @event_processor.add_event(kind: "feature", key: key, user: user, value: default, default: default, version: feature[:version]) - return default + @event_processor.add_event(kind: "feature", key: key, user: user, value: default, default: default, version: feature[:version]) + return default end rescue => exn @config.logger.warn("[LDClient] Error evaluating feature flag: #{exn.inspect}. \nTrace: #{exn.backtrace}") @@ -188,7 +188,7 @@ def all_flags(user) features = @store.all # TODO rescue if necessary - Hash[features.map{|k,f| [k, evaluate(f, user, @store)[:value]] }] + Hash[features.map{ |k, f| [k, evaluate(f, user, @store)[:value]] }] rescue => exn @config.logger.warn("[LDClient] Error evaluating all flags: #{exn.inspect}. \nTrace: #{exn.backtrace}") return Hash.new diff --git a/lib/ldclient-rb/polling.rb b/lib/ldclient-rb/polling.rb index f3e3bf69..7672c1ab 100644 --- a/lib/ldclient-rb/polling.rb +++ b/lib/ldclient-rb/polling.rb @@ -3,7 +3,6 @@ module LaunchDarkly class PollingProcessor - def initialize(config, requestor) @config = config @requestor = requestor @@ -44,13 +43,12 @@ def create_worker end rescue StandardError => exn @config.logger.error("[LDClient] Exception while polling: #{exn.inspect}") - # TODO: log_exception(__method__.to_s, exn) + # TODO: log_exception(__method__.to_s, exn) end end end end - private :poll, :create_worker end -end \ No newline at end of file +end diff --git a/lib/ldclient-rb/requestor.rb b/lib/ldclient-rb/requestor.rb index 1da7c784..86e6e555 100644 --- a/lib/ldclient-rb/requestor.rb +++ b/lib/ldclient-rb/requestor.rb @@ -56,7 +56,5 @@ def make_request(path) end private :make_request - end - -end \ No newline at end of file +end diff --git a/lib/ldclient-rb/stream.rb b/lib/ldclient-rb/stream.rb index 2c432fb2..8286b610 100644 --- a/lib/ldclient-rb/stream.rb +++ b/lib/ldclient-rb/stream.rb @@ -61,7 +61,7 @@ def process_message(message, method) @initialized.make_true @config.logger.info("[LDClient] Stream initialized (via indirect message)") elsif method == INDIRECT_PATCH - @store.upsert(message.data, @requestor.request_flag(message.data)) + @store.upsert(message.data, @requestor.request_flag(message.data)) else @config.logger.warn("[LDClient] Unknown message received: #{method}") end diff --git a/lib/ldclient-rb/version.rb b/lib/ldclient-rb/version.rb index ac30aecb..7122b4f5 100644 --- a/lib/ldclient-rb/version.rb +++ b/lib/ldclient-rb/version.rb @@ -1,3 +1,3 @@ module LaunchDarkly - VERSION = "2.2.5" + VERSION = "2.2.6" end diff --git a/script/release.sh b/script/release.sh new file mode 100755 index 00000000..18537846 --- /dev/null +++ b/script/release.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# This script updates the version for the ldclient library and releases it to RubyGems +# It will only work if you have the proper credentials set up in ~/.gem/credentials + +# It takes exactly one argument: the new version. +# It should be run from the root of this git repo like this: +# ./scripts/release.sh 4.0.9 + +# When done you should commit and push the changes made. + +set -uxe +echo "Starting ruby-client release." + +VERSION=$1 + +#Update version in ldclient/version.py +VERSION_RB_TEMP=./version.rb.tmp +sed "s/VERSION =.*/VERSION = \"${VERSION}\"/g" lib/ldclient-rb/version.rb > ${VERSION_RB_TEMP} +mv ${VERSION_RB_TEMP} lib/ldclient-rb/version.rb + +# Build Ruby Gem +gem build ldclient-rb.gemspec + +# Publish Ruby Gem +gem push ldclient-rb-${VERSION}.gem + +echo "Done with ruby-client release" \ No newline at end of file diff --git a/spec/ldclient_spec.rb b/spec/ldclient_spec.rb index 0bd872cd..47ee8f44 100644 --- a/spec/ldclient_spec.rb +++ b/spec/ldclient_spec.rb @@ -3,7 +3,7 @@ describe LaunchDarkly::LDClient do subject { LaunchDarkly::LDClient } - let(:config) { LaunchDarkly::Config.new({:offline => true}) } + let(:config) { LaunchDarkly::Config.new({offline: true}) } let(:client) do subject.new("secret", config) end @@ -33,7 +33,7 @@ describe '#secure_mode_hash' do it "will return the expected value for a known message and secret" do - result = client.secure_mode_hash({:key => :Message}) + result = client.secure_mode_hash({key: :Message}) expect(result).to eq "aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597" end end