launchdarkly-server-sdk 5.5.12 → 5.6.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/.ldrelease/config.yml +17 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +1 -1
- data/lib/ldclient-rb/evaluation.rb +7 -17
- data/lib/ldclient-rb/events.rb +1 -0
- data/lib/ldclient-rb/impl/event_factory.rb +98 -0
- data/lib/ldclient-rb/ldclient.rb +36 -29
- data/lib/ldclient-rb/version.rb +1 -1
- data/spec/evaluation_spec.rb +38 -40
- data/spec/events_spec.rb +2 -1
- data/spec/ldclient_spec.rb +82 -2
- metadata +5 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 502749fe2b55f4a3116ea223872835e76f7f698b
         | 
| 4 | 
            +
              data.tar.gz: 9ab5eae90b672dc39db4c3487bd67aa32878b467
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: a4f9f6642ab989aab6069924fc8b9f543123672cf475be58b72e1410a2563f246ce40d0cfef34dad0c77c37e28d695d05368c719df889eb5766d3618b19848b3
         | 
| 7 | 
            +
              data.tar.gz: 8c3af4780de0251380f194c8e4c4c3ce359f2a1612763e45a2a8b2eadab3d0dd20f83d5f0d60f8efb45759f5f8a4af7ccc6143085d2999065e4ca8791cf50291
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            repo:
         | 
| 2 | 
            +
              public: ruby-server-sdk
         | 
| 3 | 
            +
              private: ruby-server-sdk-private
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            publications:
         | 
| 6 | 
            +
              - url: https://rubygems.org/gems/launchdarkly-server-sdk
         | 
| 7 | 
            +
                description: RubyGems
         | 
| 8 | 
            +
              - url: https://www.rubydoc.info/gems/launchdarkly-server-sdk
         | 
| 9 | 
            +
                description: documentation
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            template:
         | 
| 12 | 
            +
              name: ruby
         | 
| 13 | 
            +
              env:
         | 
| 14 | 
            +
                LD_SKIP_DATABASE_TESTS: 1  # Don't run Redis/Consul/DynamoDB tests in release; they are run in CI
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            sdk:
         | 
| 17 | 
            +
              displayName: "Ruby"
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -2,6 +2,10 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            All notable changes to the LaunchDarkly Ruby SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).
         | 
| 4 4 |  | 
| 5 | 
            +
            ## [5.6.0] - 2019-08-20
         | 
| 6 | 
            +
            ### Added:
         | 
| 7 | 
            +
            - Added support for upcoming LaunchDarkly experimentation features. See `LDClient.track()`.
         | 
| 8 | 
            +
             | 
| 5 9 | 
             
            ## [5.5.12] - 2019-08-05
         | 
| 6 10 | 
             
            ### Fixed:
         | 
| 7 11 | 
             
            - Under conditions where analytics events are being generated at an extremely high rate (for instance, if an application is evaluating a flag repeatedly in a tight loop on many threads), it was possible for the internal event processing logic to fall behind on processing the events, causing them to use more and more memory. The logic has been changed to drop events if necessary so that besides the existing limit on the number of events waiting to be sent to LaunchDarkly (`config.capacity`), the same limit also applies on the number of events that are waiting to be processed by the worker thread that decides whether or not to send them to LaunchDarkly. If that limit is exceeded, this warning message will be logged once: "Events are being produced faster than they can be processed; some events will be dropped". Under normal conditions this should never happen; this change is meant to avoid a concurrency bottleneck in applications that are already so busy that thread starvation is likely.
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
| @@ -199,7 +199,7 @@ module LaunchDarkly | |
| 199 199 |  | 
| 200 200 | 
             
                # Evaluates a feature flag and returns an EvalResult. The result.value will be nil if the flag returns
         | 
| 201 201 | 
             
                # the default value. Error conditions produce a result with an error reason, not an exception.
         | 
| 202 | 
            -
                def evaluate(flag, user, store, logger)
         | 
| 202 | 
            +
                def evaluate(flag, user, store, logger, event_factory)
         | 
| 203 203 | 
             
                  if user.nil? || user[:key].nil?
         | 
| 204 204 | 
             
                    return EvalResult.new(error_result('USER_NOT_SPECIFIED'), [])
         | 
| 205 205 | 
             
                  end
         | 
| @@ -207,16 +207,16 @@ module LaunchDarkly | |
| 207 207 | 
             
                  sanitized_user = Util.stringify_attrs(user, USER_ATTRS_TO_STRINGIFY_FOR_EVALUATION)
         | 
| 208 208 |  | 
| 209 209 | 
             
                  events = []
         | 
| 210 | 
            -
                  detail = eval_internal(flag, sanitized_user, store, events, logger)
         | 
| 210 | 
            +
                  detail = eval_internal(flag, sanitized_user, store, events, logger, event_factory)
         | 
| 211 211 | 
             
                  return EvalResult.new(detail, events)
         | 
| 212 212 | 
             
                end
         | 
| 213 213 |  | 
| 214 | 
            -
                def eval_internal(flag, user, store, events, logger)
         | 
| 214 | 
            +
                def eval_internal(flag, user, store, events, logger, event_factory)
         | 
| 215 215 | 
             
                  if !flag[:on]
         | 
| 216 216 | 
             
                    return get_off_value(flag, { kind: 'OFF' }, logger)
         | 
| 217 217 | 
             
                  end
         | 
| 218 218 |  | 
| 219 | 
            -
                  prereq_failure_reason = check_prerequisites(flag, user, store, events, logger)
         | 
| 219 | 
            +
                  prereq_failure_reason = check_prerequisites(flag, user, store, events, logger, event_factory)
         | 
| 220 220 | 
             
                  if !prereq_failure_reason.nil?
         | 
| 221 221 | 
             
                    return get_off_value(flag, prereq_failure_reason, logger)
         | 
| 222 222 | 
             
                  end
         | 
| @@ -249,7 +249,7 @@ module LaunchDarkly | |
| 249 249 | 
             
                  return EvaluationDetail.new(nil, nil, { kind: 'FALLTHROUGH' })
         | 
| 250 250 | 
             
                end
         | 
| 251 251 |  | 
| 252 | 
            -
                def check_prerequisites(flag, user, store, events, logger)
         | 
| 252 | 
            +
                def check_prerequisites(flag, user, store, events, logger, event_factory)
         | 
| 253 253 | 
             
                  (flag[:prerequisites] || []).each do |prerequisite|
         | 
| 254 254 | 
             
                    prereq_ok = true
         | 
| 255 255 | 
             
                    prereq_key = prerequisite[:key]
         | 
| @@ -260,23 +260,13 @@ module LaunchDarkly | |
| 260 260 | 
             
                      prereq_ok = false
         | 
| 261 261 | 
             
                    else
         | 
| 262 262 | 
             
                      begin
         | 
| 263 | 
            -
                        prereq_res = eval_internal(prereq_flag, user, store, events, logger)
         | 
| 263 | 
            +
                        prereq_res = eval_internal(prereq_flag, user, store, events, logger, event_factory)
         | 
| 264 264 | 
             
                        # Note that if the prerequisite flag is off, we don't consider it a match no matter what its
         | 
| 265 265 | 
             
                        # off variation was. But we still need to evaluate it in order to generate an event.
         | 
| 266 266 | 
             
                        if !prereq_flag[:on] || prereq_res.variation_index != prerequisite[:variation]
         | 
| 267 267 | 
             
                          prereq_ok = false
         | 
| 268 268 | 
             
                        end
         | 
| 269 | 
            -
                        event =  | 
| 270 | 
            -
                          kind: "feature",
         | 
| 271 | 
            -
                          key: prereq_key,
         | 
| 272 | 
            -
                          user: user,
         | 
| 273 | 
            -
                          variation: prereq_res.variation_index,
         | 
| 274 | 
            -
                          value: prereq_res.value,
         | 
| 275 | 
            -
                          version: prereq_flag[:version],
         | 
| 276 | 
            -
                          prereqOf: flag[:key],
         | 
| 277 | 
            -
                          trackEvents: prereq_flag[:trackEvents],
         | 
| 278 | 
            -
                          debugEventsUntilDate: prereq_flag[:debugEventsUntilDate]
         | 
| 279 | 
            -
                        }
         | 
| 269 | 
            +
                        event = event_factory.new_eval_event(prereq_flag, user, prereq_res, nil, flag)
         | 
| 280 270 | 
             
                        events.push(event)
         | 
| 281 271 | 
             
                      rescue => exn
         | 
| 282 272 | 
             
                        Util.log_exception(logger, "Error evaluating prerequisite flag \"#{prereq_key}\" for flag \"#{flag[:key]}\"", exn)
         | 
    
        data/lib/ldclient-rb/events.rb
    CHANGED
    
    
| @@ -0,0 +1,98 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            module LaunchDarkly
         | 
| 3 | 
            +
              module Impl
         | 
| 4 | 
            +
                # Event constructors are centralized here to avoid mistakes and repetitive logic.
         | 
| 5 | 
            +
                # The LDClient owns two instances of EventFactory: one that always embeds evaluation reasons
         | 
| 6 | 
            +
                # in the events (for when variation_detail is called) and one that doesn't.
         | 
| 7 | 
            +
                #
         | 
| 8 | 
            +
                # Note that these methods do not set the "creationDate" property, because in the Ruby client,
         | 
| 9 | 
            +
                # that is done by EventProcessor.add_event().
         | 
| 10 | 
            +
                class EventFactory
         | 
| 11 | 
            +
                  def initialize(with_reasons)
         | 
| 12 | 
            +
                    @with_reasons = with_reasons
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def new_eval_event(flag, user, detail, default_value, prereq_of_flag = nil)
         | 
| 16 | 
            +
                    add_experiment_data = is_experiment(flag, detail.reason)
         | 
| 17 | 
            +
                    e = {
         | 
| 18 | 
            +
                      kind: 'feature',
         | 
| 19 | 
            +
                      key: flag[:key],
         | 
| 20 | 
            +
                      user: user,
         | 
| 21 | 
            +
                      variation: detail.variation_index,
         | 
| 22 | 
            +
                      value: detail.value,
         | 
| 23 | 
            +
                      default: default_value,
         | 
| 24 | 
            +
                      version: flag[:version]
         | 
| 25 | 
            +
                    }
         | 
| 26 | 
            +
                    # the following properties are handled separately so we don't waste bandwidth on unused keys
         | 
| 27 | 
            +
                    e[:trackEvents] = true if add_experiment_data || flag[:trackEvents]
         | 
| 28 | 
            +
                    e[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]
         | 
| 29 | 
            +
                    e[:prereqOf] = prereq_of_flag[:key] if !prereq_of_flag.nil?
         | 
| 30 | 
            +
                    e[:reason] = detail.reason if add_experiment_data || @with_reasons
         | 
| 31 | 
            +
                    e
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  def new_default_event(flag, user, default_value, reason)
         | 
| 35 | 
            +
                    e = {
         | 
| 36 | 
            +
                      kind: 'feature',
         | 
| 37 | 
            +
                      key: flag[:key],
         | 
| 38 | 
            +
                      user: user,
         | 
| 39 | 
            +
                      value: default_value,
         | 
| 40 | 
            +
                      default: default_value,
         | 
| 41 | 
            +
                      version: flag[:version]
         | 
| 42 | 
            +
                    }
         | 
| 43 | 
            +
                    e[:trackEvents] = true if flag[:trackEvents]
         | 
| 44 | 
            +
                    e[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]
         | 
| 45 | 
            +
                    e[:reason] = reason if @with_reasons
         | 
| 46 | 
            +
                    e
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  def new_unknown_flag_event(key, user, default_value, reason)
         | 
| 50 | 
            +
                    e = {
         | 
| 51 | 
            +
                      kind: 'feature',
         | 
| 52 | 
            +
                      key: key,
         | 
| 53 | 
            +
                      user: user,
         | 
| 54 | 
            +
                      value: default_value,
         | 
| 55 | 
            +
                      default: default_value
         | 
| 56 | 
            +
                    }
         | 
| 57 | 
            +
                    e[:reason] = reason if @with_reasons
         | 
| 58 | 
            +
                    e
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  def new_identify_event(user)
         | 
| 62 | 
            +
                    {
         | 
| 63 | 
            +
                      kind: 'identify',
         | 
| 64 | 
            +
                      key: user[:key],
         | 
| 65 | 
            +
                      user: user
         | 
| 66 | 
            +
                    }
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  def new_custom_event(event_name, user, data, metric_value)
         | 
| 70 | 
            +
                    e = {
         | 
| 71 | 
            +
                      kind: 'custom',
         | 
| 72 | 
            +
                      key: event_name,
         | 
| 73 | 
            +
                      user: user
         | 
| 74 | 
            +
                    }
         | 
| 75 | 
            +
                    e[:data] = data if !data.nil?
         | 
| 76 | 
            +
                    e[:metricValue] = metric_value if !metric_value.nil?
         | 
| 77 | 
            +
                    e
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  private
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  def is_experiment(flag, reason)
         | 
| 83 | 
            +
                    return false if !reason
         | 
| 84 | 
            +
                    case reason[:kind]
         | 
| 85 | 
            +
                    when 'RULE_MATCH'
         | 
| 86 | 
            +
                      index = reason[:ruleIndex]
         | 
| 87 | 
            +
                      if !index.nil?
         | 
| 88 | 
            +
                        rules = flag[:rules] || []
         | 
| 89 | 
            +
                        return index >= 0 && index < rules.length && rules[index][:trackEvents]
         | 
| 90 | 
            +
                      end
         | 
| 91 | 
            +
                    when 'FALLTHROUGH'
         | 
| 92 | 
            +
                      return !!flag[:trackEventsFallthrough]
         | 
| 93 | 
            +
                    end
         | 
| 94 | 
            +
                    false
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
            end
         | 
    
        data/lib/ldclient-rb/ldclient.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            require "ldclient-rb/impl/event_factory"
         | 
| 1 2 | 
             
            require "ldclient-rb/impl/store_client_wrapper"
         | 
| 2 3 | 
             
            require "concurrent/atomics"
         | 
| 3 4 | 
             
            require "digest/sha1"
         | 
| @@ -13,6 +14,7 @@ module LaunchDarkly | |
| 13 14 | 
             
              #
         | 
| 14 15 | 
             
              class LDClient
         | 
| 15 16 | 
             
                include Evaluation
         | 
| 17 | 
            +
                include Impl
         | 
| 16 18 | 
             
                #
         | 
| 17 19 | 
             
                # Creates a new client instance that connects to LaunchDarkly. A custom
         | 
| 18 20 | 
             
                # configuration parameter can also supplied to specify advanced options,
         | 
| @@ -32,6 +34,9 @@ module LaunchDarkly | |
| 32 34 | 
             
                def initialize(sdk_key, config = Config.default, wait_for_sec = 5)
         | 
| 33 35 | 
             
                  @sdk_key = sdk_key
         | 
| 34 36 |  | 
| 37 | 
            +
                  @event_factory_default = EventFactory.new(false)
         | 
| 38 | 
            +
                  @event_factory_with_reasons = EventFactory.new(true)
         | 
| 39 | 
            +
             | 
| 35 40 | 
             
                  # We need to wrap the feature store object with a FeatureStoreClientWrapper in order to add
         | 
| 36 41 | 
             
                  # some necessary logic around updates. Unfortunately, we have code elsewhere that accesses
         | 
| 37 42 | 
             
                  # the feature store through the Config object, so we need to make a new Config that uses
         | 
| @@ -165,7 +170,7 @@ module LaunchDarkly | |
| 165 170 | 
             
                # @return the variation to show the user, or the default value if there's an an error
         | 
| 166 171 | 
             
                #
         | 
| 167 172 | 
             
                def variation(key, user, default)
         | 
| 168 | 
            -
                  evaluate_internal(key, user, default,  | 
| 173 | 
            +
                  evaluate_internal(key, user, default, @event_factory_default).value
         | 
| 169 174 | 
             
                end
         | 
| 170 175 |  | 
| 171 176 | 
             
                #
         | 
| @@ -192,7 +197,7 @@ module LaunchDarkly | |
| 192 197 | 
             
                # @return [EvaluationDetail] an object describing the result
         | 
| 193 198 | 
             
                #
         | 
| 194 199 | 
             
                def variation_detail(key, user, default)
         | 
| 195 | 
            -
                  evaluate_internal(key, user, default,  | 
| 200 | 
            +
                  evaluate_internal(key, user, default, @event_factory_with_reasons)
         | 
| 196 201 | 
             
                end
         | 
| 197 202 |  | 
| 198 203 | 
             
                #
         | 
| @@ -215,7 +220,8 @@ module LaunchDarkly | |
| 215 220 | 
             
                    @config.logger.warn("Identify called with nil user or nil user key!")
         | 
| 216 221 | 
             
                    return
         | 
| 217 222 | 
             
                  end
         | 
| 218 | 
            -
                   | 
| 223 | 
            +
                  sanitize_user(user)
         | 
| 224 | 
            +
                  @event_processor.add_event(@event_factory_default.new_identify_event(user))
         | 
| 219 225 | 
             
                end
         | 
| 220 226 |  | 
| 221 227 | 
             
                #
         | 
| @@ -225,18 +231,28 @@ module LaunchDarkly | |
| 225 231 | 
             
                # Note that event delivery is asynchronous, so the event may not actually be sent
         | 
| 226 232 | 
             
                # until later; see {#flush}.
         | 
| 227 233 | 
             
                #
         | 
| 234 | 
            +
                # As of this version’s release date, the LaunchDarkly service does not support the `metricValue`
         | 
| 235 | 
            +
                # parameter. As a result, specifying `metricValue` will not yet produce any different behavior
         | 
| 236 | 
            +
                # from omitting it. Refer to the [SDK reference guide](https://docs.launchdarkly.com/docs/ruby-sdk-reference#section-track)
         | 
| 237 | 
            +
                # for the latest status.
         | 
| 238 | 
            +
                #
         | 
| 228 239 | 
             
                # @param event_name [String] The name of the event
         | 
| 229 240 | 
             
                # @param user [Hash] The user to register; this can have all the same user properties
         | 
| 230 241 | 
             
                #   described in {#variation}
         | 
| 231 | 
            -
                # @param data [Hash]  | 
| 242 | 
            +
                # @param data [Hash] An optional hash containing any additional data associated with the event
         | 
| 243 | 
            +
                # @param metric_value [Number] A numeric value used by the LaunchDarkly experimentation
         | 
| 244 | 
            +
                #   feature in numeric custom metrics. Can be omitted if this event is used by only
         | 
| 245 | 
            +
                #   non-numeric metrics. This field will also be returned as part of the custom event
         | 
| 246 | 
            +
                #   for Data Export.
         | 
| 232 247 | 
             
                # @return [void]
         | 
| 233 248 | 
             
                #
         | 
| 234 | 
            -
                def track(event_name, user, data)
         | 
| 249 | 
            +
                def track(event_name, user, data = nil, metric_value = nil)
         | 
| 235 250 | 
             
                  if !user || user[:key].nil?
         | 
| 236 251 | 
             
                    @config.logger.warn("Track called with nil user or nil user key!")
         | 
| 237 252 | 
             
                    return
         | 
| 238 253 | 
             
                  end
         | 
| 239 | 
            -
                   | 
| 254 | 
            +
                  sanitize_user(user)
         | 
| 255 | 
            +
                  @event_processor.add_event(@event_factory_default.new_custom_event(event_name, user, data, metric_value))
         | 
| 240 256 | 
             
                end
         | 
| 241 257 |  | 
| 242 258 | 
             
                #
         | 
| @@ -294,7 +310,7 @@ module LaunchDarkly | |
| 294 310 | 
             
                      next
         | 
| 295 311 | 
             
                    end
         | 
| 296 312 | 
             
                    begin
         | 
| 297 | 
            -
                      result = evaluate(f, user, @store, @config.logger)
         | 
| 313 | 
            +
                      result = evaluate(f, user, @store, @config.logger, @event_factory_default)
         | 
| 298 314 | 
             
                      state.add_flag(f, result.detail.value, result.detail.variation_index, with_reasons ? result.detail.reason : nil,
         | 
| 299 315 | 
             
                        details_only_if_tracked)
         | 
| 300 316 | 
             
                    rescue => exn
         | 
| @@ -334,7 +350,7 @@ module LaunchDarkly | |
| 334 350 | 
             
                end
         | 
| 335 351 |  | 
| 336 352 | 
             
                # @return [EvaluationDetail]
         | 
| 337 | 
            -
                def evaluate_internal(key, user, default,  | 
| 353 | 
            +
                def evaluate_internal(key, user, default, event_factory)
         | 
| 338 354 | 
             
                  if @config.offline?
         | 
| 339 355 | 
             
                    return error_result('CLIENT_NOT_READY', default)
         | 
| 340 356 | 
             
                  end
         | 
| @@ -344,8 +360,9 @@ module LaunchDarkly | |
| 344 360 | 
             
                      @config.logger.warn { "[LDClient] Client has not finished initializing; using last known values from feature store" }
         | 
| 345 361 | 
             
                    else
         | 
| 346 362 | 
             
                      @config.logger.error { "[LDClient] Client has not finished initializing; feature store unavailable, returning default value" }
         | 
| 347 | 
            -
                       | 
| 348 | 
            -
                       | 
| 363 | 
            +
                      detail = error_result('CLIENT_NOT_READY', default)
         | 
| 364 | 
            +
                      @event_processor.add_event(event_factory.new_unknown_flag_event(key, user, default, detail.reason))
         | 
| 365 | 
            +
                      return  detail
         | 
| 349 366 | 
             
                    end
         | 
| 350 367 | 
             
                  end
         | 
| 351 368 |  | 
| @@ -354,20 +371,19 @@ module LaunchDarkly | |
| 354 371 | 
             
                  if feature.nil?
         | 
| 355 372 | 
             
                    @config.logger.info { "[LDClient] Unknown feature flag \"#{key}\". Returning default value" }
         | 
| 356 373 | 
             
                    detail = error_result('FLAG_NOT_FOUND', default)
         | 
| 357 | 
            -
                    @event_processor.add_event( | 
| 358 | 
            -
                      reason: include_reasons_in_events ? detail.reason : nil)
         | 
| 374 | 
            +
                    @event_processor.add_event(event_factory.new_unknown_flag_event(key, user, default, detail.reason))
         | 
| 359 375 | 
             
                    return detail
         | 
| 360 376 | 
             
                  end
         | 
| 361 377 |  | 
| 362 378 | 
             
                  unless user
         | 
| 363 379 | 
             
                    @config.logger.error { "[LDClient] Must specify user" }
         | 
| 364 380 | 
             
                    detail = error_result('USER_NOT_SPECIFIED', default)
         | 
| 365 | 
            -
                    @event_processor.add_event( | 
| 381 | 
            +
                    @event_processor.add_event(event_factory.new_default_event(feature, user, default, detail.reason))
         | 
| 366 382 | 
             
                    return detail
         | 
| 367 383 | 
             
                  end
         | 
| 368 384 |  | 
| 369 385 | 
             
                  begin
         | 
| 370 | 
            -
                    res = evaluate(feature, user, @store, @config.logger | 
| 386 | 
            +
                    res = evaluate(feature, user, @store, @config.logger, event_factory)
         | 
| 371 387 | 
             
                    if !res.events.nil?
         | 
| 372 388 | 
             
                      res.events.each do |event|
         | 
| 373 389 | 
             
                        @event_processor.add_event(event)
         | 
| @@ -377,29 +393,20 @@ module LaunchDarkly | |
| 377 393 | 
             
                    if detail.default_value?
         | 
| 378 394 | 
             
                      detail = EvaluationDetail.new(default, nil, detail.reason)
         | 
| 379 395 | 
             
                    end
         | 
| 380 | 
            -
                    @event_processor.add_event( | 
| 396 | 
            +
                    @event_processor.add_event(event_factory.new_eval_event(feature, user, detail, default))
         | 
| 381 397 | 
             
                    return detail
         | 
| 382 398 | 
             
                  rescue => exn
         | 
| 383 399 | 
             
                    Util.log_exception(@config.logger, "Error evaluating feature flag \"#{key}\"", exn)
         | 
| 384 400 | 
             
                    detail = error_result('EXCEPTION', default)
         | 
| 385 | 
            -
                    @event_processor.add_event( | 
| 401 | 
            +
                    @event_processor.add_event(event_factory.new_default_event(feature, user, default, detail.reason))
         | 
| 386 402 | 
             
                    return detail
         | 
| 387 403 | 
             
                  end
         | 
| 388 404 | 
             
                end
         | 
| 389 405 |  | 
| 390 | 
            -
                def  | 
| 391 | 
            -
                   | 
| 392 | 
            -
                     | 
| 393 | 
            -
             | 
| 394 | 
            -
                    user: user,
         | 
| 395 | 
            -
                    variation: detail.variation_index,
         | 
| 396 | 
            -
                    value: detail.value,
         | 
| 397 | 
            -
                    default: default,
         | 
| 398 | 
            -
                    version: flag[:version],
         | 
| 399 | 
            -
                    trackEvents: flag[:trackEvents],
         | 
| 400 | 
            -
                    debugEventsUntilDate: flag[:debugEventsUntilDate],
         | 
| 401 | 
            -
                    reason: with_reasons ? detail.reason : nil
         | 
| 402 | 
            -
                  }
         | 
| 406 | 
            +
                def sanitize_user(user)
         | 
| 407 | 
            +
                  if user[:key]
         | 
| 408 | 
            +
                    user[:key] = user[:key].to_s
         | 
| 409 | 
            +
                  end
         | 
| 403 410 | 
             
                end
         | 
| 404 411 | 
             
              end
         | 
| 405 412 |  | 
    
        data/lib/ldclient-rb/version.rb
    CHANGED
    
    
    
        data/spec/evaluation_spec.rb
    CHANGED
    
    | @@ -7,6 +7,8 @@ describe LaunchDarkly::Evaluation do | |
| 7 7 |  | 
| 8 8 | 
             
              let(:features) { LaunchDarkly::InMemoryFeatureStore.new }
         | 
| 9 9 |  | 
| 10 | 
            +
              let(:factory) { LaunchDarkly::Impl::EventFactory.new(false) }
         | 
| 11 | 
            +
             | 
| 10 12 | 
             
              let(:user) {
         | 
| 11 13 | 
             
                {
         | 
| 12 14 | 
             
                  key: "userkey",
         | 
| @@ -36,7 +38,7 @@ describe LaunchDarkly::Evaluation do | |
| 36 38 | 
             
                  }
         | 
| 37 39 | 
             
                  user = { key: 'x' }
         | 
| 38 40 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new('b', 1, { kind: 'OFF' })
         | 
| 39 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 41 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 40 42 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 41 43 | 
             
                  expect(result.events).to eq([])
         | 
| 42 44 | 
             
                end
         | 
| @@ -50,7 +52,7 @@ describe LaunchDarkly::Evaluation do | |
| 50 52 | 
             
                  }
         | 
| 51 53 | 
             
                  user = { key: 'x' }
         | 
| 52 54 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'OFF' })
         | 
| 53 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 55 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 54 56 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 55 57 | 
             
                  expect(result.events).to eq([])
         | 
| 56 58 | 
             
                end
         | 
| @@ -66,7 +68,7 @@ describe LaunchDarkly::Evaluation do | |
| 66 68 | 
             
                  user = { key: 'x' }
         | 
| 67 69 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
         | 
| 68 70 | 
             
                    { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
         | 
| 69 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 71 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 70 72 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 71 73 | 
             
                  expect(result.events).to eq([])
         | 
| 72 74 | 
             
                end
         | 
| @@ -82,7 +84,7 @@ describe LaunchDarkly::Evaluation do | |
| 82 84 | 
             
                  user = { key: 'x' }
         | 
| 83 85 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
         | 
| 84 86 | 
             
                    { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
         | 
| 85 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 87 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 86 88 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 87 89 | 
             
                  expect(result.events).to eq([])
         | 
| 88 90 | 
             
                end
         | 
| @@ -99,7 +101,7 @@ describe LaunchDarkly::Evaluation do | |
| 99 101 | 
             
                  user = { key: 'x' }
         | 
| 100 102 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new('b', 1,
         | 
| 101 103 | 
             
                    { kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'badfeature' })
         | 
| 102 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 104 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 103 105 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 104 106 | 
             
                  expect(result.events).to eq([])
         | 
| 105 107 | 
             
                end
         | 
| @@ -127,10 +129,9 @@ describe LaunchDarkly::Evaluation do | |
| 127 129 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new('b', 1,
         | 
| 128 130 | 
             
                    { kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'feature1' })
         | 
| 129 131 | 
             
                  events_should_be = [{
         | 
| 130 | 
            -
                    kind: 'feature', key: 'feature1', user: user,  | 
| 131 | 
            -
                    trackEvents: nil, debugEventsUntilDate: nil
         | 
| 132 | 
            +
                    kind: 'feature', key: 'feature1', user: user, value: nil, default: nil, variation: nil, version: 2, prereqOf: 'feature0'
         | 
| 132 133 | 
             
                  }]
         | 
| 133 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 134 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 134 135 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 135 136 | 
             
                  expect(result.events).to eq(events_should_be)
         | 
| 136 137 | 
             
                end
         | 
| @@ -159,10 +160,9 @@ describe LaunchDarkly::Evaluation do | |
| 159 160 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new('b', 1,
         | 
| 160 161 | 
             
                    { kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'feature1' })
         | 
| 161 162 | 
             
                  events_should_be = [{
         | 
| 162 | 
            -
                    kind: 'feature', key: 'feature1', user: user, variation: 1, value: 'e', version: 2, prereqOf: 'feature0' | 
| 163 | 
            -
                    trackEvents: nil, debugEventsUntilDate: nil
         | 
| 163 | 
            +
                    kind: 'feature', key: 'feature1', user: user, variation: 1, value: 'e', default: nil, version: 2, prereqOf: 'feature0'
         | 
| 164 164 | 
             
                  }]
         | 
| 165 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 165 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 166 166 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 167 167 | 
             
                  expect(result.events).to eq(events_should_be)
         | 
| 168 168 | 
             
                end
         | 
| @@ -189,10 +189,9 @@ describe LaunchDarkly::Evaluation do | |
| 189 189 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new('b', 1,
         | 
| 190 190 | 
             
                    { kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'feature1' })
         | 
| 191 191 | 
             
                  events_should_be = [{
         | 
| 192 | 
            -
                    kind: 'feature', key: 'feature1', user: user, variation: 0, value: 'd', version: 2, prereqOf: 'feature0' | 
| 193 | 
            -
                    trackEvents: nil, debugEventsUntilDate: nil
         | 
| 192 | 
            +
                    kind: 'feature', key: 'feature1', user: user, variation: 0, value: 'd', default: nil, version: 2, prereqOf: 'feature0'
         | 
| 194 193 | 
             
                  }]
         | 
| 195 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 194 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 196 195 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 197 196 | 
             
                  expect(result.events).to eq(events_should_be)
         | 
| 198 197 | 
             
                end
         | 
| @@ -218,10 +217,9 @@ describe LaunchDarkly::Evaluation do | |
| 218 217 | 
             
                  user = { key: 'x' }
         | 
| 219 218 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new('a', 0, { kind: 'FALLTHROUGH' })
         | 
| 220 219 | 
             
                  events_should_be = [{
         | 
| 221 | 
            -
                    kind: 'feature', key: 'feature1', user: user, variation: 1, value: 'e', version: 2, prereqOf: 'feature0' | 
| 222 | 
            -
                    trackEvents: nil, debugEventsUntilDate: nil
         | 
| 220 | 
            +
                    kind: 'feature', key: 'feature1', user: user, variation: 1, value: 'e', default: nil, version: 2, prereqOf: 'feature0'
         | 
| 223 221 | 
             
                  }]
         | 
| 224 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 222 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 225 223 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 226 224 | 
             
                  expect(result.events).to eq(events_should_be)
         | 
| 227 225 | 
             
                end
         | 
| @@ -236,7 +234,7 @@ describe LaunchDarkly::Evaluation do | |
| 236 234 | 
             
                  }
         | 
| 237 235 | 
             
                  user = { key: 'userkey' }
         | 
| 238 236 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
         | 
| 239 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 237 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 240 238 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 241 239 | 
             
                  expect(result.events).to eq([])
         | 
| 242 240 | 
             
                end
         | 
| @@ -251,7 +249,7 @@ describe LaunchDarkly::Evaluation do | |
| 251 249 | 
             
                  }
         | 
| 252 250 | 
             
                  user = { key: 'userkey' }
         | 
| 253 251 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
         | 
| 254 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 252 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 255 253 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 256 254 | 
             
                  expect(result.events).to eq([])
         | 
| 257 255 | 
             
                end
         | 
| @@ -266,7 +264,7 @@ describe LaunchDarkly::Evaluation do | |
| 266 264 | 
             
                  }
         | 
| 267 265 | 
             
                  user = { key: 'userkey' }
         | 
| 268 266 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
         | 
| 269 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 267 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 270 268 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 271 269 | 
             
                  expect(result.events).to eq([])
         | 
| 272 270 | 
             
                end
         | 
| @@ -281,7 +279,7 @@ describe LaunchDarkly::Evaluation do | |
| 281 279 | 
             
                  }
         | 
| 282 280 | 
             
                  user = { key: 'userkey' }
         | 
| 283 281 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
         | 
| 284 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 282 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 285 283 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 286 284 | 
             
                  expect(result.events).to eq([])
         | 
| 287 285 | 
             
                end
         | 
| @@ -299,7 +297,7 @@ describe LaunchDarkly::Evaluation do | |
| 299 297 | 
             
                  }
         | 
| 300 298 | 
             
                  user = { key: 'userkey' }
         | 
| 301 299 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new('c', 2, { kind: 'TARGET_MATCH' })
         | 
| 302 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 300 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 303 301 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 304 302 | 
             
                  expect(result.events).to eq([])
         | 
| 305 303 | 
             
                end
         | 
| @@ -310,7 +308,7 @@ describe LaunchDarkly::Evaluation do | |
| 310 308 | 
             
                  user = { key: 'userkey' }
         | 
| 311 309 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new(true, 1,
         | 
| 312 310 | 
             
                    { kind: 'RULE_MATCH', ruleIndex: 0, ruleId: 'ruleid' })
         | 
| 313 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 311 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 314 312 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 315 313 | 
             
                  expect(result.events).to eq([])
         | 
| 316 314 | 
             
                end
         | 
| @@ -321,7 +319,7 @@ describe LaunchDarkly::Evaluation do | |
| 321 319 | 
             
                  user = { key: 'userkey' }
         | 
| 322 320 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
         | 
| 323 321 | 
             
                    { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
         | 
| 324 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 322 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 325 323 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 326 324 | 
             
                  expect(result.events).to eq([])
         | 
| 327 325 | 
             
                end
         | 
| @@ -332,7 +330,7 @@ describe LaunchDarkly::Evaluation do | |
| 332 330 | 
             
                  user = { key: 'userkey' }
         | 
| 333 331 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
         | 
| 334 332 | 
             
                    { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
         | 
| 335 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 333 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 336 334 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 337 335 | 
             
                  expect(result.events).to eq([])
         | 
| 338 336 | 
             
                end
         | 
| @@ -343,7 +341,7 @@ describe LaunchDarkly::Evaluation do | |
| 343 341 | 
             
                  user = { key: 'userkey' }
         | 
| 344 342 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
         | 
| 345 343 | 
             
                    { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
         | 
| 346 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 344 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 347 345 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 348 346 | 
             
                  expect(result.events).to eq([])
         | 
| 349 347 | 
             
                end
         | 
| @@ -355,7 +353,7 @@ describe LaunchDarkly::Evaluation do | |
| 355 353 | 
             
                  user = { key: 'userkey' }
         | 
| 356 354 | 
             
                  detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
         | 
| 357 355 | 
             
                    { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
         | 
| 358 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 356 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 359 357 | 
             
                  expect(result.detail).to eq(detail)
         | 
| 360 358 | 
             
                  expect(result.events).to eq([])
         | 
| 361 359 | 
             
                end
         | 
| @@ -364,7 +362,7 @@ describe LaunchDarkly::Evaluation do | |
| 364 362 | 
             
                  clause = { attribute: 'key', op: 'in', values: ['999'] }
         | 
| 365 363 | 
             
                  flag = boolean_flag_with_clauses([clause])
         | 
| 366 364 | 
             
                  user = { key: 999 }
         | 
| 367 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 365 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 368 366 | 
             
                  expect(result.detail.value).to eq(true)
         | 
| 369 367 | 
             
                end
         | 
| 370 368 |  | 
| @@ -375,7 +373,7 @@ describe LaunchDarkly::Evaluation do | |
| 375 373 | 
             
                    rollout: { salt: '', variations: [ { weight: 100000, variation: 1 } ] } }
         | 
| 376 374 | 
             
                  flag = boolean_flag_with_rules([rule])
         | 
| 377 375 | 
             
                  user = { key: "userkey", secondary: 999 }
         | 
| 378 | 
            -
                  result = evaluate(flag, user, features, logger)
         | 
| 376 | 
            +
                  result = evaluate(flag, user, features, logger, factory)
         | 
| 379 377 | 
             
                  expect(result.detail.reason).to eq({ kind: 'RULE_MATCH', ruleIndex: 0, ruleId: 'ruleid'})
         | 
| 380 378 | 
             
                end
         | 
| 381 379 | 
             
              end
         | 
| @@ -385,28 +383,28 @@ describe LaunchDarkly::Evaluation do | |
| 385 383 | 
             
                  user = { key: 'x', name: 'Bob' }
         | 
| 386 384 | 
             
                  clause = { attribute: 'name', op: 'in', values: ['Bob'] }
         | 
| 387 385 | 
             
                  flag = boolean_flag_with_clauses([clause])
         | 
| 388 | 
            -
                  expect(evaluate(flag, user, features, logger).detail.value).to be true
         | 
| 386 | 
            +
                  expect(evaluate(flag, user, features, logger, factory).detail.value).to be true
         | 
| 389 387 | 
             
                end
         | 
| 390 388 |  | 
| 391 389 | 
             
                it "can match custom attribute" do
         | 
| 392 390 | 
             
                  user = { key: 'x', name: 'Bob', custom: { legs: 4 } }
         | 
| 393 391 | 
             
                  clause = { attribute: 'legs', op: 'in', values: [4] }
         | 
| 394 392 | 
             
                  flag = boolean_flag_with_clauses([clause])
         | 
| 395 | 
            -
                  expect(evaluate(flag, user, features, logger).detail.value).to be true
         | 
| 393 | 
            +
                  expect(evaluate(flag, user, features, logger, factory).detail.value).to be true
         | 
| 396 394 | 
             
                end
         | 
| 397 395 |  | 
| 398 396 | 
             
                it "returns false for missing attribute" do
         | 
| 399 397 | 
             
                  user = { key: 'x', name: 'Bob' }
         | 
| 400 398 | 
             
                  clause = { attribute: 'legs', op: 'in', values: [4] }
         | 
| 401 399 | 
             
                  flag = boolean_flag_with_clauses([clause])
         | 
| 402 | 
            -
                  expect(evaluate(flag, user, features, logger).detail.value).to be false
         | 
| 400 | 
            +
                  expect(evaluate(flag, user, features, logger, factory).detail.value).to be false
         | 
| 403 401 | 
             
                end
         | 
| 404 402 |  | 
| 405 403 | 
             
                it "returns false for unknown operator" do
         | 
| 406 404 | 
             
                  user = { key: 'x', name: 'Bob' }
         | 
| 407 405 | 
             
                  clause = { attribute: 'name', op: 'unknown', values: [4] }
         | 
| 408 406 | 
             
                  flag = boolean_flag_with_clauses([clause])
         | 
| 409 | 
            -
                  expect(evaluate(flag, user, features, logger).detail.value).to be false
         | 
| 407 | 
            +
                  expect(evaluate(flag, user, features, logger, factory).detail.value).to be false
         | 
| 410 408 | 
             
                end
         | 
| 411 409 |  | 
| 412 410 | 
             
                it "does not stop evaluating rules after clause with unknown operator" do
         | 
| @@ -416,14 +414,14 @@ describe LaunchDarkly::Evaluation do | |
| 416 414 | 
             
                  clause1 = { attribute: 'name', op: 'in', values: ['Bob'] }
         | 
| 417 415 | 
             
                  rule1 = { clauses: [ clause1 ], variation: 1 }
         | 
| 418 416 | 
             
                  flag = boolean_flag_with_rules([rule0, rule1])
         | 
| 419 | 
            -
                  expect(evaluate(flag, user, features, logger).detail.value).to be true
         | 
| 417 | 
            +
                  expect(evaluate(flag, user, features, logger, factory).detail.value).to be true
         | 
| 420 418 | 
             
                end
         | 
| 421 419 |  | 
| 422 420 | 
             
                it "can be negated" do
         | 
| 423 421 | 
             
                  user = { key: 'x', name: 'Bob' }
         | 
| 424 422 | 
             
                  clause = { attribute: 'name', op: 'in', values: ['Bob'], negate: true }
         | 
| 425 423 | 
             
                  flag = boolean_flag_with_clauses([clause])
         | 
| 426 | 
            -
                  expect(evaluate(flag, user, features, logger).detail.value).to be false
         | 
| 424 | 
            +
                  expect(evaluate(flag, user, features, logger, factory).detail.value).to be false
         | 
| 427 425 | 
             
                end
         | 
| 428 426 |  | 
| 429 427 | 
             
                it "retrieves segment from segment store for segmentMatch operator" do
         | 
| @@ -438,14 +436,14 @@ describe LaunchDarkly::Evaluation do | |
| 438 436 | 
             
                  user = { key: 'userkey' }
         | 
| 439 437 | 
             
                  clause = { attribute: '', op: 'segmentMatch', values: ['segkey'] }
         | 
| 440 438 | 
             
                  flag = boolean_flag_with_clauses([clause])
         | 
| 441 | 
            -
                  expect(evaluate(flag, user, features, logger).detail.value).to be true
         | 
| 439 | 
            +
                  expect(evaluate(flag, user, features, logger, factory).detail.value).to be true
         | 
| 442 440 | 
             
                end
         | 
| 443 441 |  | 
| 444 442 | 
             
                it "falls through with no errors if referenced segment is not found" do
         | 
| 445 443 | 
             
                  user = { key: 'userkey' }
         | 
| 446 444 | 
             
                  clause = { attribute: '', op: 'segmentMatch', values: ['segkey'] }
         | 
| 447 445 | 
             
                  flag = boolean_flag_with_clauses([clause])
         | 
| 448 | 
            -
                  expect(evaluate(flag, user, features, logger).detail.value).to be false
         | 
| 446 | 
            +
                  expect(evaluate(flag, user, features, logger, factory).detail.value).to be false
         | 
| 449 447 | 
             
                end
         | 
| 450 448 |  | 
| 451 449 | 
             
                it "can be negated" do
         | 
| @@ -454,7 +452,7 @@ describe LaunchDarkly::Evaluation do | |
| 454 452 | 
             
                  flag = boolean_flag_with_clauses([clause])
         | 
| 455 453 | 
             
                  expect {
         | 
| 456 454 | 
             
                     clause[:negate] = true
         | 
| 457 | 
            -
                  }.to change {evaluate(flag, user, features, logger).detail.value}.from(true).to(false)
         | 
| 455 | 
            +
                  }.to change {evaluate(flag, user, features, logger, factory).detail.value}.from(true).to(false)
         | 
| 458 456 | 
             
                end
         | 
| 459 457 | 
             
              end
         | 
| 460 458 |  | 
| @@ -557,7 +555,7 @@ describe LaunchDarkly::Evaluation do | |
| 557 555 | 
             
                    user = { key: 'x', custom: { foo: value1 } }
         | 
| 558 556 | 
             
                    clause = { attribute: 'foo', op: op, values: [value2] }
         | 
| 559 557 | 
             
                    flag = boolean_flag_with_clauses([clause])
         | 
| 560 | 
            -
                    expect(evaluate(flag, user, features, logger).detail.value).to be shouldBe
         | 
| 558 | 
            +
                    expect(evaluate(flag, user, features, logger, factory).detail.value).to be shouldBe
         | 
| 561 559 | 
             
                  end
         | 
| 562 560 | 
             
                end
         | 
| 563 561 | 
             
              end
         | 
| @@ -648,7 +646,7 @@ describe LaunchDarkly::Evaluation do | |
| 648 646 | 
             
                  features.upsert(LaunchDarkly::SEGMENTS, segment)
         | 
| 649 647 | 
             
                  clause = make_segment_match_clause(segment)
         | 
| 650 648 | 
             
                  flag = boolean_flag_with_clauses([clause])
         | 
| 651 | 
            -
                  evaluate(flag, user, features, logger).detail.value
         | 
| 649 | 
            +
                  evaluate(flag, user, features, logger, factory).detail.value
         | 
| 652 650 | 
             
                end
         | 
| 653 651 |  | 
| 654 652 | 
             
                it 'explicitly includes user' do
         | 
    
        data/spec/events_spec.rb
    CHANGED
    
    | @@ -342,7 +342,7 @@ describe LaunchDarkly::EventProcessor do | |
| 342 342 |  | 
| 343 343 | 
             
              it "queues custom event with user" do
         | 
| 344 344 | 
             
                @ep = subject.new("sdk_key", default_config, hc)
         | 
| 345 | 
            -
                e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" } }
         | 
| 345 | 
            +
                e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" }, metricValue: 1.5 }
         | 
| 346 346 | 
             
                @ep.add_event(e)
         | 
| 347 347 |  | 
| 348 348 | 
             
                output = flush_and_get_events
         | 
| @@ -565,6 +565,7 @@ describe LaunchDarkly::EventProcessor do | |
| 565 565 | 
             
                else
         | 
| 566 566 | 
             
                  out[:user] = inline_user
         | 
| 567 567 | 
             
                end
         | 
| 568 | 
            +
                out[:metricValue] = e[:metricValue] if e.has_key?(:metricValue)
         | 
| 568 569 | 
             
                out
         | 
| 569 570 | 
             
              end
         | 
| 570 571 |  | 
    
        data/spec/ldclient_spec.rb
    CHANGED
    
    | @@ -25,6 +25,22 @@ describe LaunchDarkly::LDClient do | |
| 25 25 | 
             
                  }
         | 
| 26 26 | 
             
                }
         | 
| 27 27 | 
             
              end
         | 
| 28 | 
            +
              let(:numeric_key_user) do
         | 
| 29 | 
            +
                {
         | 
| 30 | 
            +
                  key: 33,
         | 
| 31 | 
            +
                  custom: {
         | 
| 32 | 
            +
                      groups: [ "microsoft", "google" ]
         | 
| 33 | 
            +
                  }
         | 
| 34 | 
            +
                }
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
              let(:sanitized_numeric_key_user) do
         | 
| 37 | 
            +
                {
         | 
| 38 | 
            +
                  key: "33",
         | 
| 39 | 
            +
                  custom: {
         | 
| 40 | 
            +
                      groups: [ "microsoft", "google" ]
         | 
| 41 | 
            +
                  }
         | 
| 42 | 
            +
                }
         | 
| 43 | 
            +
              end
         | 
| 28 44 | 
             
              let(:user_without_key) do
         | 
| 29 45 | 
             
                { name: "Keyless Joe" }
         | 
| 30 46 | 
             
              end
         | 
| @@ -91,7 +107,6 @@ describe LaunchDarkly::LDClient do | |
| 91 107 | 
             
                    key: "key",
         | 
| 92 108 | 
             
                    version: 100,
         | 
| 93 109 | 
             
                    user: nil,
         | 
| 94 | 
            -
                    variation: nil,
         | 
| 95 110 | 
             
                    value: "default",
         | 
| 96 111 | 
             
                    default: "default",
         | 
| 97 112 | 
             
                    trackEvents: true,
         | 
| @@ -109,7 +124,6 @@ describe LaunchDarkly::LDClient do | |
| 109 124 | 
             
                    key: "key",
         | 
| 110 125 | 
             
                    version: 100,
         | 
| 111 126 | 
             
                    user: bad_user,
         | 
| 112 | 
            -
                    variation: nil,
         | 
| 113 127 | 
             
                    value: "default",
         | 
| 114 128 | 
             
                    default: "default",
         | 
| 115 129 | 
             
                    trackEvents: true,
         | 
| @@ -117,6 +131,61 @@ describe LaunchDarkly::LDClient do | |
| 117 131 | 
             
                  ))
         | 
| 118 132 | 
             
                  client.variation("key", bad_user, "default")
         | 
| 119 133 | 
             
                end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                it "sets trackEvents and reason if trackEvents is set for matched rule" do
         | 
| 136 | 
            +
                  flag = {
         | 
| 137 | 
            +
                    key: 'flag',
         | 
| 138 | 
            +
                    on: true,
         | 
| 139 | 
            +
                    variations: [ 'value' ],
         | 
| 140 | 
            +
                    version: 100,
         | 
| 141 | 
            +
                    rules: [
         | 
| 142 | 
            +
                      clauses: [
         | 
| 143 | 
            +
                        { attribute: 'key', op: 'in', values: [ user[:key] ] }
         | 
| 144 | 
            +
                      ],
         | 
| 145 | 
            +
                      variation: 0,
         | 
| 146 | 
            +
                      id: 'id',
         | 
| 147 | 
            +
                      trackEvents: true
         | 
| 148 | 
            +
                    ]
         | 
| 149 | 
            +
                  }
         | 
| 150 | 
            +
                  config.feature_store.init({ LaunchDarkly::FEATURES => {} })
         | 
| 151 | 
            +
                  config.feature_store.upsert(LaunchDarkly::FEATURES, flag)
         | 
| 152 | 
            +
                  expect(event_processor).to receive(:add_event).with(hash_including(
         | 
| 153 | 
            +
                    kind: 'feature',
         | 
| 154 | 
            +
                    key: 'flag',
         | 
| 155 | 
            +
                    version: 100,
         | 
| 156 | 
            +
                    user: user,
         | 
| 157 | 
            +
                    value: 'value',
         | 
| 158 | 
            +
                    default: 'default',
         | 
| 159 | 
            +
                    trackEvents: true,
         | 
| 160 | 
            +
                    reason: { kind: 'RULE_MATCH', ruleIndex: 0, ruleId: 'id' }
         | 
| 161 | 
            +
                  ))
         | 
| 162 | 
            +
                  client.variation('flag', user, 'default')
         | 
| 163 | 
            +
                end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                it "sets trackEvents and reason if trackEventsFallthrough is set and we fell through" do
         | 
| 166 | 
            +
                  flag = {
         | 
| 167 | 
            +
                    key: 'flag',
         | 
| 168 | 
            +
                    on: true,
         | 
| 169 | 
            +
                    variations: [ 'value' ],
         | 
| 170 | 
            +
                    fallthrough: { variation: 0 },
         | 
| 171 | 
            +
                    version: 100,
         | 
| 172 | 
            +
                    rules: [],
         | 
| 173 | 
            +
                    trackEventsFallthrough: true
         | 
| 174 | 
            +
                  }
         | 
| 175 | 
            +
                  config.feature_store.init({ LaunchDarkly::FEATURES => {} })
         | 
| 176 | 
            +
                  config.feature_store.upsert(LaunchDarkly::FEATURES, flag)
         | 
| 177 | 
            +
                  expect(event_processor).to receive(:add_event).with(hash_including(
         | 
| 178 | 
            +
                    kind: 'feature',
         | 
| 179 | 
            +
                    key: 'flag',
         | 
| 180 | 
            +
                    version: 100,
         | 
| 181 | 
            +
                    user: user,
         | 
| 182 | 
            +
                    value: 'value',
         | 
| 183 | 
            +
                    default: 'default',
         | 
| 184 | 
            +
                    trackEvents: true,
         | 
| 185 | 
            +
                    reason: { kind: 'FALLTHROUGH' }
         | 
| 186 | 
            +
                  ))
         | 
| 187 | 
            +
                  client.variation('flag', user, 'default')
         | 
| 188 | 
            +
                end
         | 
| 120 189 | 
             
              end
         | 
| 121 190 |  | 
| 122 191 | 
             
              describe '#variation_detail' do
         | 
| @@ -338,6 +407,17 @@ describe LaunchDarkly::LDClient do | |
| 338 407 | 
             
                  client.track("custom_event_name", user, 42)
         | 
| 339 408 | 
             
                end
         | 
| 340 409 |  | 
| 410 | 
            +
                it "can include a metric value" do
         | 
| 411 | 
            +
                  expect(event_processor).to receive(:add_event).with(hash_including(
         | 
| 412 | 
            +
                    kind: "custom", key: "custom_event_name", user: user, metricValue: 1.5))
         | 
| 413 | 
            +
                  client.track("custom_event_name", user, nil, 1.5)
         | 
| 414 | 
            +
                end
         | 
| 415 | 
            +
             | 
| 416 | 
            +
                it "sanitizes the user in the event" do
         | 
| 417 | 
            +
                  expect(event_processor).to receive(:add_event).with(hash_including(user: sanitized_numeric_key_user))
         | 
| 418 | 
            +
                  client.track("custom_event_name", numeric_key_user, nil)
         | 
| 419 | 
            +
                end
         | 
| 420 | 
            +
             | 
| 341 421 | 
             
                it "does not send an event, and logs a warning, if user is nil" do
         | 
| 342 422 | 
             
                  expect(event_processor).not_to receive(:add_event)
         | 
| 343 423 | 
             
                  expect(logger).to receive(:warn)
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: launchdarkly-server-sdk
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 5. | 
| 4 | 
            +
              version: 5.6.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - LaunchDarkly
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2019-08- | 
| 11 | 
            +
            date: 2019-08-20 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: aws-sdk-dynamodb
         | 
| @@ -239,6 +239,7 @@ files: | |
| 239 239 | 
             
            - ".github/ISSUE_TEMPLATE/feature_request.md"
         | 
| 240 240 | 
             
            - ".gitignore"
         | 
| 241 241 | 
             
            - ".hound.yml"
         | 
| 242 | 
            +
            - ".ldrelease/config.yml"
         | 
| 242 243 | 
             
            - ".rspec"
         | 
| 243 244 | 
             
            - ".rubocop.yml"
         | 
| 244 245 | 
             
            - ".simplecov"
         | 
| @@ -265,6 +266,7 @@ files: | |
| 265 266 | 
             
            - lib/ldclient-rb/file_data_source.rb
         | 
| 266 267 | 
             
            - lib/ldclient-rb/flags_state.rb
         | 
| 267 268 | 
             
            - lib/ldclient-rb/impl.rb
         | 
| 269 | 
            +
            - lib/ldclient-rb/impl/event_factory.rb
         | 
| 268 270 | 
             
            - lib/ldclient-rb/impl/integrations/consul_impl.rb
         | 
| 269 271 | 
             
            - lib/ldclient-rb/impl/integrations/dynamodb_impl.rb
         | 
| 270 272 | 
             
            - lib/ldclient-rb/impl/integrations/redis_impl.rb
         | 
| @@ -342,7 +344,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 342 344 | 
             
                  version: '0'
         | 
| 343 345 | 
             
            requirements: []
         | 
| 344 346 | 
             
            rubyforge_project: 
         | 
| 345 | 
            -
            rubygems_version: 2.5.2
         | 
| 347 | 
            +
            rubygems_version: 2.5.2.3
         | 
| 346 348 | 
             
            signing_key: 
         | 
| 347 349 | 
             
            specification_version: 4
         | 
| 348 350 | 
             
            summary: LaunchDarkly SDK for Ruby
         |