launchdarkly-server-sdk 6.2.5 → 7.0.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/README.md +1 -2
 - data/lib/ldclient-rb/config.rb +203 -43
 - data/lib/ldclient-rb/context.rb +487 -0
 - data/lib/ldclient-rb/evaluation_detail.rb +85 -26
 - data/lib/ldclient-rb/events.rb +185 -146
 - data/lib/ldclient-rb/flags_state.rb +25 -14
 - data/lib/ldclient-rb/impl/big_segments.rb +117 -0
 - data/lib/ldclient-rb/impl/context.rb +96 -0
 - data/lib/ldclient-rb/impl/context_filter.rb +145 -0
 - data/lib/ldclient-rb/impl/diagnostic_events.rb +9 -10
 - data/lib/ldclient-rb/impl/evaluator.rb +428 -132
 - data/lib/ldclient-rb/impl/evaluator_bucketing.rb +40 -41
 - data/lib/ldclient-rb/impl/evaluator_helpers.rb +50 -0
 - data/lib/ldclient-rb/impl/evaluator_operators.rb +26 -55
 - data/lib/ldclient-rb/impl/event_sender.rb +6 -6
 - data/lib/ldclient-rb/impl/event_summarizer.rb +68 -0
 - data/lib/ldclient-rb/impl/event_types.rb +78 -0
 - data/lib/ldclient-rb/impl/integrations/consul_impl.rb +7 -7
 - data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +92 -28
 - data/lib/ldclient-rb/impl/integrations/file_data_source.rb +212 -0
 - data/lib/ldclient-rb/impl/integrations/redis_impl.rb +165 -32
 - data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +40 -0
 - data/lib/ldclient-rb/impl/model/clause.rb +39 -0
 - data/lib/ldclient-rb/impl/model/feature_flag.rb +213 -0
 - data/lib/ldclient-rb/impl/model/preprocessed_data.rb +64 -0
 - data/lib/ldclient-rb/impl/model/segment.rb +126 -0
 - data/lib/ldclient-rb/impl/model/serialization.rb +54 -44
 - data/lib/ldclient-rb/impl/repeating_task.rb +47 -0
 - data/lib/ldclient-rb/impl/store_data_set_sorter.rb +2 -2
 - data/lib/ldclient-rb/impl/unbounded_pool.rb +1 -1
 - data/lib/ldclient-rb/impl/util.rb +62 -1
 - data/lib/ldclient-rb/in_memory_store.rb +2 -2
 - data/lib/ldclient-rb/integrations/consul.rb +9 -2
 - data/lib/ldclient-rb/integrations/dynamodb.rb +47 -2
 - data/lib/ldclient-rb/integrations/file_data.rb +108 -0
 - data/lib/ldclient-rb/integrations/redis.rb +43 -3
 - data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +594 -0
 - data/lib/ldclient-rb/integrations/test_data.rb +213 -0
 - data/lib/ldclient-rb/integrations/util/store_wrapper.rb +14 -9
 - data/lib/ldclient-rb/integrations.rb +2 -51
 - data/lib/ldclient-rb/interfaces.rb +151 -1
 - data/lib/ldclient-rb/ldclient.rb +175 -133
 - data/lib/ldclient-rb/memoized_value.rb +1 -1
 - data/lib/ldclient-rb/non_blocking_thread_pool.rb +1 -1
 - data/lib/ldclient-rb/polling.rb +22 -41
 - data/lib/ldclient-rb/reference.rb +274 -0
 - data/lib/ldclient-rb/requestor.rb +7 -7
 - data/lib/ldclient-rb/stream.rb +9 -9
 - data/lib/ldclient-rb/util.rb +11 -17
 - data/lib/ldclient-rb/version.rb +1 -1
 - data/lib/ldclient-rb.rb +2 -4
 - metadata +49 -23
 - data/lib/ldclient-rb/event_summarizer.rb +0 -55
 - data/lib/ldclient-rb/file_data_source.rb +0 -314
 - data/lib/ldclient-rb/impl/event_factory.rb +0 -126
 - data/lib/ldclient-rb/newrelic.rb +0 -17
 - data/lib/ldclient-rb/redis_store.rb +0 -88
 - data/lib/ldclient-rb/user_filter.rb +0 -52
 
    
        data/lib/ldclient-rb/events.rb
    CHANGED
    
    | 
         @@ -1,5 +1,8 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "ldclient-rb/impl/context_filter"
         
     | 
| 
       1 
2 
     | 
    
         
             
            require "ldclient-rb/impl/diagnostic_events"
         
     | 
| 
       2 
3 
     | 
    
         
             
            require "ldclient-rb/impl/event_sender"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "ldclient-rb/impl/event_summarizer"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "ldclient-rb/impl/event_types"
         
     | 
| 
       3 
6 
     | 
    
         
             
            require "ldclient-rb/impl/util"
         
     | 
| 
       4 
7 
     | 
    
         | 
| 
       5 
8 
     | 
    
         
             
            require "concurrent"
         
     | 
| 
         @@ -18,7 +21,7 @@ require "time" 
     | 
|
| 
       18 
21 
     | 
    
         
             
            # On a separate worker thread, EventDispatcher consumes events from the inbox. These are considered
         
     | 
| 
       19 
22 
     | 
    
         
             
            # "input events" because they may or may not actually be sent to LaunchDarkly; most flag evaluation
         
     | 
| 
       20 
23 
     | 
    
         
             
            # events are not sent, but are counted and the counters become part of a single summary event.
         
     | 
| 
       21 
     | 
    
         
            -
            # EventDispatcher updates those counters, creates "index" events for any  
     | 
| 
      
 24 
     | 
    
         
            +
            # EventDispatcher updates those counters, creates "index" events for any contexts that have not been seen
         
     | 
| 
       22 
25 
     | 
    
         
             
            # recently, and places any events that will be sent to LaunchDarkly into the "outbox" queue.
         
     | 
| 
       23 
26 
     | 
    
         
             
            #
         
     | 
| 
       24 
27 
     | 
    
         
             
            # When it is time to flush events to LaunchDarkly, the contents of the outbox are handed off to
         
     | 
| 
         @@ -26,16 +29,30 @@ require "time" 
     | 
|
| 
       26 
29 
     | 
    
         
             
            #
         
     | 
| 
       27 
30 
     | 
    
         | 
| 
       28 
31 
     | 
    
         
             
            module LaunchDarkly
         
     | 
| 
       29 
     | 
    
         
            -
               
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
      
 32 
     | 
    
         
            +
              module EventProcessorMethods
         
     | 
| 
      
 33 
     | 
    
         
            +
                def record_eval_event(
         
     | 
| 
      
 34 
     | 
    
         
            +
                  context,
         
     | 
| 
      
 35 
     | 
    
         
            +
                  key,
         
     | 
| 
      
 36 
     | 
    
         
            +
                  version = nil,
         
     | 
| 
      
 37 
     | 
    
         
            +
                  variation = nil,
         
     | 
| 
      
 38 
     | 
    
         
            +
                  value = nil,
         
     | 
| 
      
 39 
     | 
    
         
            +
                  reason = nil,
         
     | 
| 
      
 40 
     | 
    
         
            +
                  default = nil,
         
     | 
| 
      
 41 
     | 
    
         
            +
                  track_events = false,
         
     | 
| 
      
 42 
     | 
    
         
            +
                  debug_until = nil,
         
     | 
| 
      
 43 
     | 
    
         
            +
                  prereq_of = nil
         
     | 
| 
      
 44 
     | 
    
         
            +
                )
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
       32 
46 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
      
 47 
     | 
    
         
            +
                def record_identify_event(context)
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
       35 
49 
     | 
    
         | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
      
 50 
     | 
    
         
            +
                def record_custom_event(
         
     | 
| 
      
 51 
     | 
    
         
            +
                  context,
         
     | 
| 
      
 52 
     | 
    
         
            +
                  key,
         
     | 
| 
      
 53 
     | 
    
         
            +
                  data = nil,
         
     | 
| 
      
 54 
     | 
    
         
            +
                  metric_value = nil
         
     | 
| 
      
 55 
     | 
    
         
            +
                )
         
     | 
| 
       39 
56 
     | 
    
         
             
                end
         
     | 
| 
       40 
57 
     | 
    
         | 
| 
       41 
58 
     | 
    
         
             
                def flush
         
     | 
| 
         @@ -45,12 +62,12 @@ module LaunchDarkly 
     | 
|
| 
       45 
62 
     | 
    
         
             
                end
         
     | 
| 
       46 
63 
     | 
    
         
             
              end
         
     | 
| 
       47 
64 
     | 
    
         | 
| 
      
 65 
     | 
    
         
            +
              MAX_FLUSH_WORKERS = 5
         
     | 
| 
      
 66 
     | 
    
         
            +
              private_constant :MAX_FLUSH_WORKERS
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
       48 
68 
     | 
    
         
             
              # @private
         
     | 
| 
       49 
     | 
    
         
            -
              class  
     | 
| 
       50 
     | 
    
         
            -
                 
     | 
| 
       51 
     | 
    
         
            -
                  @event = event
         
     | 
| 
       52 
     | 
    
         
            -
                end
         
     | 
| 
       53 
     | 
    
         
            -
                attr_reader :event
         
     | 
| 
      
 69 
     | 
    
         
            +
              class NullEventProcessor
         
     | 
| 
      
 70 
     | 
    
         
            +
                include EventProcessorMethods
         
     | 
| 
       54 
71 
     | 
    
         
             
              end
         
     | 
| 
       55 
72 
     | 
    
         | 
| 
       56 
73 
     | 
    
         
             
              # @private
         
     | 
| 
         @@ -58,7 +75,7 @@ module LaunchDarkly 
     | 
|
| 
       58 
75 
     | 
    
         
             
              end
         
     | 
| 
       59 
76 
     | 
    
         | 
| 
       60 
77 
     | 
    
         
             
              # @private
         
     | 
| 
       61 
     | 
    
         
            -
              class  
     | 
| 
      
 78 
     | 
    
         
            +
              class FlushContextsMessage
         
     | 
| 
       62 
79 
     | 
    
         
             
              end
         
     | 
| 
       63 
80 
     | 
    
         | 
| 
       64 
81 
     | 
    
         
             
              # @private
         
     | 
| 
         @@ -70,7 +87,7 @@ module LaunchDarkly 
     | 
|
| 
       70 
87 
     | 
    
         
             
                def initialize
         
     | 
| 
       71 
88 
     | 
    
         
             
                  @reply = Concurrent::Semaphore.new(0)
         
     | 
| 
       72 
89 
     | 
    
         
             
                end
         
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
       74 
91 
     | 
    
         
             
                def completed
         
     | 
| 
       75 
92 
     | 
    
         
             
                  @reply.release
         
     | 
| 
       76 
93 
     | 
    
         
             
                end
         
     | 
| 
         @@ -90,6 +107,8 @@ module LaunchDarkly 
     | 
|
| 
       90 
107 
     | 
    
         | 
| 
       91 
108 
     | 
    
         
             
              # @private
         
     | 
| 
       92 
109 
     | 
    
         
             
              class EventProcessor
         
     | 
| 
      
 110 
     | 
    
         
            +
                include EventProcessorMethods
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
       93 
112 
     | 
    
         
             
                def initialize(sdk_key, config, client = nil, diagnostic_accumulator = nil, test_properties = nil)
         
     | 
| 
       94 
113 
     | 
    
         
             
                  raise ArgumentError, "sdk_key must not be nil" if sdk_key.nil?  # see LDClient constructor comment on sdk_key
         
     | 
| 
       95 
114 
     | 
    
         
             
                  @logger = config.logger
         
     | 
| 
         @@ -98,10 +117,10 @@ module LaunchDarkly 
     | 
|
| 
       98 
117 
     | 
    
         
             
                    post_to_inbox(FlushMessage.new)
         
     | 
| 
       99 
118 
     | 
    
         
             
                  end
         
     | 
| 
       100 
119 
     | 
    
         
             
                  @flush_task.execute
         
     | 
| 
       101 
     | 
    
         
            -
                  @ 
     | 
| 
       102 
     | 
    
         
            -
                    post_to_inbox( 
     | 
| 
      
 120 
     | 
    
         
            +
                  @contexts_flush_task = Concurrent::TimerTask.new(execution_interval: config.context_keys_flush_interval) do
         
     | 
| 
      
 121 
     | 
    
         
            +
                    post_to_inbox(FlushContextsMessage.new)
         
     | 
| 
       103 
122 
     | 
    
         
             
                  end
         
     | 
| 
       104 
     | 
    
         
            -
                  @ 
     | 
| 
      
 123 
     | 
    
         
            +
                  @contexts_flush_task.execute
         
     | 
| 
       105 
124 
     | 
    
         
             
                  if !diagnostic_accumulator.nil?
         
     | 
| 
       106 
125 
     | 
    
         
             
                    interval = test_properties && test_properties.has_key?(:diagnostic_recording_interval) ?
         
     | 
| 
       107 
126 
     | 
    
         
             
                      test_properties[:diagnostic_recording_interval] :
         
     | 
| 
         @@ -116,16 +135,36 @@ module LaunchDarkly 
     | 
|
| 
       116 
135 
     | 
    
         
             
                  @stopped = Concurrent::AtomicBoolean.new(false)
         
     | 
| 
       117 
136 
     | 
    
         
             
                  @inbox_full = Concurrent::AtomicBoolean.new(false)
         
     | 
| 
       118 
137 
     | 
    
         | 
| 
       119 
     | 
    
         
            -
                  event_sender = test_properties  
     | 
| 
       120 
     | 
    
         
            -
                     
     | 
| 
       121 
     | 
    
         
            -
             
     | 
| 
      
 138 
     | 
    
         
            +
                  event_sender = (test_properties || {})[:event_sender] ||
         
     | 
| 
      
 139 
     | 
    
         
            +
                    Impl::EventSender.new(sdk_key, config, client || Util.new_http_client(config.events_uri, config))
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                  @timestamp_fn = (test_properties || {})[:timestamp_fn] || proc { Impl::Util.current_time_millis }
         
     | 
| 
       122 
142 
     | 
    
         | 
| 
       123 
143 
     | 
    
         
             
                  EventDispatcher.new(@inbox, sdk_key, config, diagnostic_accumulator, event_sender)
         
     | 
| 
       124 
144 
     | 
    
         
             
                end
         
     | 
| 
       125 
145 
     | 
    
         | 
| 
       126 
     | 
    
         
            -
                def  
     | 
| 
       127 
     | 
    
         
            -
                   
     | 
| 
       128 
     | 
    
         
            -
                   
     | 
| 
      
 146 
     | 
    
         
            +
                def record_eval_event(
         
     | 
| 
      
 147 
     | 
    
         
            +
                  context,
         
     | 
| 
      
 148 
     | 
    
         
            +
                  key,
         
     | 
| 
      
 149 
     | 
    
         
            +
                  version = nil,
         
     | 
| 
      
 150 
     | 
    
         
            +
                  variation = nil,
         
     | 
| 
      
 151 
     | 
    
         
            +
                  value = nil,
         
     | 
| 
      
 152 
     | 
    
         
            +
                  reason = nil,
         
     | 
| 
      
 153 
     | 
    
         
            +
                  default = nil,
         
     | 
| 
      
 154 
     | 
    
         
            +
                  track_events = false,
         
     | 
| 
      
 155 
     | 
    
         
            +
                  debug_until = nil,
         
     | 
| 
      
 156 
     | 
    
         
            +
                  prereq_of = nil
         
     | 
| 
      
 157 
     | 
    
         
            +
                )
         
     | 
| 
      
 158 
     | 
    
         
            +
                  post_to_inbox(LaunchDarkly::Impl::EvalEvent.new(timestamp, context, key, version, variation, value, reason,
         
     | 
| 
      
 159 
     | 
    
         
            +
                    default, track_events, debug_until, prereq_of))
         
     | 
| 
      
 160 
     | 
    
         
            +
                end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                def record_identify_event(context)
         
     | 
| 
      
 163 
     | 
    
         
            +
                  post_to_inbox(LaunchDarkly::Impl::IdentifyEvent.new(timestamp, context))
         
     | 
| 
      
 164 
     | 
    
         
            +
                end
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                def record_custom_event(context, key, data = nil, metric_value = nil)
         
     | 
| 
      
 167 
     | 
    
         
            +
                  post_to_inbox(LaunchDarkly::Impl::CustomEvent.new(timestamp, context, key, data, metric_value))
         
     | 
| 
       129 
168 
     | 
    
         
             
                end
         
     | 
| 
       130 
169 
     | 
    
         | 
| 
       131 
170 
     | 
    
         
             
                def flush
         
     | 
| 
         @@ -137,8 +176,8 @@ module LaunchDarkly 
     | 
|
| 
       137 
176 
     | 
    
         
             
                  # final shutdown, which includes a final flush, is done synchronously
         
     | 
| 
       138 
177 
     | 
    
         
             
                  if @stopped.make_true
         
     | 
| 
       139 
178 
     | 
    
         
             
                    @flush_task.shutdown
         
     | 
| 
       140 
     | 
    
         
            -
                    @ 
     | 
| 
       141 
     | 
    
         
            -
                    @diagnostic_event_task.shutdown  
     | 
| 
      
 179 
     | 
    
         
            +
                    @contexts_flush_task.shutdown
         
     | 
| 
      
 180 
     | 
    
         
            +
                    @diagnostic_event_task.shutdown unless @diagnostic_event_task.nil?
         
     | 
| 
       142 
181 
     | 
    
         
             
                    # Note that here we are not calling post_to_inbox, because we *do* want to wait if the inbox
         
     | 
| 
       143 
182 
     | 
    
         
             
                    # is full; an orderly shutdown can't happen unless these messages are received.
         
     | 
| 
       144 
183 
     | 
    
         
             
                    @inbox << FlushMessage.new
         
     | 
| 
         @@ -155,9 +194,11 @@ module LaunchDarkly 
     | 
|
| 
       155 
194 
     | 
    
         
             
                  sync_msg.wait_for_completion
         
     | 
| 
       156 
195 
     | 
    
         
             
                end
         
     | 
| 
       157 
196 
     | 
    
         | 
| 
       158 
     | 
    
         
            -
                private
         
     | 
| 
      
 197 
     | 
    
         
            +
                private def timestamp
         
     | 
| 
      
 198 
     | 
    
         
            +
                  @timestamp_fn.call()
         
     | 
| 
      
 199 
     | 
    
         
            +
                end
         
     | 
| 
       159 
200 
     | 
    
         | 
| 
       160 
     | 
    
         
            -
                def post_to_inbox(message)
         
     | 
| 
      
 201 
     | 
    
         
            +
                private def post_to_inbox(message)
         
     | 
| 
       161 
202 
     | 
    
         
             
                  begin
         
     | 
| 
       162 
203 
     | 
    
         
             
                    @inbox.push(message, non_block=true)
         
     | 
| 
       163 
204 
     | 
    
         
             
                  rescue ThreadError
         
     | 
| 
         @@ -180,13 +221,13 @@ module LaunchDarkly 
     | 
|
| 
       180 
221 
     | 
    
         
             
                  @diagnostic_accumulator = config.diagnostic_opt_out? ? nil : diagnostic_accumulator
         
     | 
| 
       181 
222 
     | 
    
         
             
                  @event_sender = event_sender
         
     | 
| 
       182 
223 
     | 
    
         | 
| 
       183 
     | 
    
         
            -
                  @ 
     | 
| 
      
 224 
     | 
    
         
            +
                  @context_keys = SimpleLRUCacheSet.new(config.context_keys_capacity)
         
     | 
| 
       184 
225 
     | 
    
         
             
                  @formatter = EventOutputFormatter.new(config)
         
     | 
| 
       185 
226 
     | 
    
         
             
                  @disabled = Concurrent::AtomicBoolean.new(false)
         
     | 
| 
       186 
227 
     | 
    
         
             
                  @last_known_past_time = Concurrent::AtomicReference.new(0)
         
     | 
| 
       187 
     | 
    
         
            -
                  @ 
     | 
| 
      
 228 
     | 
    
         
            +
                  @deduplicated_contexts = 0
         
     | 
| 
       188 
229 
     | 
    
         
             
                  @events_in_last_batch = 0
         
     | 
| 
       189 
     | 
    
         
            -
             
     | 
| 
      
 230 
     | 
    
         
            +
             
     | 
| 
       190 
231 
     | 
    
         
             
                  outbox = EventBuffer.new(config.capacity, config.logger)
         
     | 
| 
       191 
232 
     | 
    
         
             
                  flush_workers = NonBlockingThreadPool.new(MAX_FLUSH_WORKERS)
         
     | 
| 
       192 
233 
     | 
    
         | 
| 
         @@ -209,12 +250,10 @@ module LaunchDarkly 
     | 
|
| 
       209 
250 
     | 
    
         
             
                    begin
         
     | 
| 
       210 
251 
     | 
    
         
             
                      message = inbox.pop
         
     | 
| 
       211 
252 
     | 
    
         
             
                      case message
         
     | 
| 
       212 
     | 
    
         
            -
                      when EventMessage
         
     | 
| 
       213 
     | 
    
         
            -
                        dispatch_event(message.event, outbox)
         
     | 
| 
       214 
253 
     | 
    
         
             
                      when FlushMessage
         
     | 
| 
       215 
254 
     | 
    
         
             
                        trigger_flush(outbox, flush_workers)
         
     | 
| 
       216 
     | 
    
         
            -
                      when  
     | 
| 
       217 
     | 
    
         
            -
                        @ 
     | 
| 
      
 255 
     | 
    
         
            +
                      when FlushContextsMessage
         
     | 
| 
      
 256 
     | 
    
         
            +
                        @context_keys.clear
         
     | 
| 
       218 
257 
     | 
    
         
             
                      when DiagnosticEventMessage
         
     | 
| 
       219 
258 
     | 
    
         
             
                        send_and_reset_diagnostics(outbox, diagnostic_event_workers)
         
     | 
| 
       220 
259 
     | 
    
         
             
                      when TestSyncMessage
         
     | 
| 
         @@ -224,6 +263,8 @@ module LaunchDarkly 
     | 
|
| 
       224 
263 
     | 
    
         
             
                        do_shutdown(flush_workers, diagnostic_event_workers)
         
     | 
| 
       225 
264 
     | 
    
         
             
                        running = false
         
     | 
| 
       226 
265 
     | 
    
         
             
                        message.completed
         
     | 
| 
      
 266 
     | 
    
         
            +
                      else
         
     | 
| 
      
 267 
     | 
    
         
            +
                        dispatch_event(message, outbox)
         
     | 
| 
       227 
268 
     | 
    
         
             
                      end
         
     | 
| 
       228 
269 
     | 
    
         
             
                    rescue => e
         
     | 
| 
       229 
270 
     | 
    
         
             
                      Util.log_exception(@config.logger, "Unexpected error in event processor", e)
         
     | 
| 
         @@ -234,7 +275,7 @@ module LaunchDarkly 
     | 
|
| 
       234 
275 
     | 
    
         
             
                def do_shutdown(flush_workers, diagnostic_event_workers)
         
     | 
| 
       235 
276 
     | 
    
         
             
                  flush_workers.shutdown
         
     | 
| 
       236 
277 
     | 
    
         
             
                  flush_workers.wait_for_termination
         
     | 
| 
       237 
     | 
    
         
            -
                   
     | 
| 
      
 278 
     | 
    
         
            +
                  unless diagnostic_event_workers.nil?
         
     | 
| 
       238 
279 
     | 
    
         
             
                    diagnostic_event_workers.shutdown
         
     | 
| 
       239 
280 
     | 
    
         
             
                    diagnostic_event_workers.wait_for_termination
         
     | 
| 
       240 
281 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -244,7 +285,7 @@ module LaunchDarkly 
     | 
|
| 
       244 
285 
     | 
    
         
             
                def synchronize_for_testing(flush_workers, diagnostic_event_workers)
         
     | 
| 
       245 
286 
     | 
    
         
             
                  # Used only by unit tests. Wait until all active flush workers have finished.
         
     | 
| 
       246 
287 
     | 
    
         
             
                  flush_workers.wait_all
         
     | 
| 
       247 
     | 
    
         
            -
                  diagnostic_event_workers.wait_all  
     | 
| 
      
 288 
     | 
    
         
            +
                  diagnostic_event_workers.wait_all unless diagnostic_event_workers.nil?
         
     | 
| 
       248 
289 
     | 
    
         
             
                end
         
     | 
| 
       249 
290 
     | 
    
         | 
| 
       250 
291 
     | 
    
         
             
                def dispatch_event(event, outbox)
         
     | 
| 
         @@ -257,45 +298,39 @@ module LaunchDarkly 
     | 
|
| 
       257 
298 
     | 
    
         
             
                  # the event (if tracked) and once for debugging.
         
     | 
| 
       258 
299 
     | 
    
         
             
                  will_add_full_event = false
         
     | 
| 
       259 
300 
     | 
    
         
             
                  debug_event = nil
         
     | 
| 
       260 
     | 
    
         
            -
                  if event 
     | 
| 
       261 
     | 
    
         
            -
                    will_add_full_event = event 
     | 
| 
      
 301 
     | 
    
         
            +
                  if event.is_a?(LaunchDarkly::Impl::EvalEvent)
         
     | 
| 
      
 302 
     | 
    
         
            +
                    will_add_full_event = event.track_events
         
     | 
| 
       262 
303 
     | 
    
         
             
                    if should_debug_event(event)
         
     | 
| 
       263 
     | 
    
         
            -
                      debug_event = event 
     | 
| 
       264 
     | 
    
         
            -
                      debug_event[:debug] = true
         
     | 
| 
      
 304 
     | 
    
         
            +
                      debug_event = LaunchDarkly::Impl::DebugEvent.new(event)
         
     | 
| 
       265 
305 
     | 
    
         
             
                    end
         
     | 
| 
       266 
306 
     | 
    
         
             
                  else
         
     | 
| 
       267 
307 
     | 
    
         
             
                    will_add_full_event = true
         
     | 
| 
       268 
308 
     | 
    
         
             
                  end
         
     | 
| 
       269 
309 
     | 
    
         | 
| 
       270 
     | 
    
         
            -
                  # For each  
     | 
| 
       271 
     | 
    
         
            -
                  # an identify event for that  
     | 
| 
       272 
     | 
    
         
            -
                  if !( 
     | 
| 
       273 
     | 
    
         
            -
                     
     | 
| 
       274 
     | 
    
         
            -
                      outbox.add_event({
         
     | 
| 
       275 
     | 
    
         
            -
                        kind: "index",
         
     | 
| 
       276 
     | 
    
         
            -
                        creationDate: event[:creationDate],
         
     | 
| 
       277 
     | 
    
         
            -
                        user: event[:user]
         
     | 
| 
       278 
     | 
    
         
            -
                      })
         
     | 
| 
       279 
     | 
    
         
            -
                    end
         
     | 
| 
      
 310 
     | 
    
         
            +
                  # For each context we haven't seen before, we add an index event - unless this is already
         
     | 
| 
      
 311 
     | 
    
         
            +
                  # an identify event for that context.
         
     | 
| 
      
 312 
     | 
    
         
            +
                  if !event.context.nil? && !notice_context(event.context) && !event.is_a?(LaunchDarkly::Impl::IdentifyEvent)
         
     | 
| 
      
 313 
     | 
    
         
            +
                    outbox.add_event(LaunchDarkly::Impl::IndexEvent.new(event.timestamp, event.context))
         
     | 
| 
       280 
314 
     | 
    
         
             
                  end
         
     | 
| 
       281 
315 
     | 
    
         | 
| 
       282 
316 
     | 
    
         
             
                  outbox.add_event(event) if will_add_full_event
         
     | 
| 
       283 
     | 
    
         
            -
                  outbox.add_event(debug_event)  
     | 
| 
      
 317 
     | 
    
         
            +
                  outbox.add_event(debug_event) unless debug_event.nil?
         
     | 
| 
       284 
318 
     | 
    
         
             
                end
         
     | 
| 
       285 
319 
     | 
    
         | 
| 
       286 
     | 
    
         
            -
                # 
     | 
| 
       287 
     | 
    
         
            -
                 
     | 
| 
       288 
     | 
    
         
            -
             
     | 
| 
       289 
     | 
    
         
            -
             
     | 
| 
       290 
     | 
    
         
            -
             
     | 
| 
       291 
     | 
    
         
            -
             
     | 
| 
       292 
     | 
    
         
            -
             
     | 
| 
       293 
     | 
    
         
            -
             
     | 
| 
       294 
     | 
    
         
            -
                   
     | 
| 
      
 320 
     | 
    
         
            +
                #
         
     | 
| 
      
 321 
     | 
    
         
            +
                # Add to the set of contexts we've noticed, and return true if the context
         
     | 
| 
      
 322 
     | 
    
         
            +
                # was already known to us.
         
     | 
| 
      
 323 
     | 
    
         
            +
                # @param context [LaunchDarkly::LDContext]
         
     | 
| 
      
 324 
     | 
    
         
            +
                # @return [Boolean]
         
     | 
| 
      
 325 
     | 
    
         
            +
                #
         
     | 
| 
      
 326 
     | 
    
         
            +
                def notice_context(context)
         
     | 
| 
      
 327 
     | 
    
         
            +
                  known = @context_keys.add(context.fully_qualified_key)
         
     | 
| 
      
 328 
     | 
    
         
            +
                  @deduplicated_contexts += 1 if known
         
     | 
| 
      
 329 
     | 
    
         
            +
                  known
         
     | 
| 
       295 
330 
     | 
    
         
             
                end
         
     | 
| 
       296 
331 
     | 
    
         | 
| 
       297 
332 
     | 
    
         
             
                def should_debug_event(event)
         
     | 
| 
       298 
     | 
    
         
            -
                  debug_until = event 
     | 
| 
      
 333 
     | 
    
         
            +
                  debug_until = event.debug_until
         
     | 
| 
       299 
334 
     | 
    
         
             
                  if !debug_until.nil?
         
     | 
| 
       300 
335 
     | 
    
         
             
                    last_past = @last_known_past_time.value
         
     | 
| 
       301 
336 
     | 
    
         
             
                    debug_until > last_past && debug_until > Impl::Util.current_time_millis
         
     | 
| 
         @@ -309,7 +344,7 @@ module LaunchDarkly 
     | 
|
| 
       309 
344 
     | 
    
         
             
                    return
         
     | 
| 
       310 
345 
     | 
    
         
             
                  end
         
     | 
| 
       311 
346 
     | 
    
         | 
| 
       312 
     | 
    
         
            -
                  payload = outbox.get_payload 
     | 
| 
      
 347 
     | 
    
         
            +
                  payload = outbox.get_payload
         
     | 
| 
       313 
348 
     | 
    
         
             
                  if !payload.events.empty? || !payload.summary.counters.empty?
         
     | 
| 
       314 
349 
     | 
    
         
             
                    count = payload.events.length + (payload.summary.counters.empty? ? 0 : 1)
         
     | 
| 
       315 
350 
     | 
    
         
             
                    @events_in_last_batch = count
         
     | 
| 
         @@ -319,7 +354,7 @@ module LaunchDarkly 
     | 
|
| 
       319 
354 
     | 
    
         
             
                        events_out = @formatter.make_output_events(payload.events, payload.summary)
         
     | 
| 
       320 
355 
     | 
    
         
             
                        result = @event_sender.send_event_data(events_out.to_json, "#{events_out.length} events", false)
         
     | 
| 
       321 
356 
     | 
    
         
             
                        @disabled.value = true if result.must_shutdown
         
     | 
| 
       322 
     | 
    
         
            -
                         
     | 
| 
      
 357 
     | 
    
         
            +
                        unless result.time_from_server.nil?
         
     | 
| 
       323 
358 
     | 
    
         
             
                          @last_known_past_time.value = (result.time_from_server.to_f * 1000).to_i
         
     | 
| 
       324 
359 
     | 
    
         
             
                        end
         
     | 
| 
       325 
360 
     | 
    
         
             
                      rescue => e
         
     | 
| 
         @@ -335,8 +370,8 @@ module LaunchDarkly 
     | 
|
| 
       335 
370 
     | 
    
         
             
                def send_and_reset_diagnostics(outbox, diagnostic_event_workers)
         
     | 
| 
       336 
371 
     | 
    
         
             
                  return if @diagnostic_accumulator.nil?
         
     | 
| 
       337 
372 
     | 
    
         
             
                  dropped_count = outbox.get_and_clear_dropped_count
         
     | 
| 
       338 
     | 
    
         
            -
                  event = @diagnostic_accumulator.create_periodic_event_and_reset(dropped_count, @ 
     | 
| 
       339 
     | 
    
         
            -
                  @ 
     | 
| 
      
 373 
     | 
    
         
            +
                  event = @diagnostic_accumulator.create_periodic_event_and_reset(dropped_count, @deduplicated_contexts, @events_in_last_batch)
         
     | 
| 
      
 374 
     | 
    
         
            +
                  @deduplicated_contexts = 0
         
     | 
| 
       340 
375 
     | 
    
         
             
                  @events_in_last_batch = 0
         
     | 
| 
       341 
376 
     | 
    
         
             
                  send_diagnostic_event(event, diagnostic_event_workers)
         
     | 
| 
       342 
377 
     | 
    
         
             
                end
         
     | 
| 
         @@ -365,17 +400,16 @@ module LaunchDarkly 
     | 
|
| 
       365 
400 
     | 
    
         
             
                  @capacity_exceeded = false
         
     | 
| 
       366 
401 
     | 
    
         
             
                  @dropped_events = 0
         
     | 
| 
       367 
402 
     | 
    
         
             
                  @events = []
         
     | 
| 
       368 
     | 
    
         
            -
                  @summarizer = EventSummarizer.new
         
     | 
| 
      
 403 
     | 
    
         
            +
                  @summarizer = LaunchDarkly::Impl::EventSummarizer.new
         
     | 
| 
       369 
404 
     | 
    
         
             
                end
         
     | 
| 
       370 
405 
     | 
    
         | 
| 
       371 
406 
     | 
    
         
             
                def add_event(event)
         
     | 
| 
       372 
407 
     | 
    
         
             
                  if @events.length < @capacity
         
     | 
| 
       373 
     | 
    
         
            -
                    @logger.debug { "[LDClient] Enqueueing event: #{event.to_json}" }
         
     | 
| 
       374 
408 
     | 
    
         
             
                    @events.push(event)
         
     | 
| 
       375 
409 
     | 
    
         
             
                    @capacity_exceeded = false
         
     | 
| 
       376 
410 
     | 
    
         
             
                  else
         
     | 
| 
       377 
411 
     | 
    
         
             
                    @dropped_events += 1
         
     | 
| 
       378 
     | 
    
         
            -
                     
     | 
| 
      
 412 
     | 
    
         
            +
                    unless @capacity_exceeded
         
     | 
| 
       379 
413 
     | 
    
         
             
                      @capacity_exceeded = true
         
     | 
| 
       380 
414 
     | 
    
         
             
                      @logger.warn { "[LDClient] Exceeded event queue capacity. Increase capacity to avoid dropping events." }
         
     | 
| 
       381 
415 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -387,7 +421,7 @@ module LaunchDarkly 
     | 
|
| 
       387 
421 
     | 
    
         
             
                end
         
     | 
| 
       388 
422 
     | 
    
         | 
| 
       389 
423 
     | 
    
         
             
                def get_payload
         
     | 
| 
       390 
     | 
    
         
            -
                   
     | 
| 
      
 424 
     | 
    
         
            +
                  FlushPayload.new(@events, @summarizer.snapshot)
         
     | 
| 
       391 
425 
     | 
    
         
             
                end
         
     | 
| 
       392 
426 
     | 
    
         | 
| 
       393 
427 
     | 
    
         
             
                def get_and_clear_dropped_count
         
     | 
| 
         @@ -404,113 +438,118 @@ module LaunchDarkly 
     | 
|
| 
       404 
438 
     | 
    
         | 
| 
       405 
439 
     | 
    
         
             
              # @private
         
     | 
| 
       406 
440 
     | 
    
         
             
              class EventOutputFormatter
         
     | 
| 
      
 441 
     | 
    
         
            +
                FEATURE_KIND = 'feature'
         
     | 
| 
      
 442 
     | 
    
         
            +
                IDENTIFY_KIND = 'identify'
         
     | 
| 
      
 443 
     | 
    
         
            +
                CUSTOM_KIND = 'custom'
         
     | 
| 
      
 444 
     | 
    
         
            +
                INDEX_KIND = 'index'
         
     | 
| 
      
 445 
     | 
    
         
            +
                DEBUG_KIND = 'debug'
         
     | 
| 
      
 446 
     | 
    
         
            +
                SUMMARY_KIND = 'summary'
         
     | 
| 
      
 447 
     | 
    
         
            +
             
     | 
| 
       407 
448 
     | 
    
         
             
                def initialize(config)
         
     | 
| 
       408 
     | 
    
         
            -
                  @ 
     | 
| 
       409 
     | 
    
         
            -
                  @user_filter = UserFilter.new(config)
         
     | 
| 
      
 449 
     | 
    
         
            +
                  @context_filter = LaunchDarkly::Impl::ContextFilter.new(config.all_attributes_private, config.private_attributes)
         
     | 
| 
       410 
450 
     | 
    
         
             
                end
         
     | 
| 
       411 
451 
     | 
    
         | 
| 
       412 
452 
     | 
    
         
             
                # Transforms events into the format used for event sending.
         
     | 
| 
       413 
453 
     | 
    
         
             
                def make_output_events(events, summary)
         
     | 
| 
       414 
454 
     | 
    
         
             
                  events_out = events.map { |e| make_output_event(e) }
         
     | 
| 
       415 
     | 
    
         
            -
                   
     | 
| 
      
 455 
     | 
    
         
            +
                  unless summary.counters.empty?
         
     | 
| 
       416 
456 
     | 
    
         
             
                    events_out.push(make_summary_event(summary))
         
     | 
| 
       417 
457 
     | 
    
         
             
                  end
         
     | 
| 
       418 
458 
     | 
    
         
             
                  events_out
         
     | 
| 
       419 
459 
     | 
    
         
             
                end
         
     | 
| 
       420 
460 
     | 
    
         | 
| 
       421 
     | 
    
         
            -
                private
         
     | 
| 
       422 
     | 
    
         
            -
             
     | 
| 
       423 
     | 
    
         
            -
                def process_user(event)
         
     | 
| 
       424 
     | 
    
         
            -
                  filtered = @user_filter.transform_user_props(event[:user])
         
     | 
| 
       425 
     | 
    
         
            -
                  Util.stringify_attrs(filtered, USER_ATTRS_TO_STRINGIFY_FOR_EVENTS)
         
     | 
| 
       426 
     | 
    
         
            -
                end
         
     | 
| 
      
 461 
     | 
    
         
            +
                private def make_output_event(event)
         
     | 
| 
      
 462 
     | 
    
         
            +
                  case event
         
     | 
| 
       427 
463 
     | 
    
         | 
| 
       428 
     | 
    
         
            -
             
     | 
| 
       429 
     | 
    
         
            -
                  case event[:kind]
         
     | 
| 
       430 
     | 
    
         
            -
                  when "feature"
         
     | 
| 
       431 
     | 
    
         
            -
                    is_debug = event[:debug]
         
     | 
| 
      
 464 
     | 
    
         
            +
                  when LaunchDarkly::Impl::EvalEvent
         
     | 
| 
       432 
465 
     | 
    
         
             
                    out = {
         
     | 
| 
       433 
     | 
    
         
            -
                      kind:  
     | 
| 
       434 
     | 
    
         
            -
                      creationDate: event 
     | 
| 
       435 
     | 
    
         
            -
                      key: event 
     | 
| 
       436 
     | 
    
         
            -
                      value: event 
     | 
| 
      
 466 
     | 
    
         
            +
                      kind: FEATURE_KIND,
         
     | 
| 
      
 467 
     | 
    
         
            +
                      creationDate: event.timestamp,
         
     | 
| 
      
 468 
     | 
    
         
            +
                      key: event.key,
         
     | 
| 
      
 469 
     | 
    
         
            +
                      value: event.value,
         
     | 
| 
       437 
470 
     | 
    
         
             
                    }
         
     | 
| 
       438 
     | 
    
         
            -
                    out[:default] = event 
     | 
| 
       439 
     | 
    
         
            -
                    out[:variation] = event 
     | 
| 
       440 
     | 
    
         
            -
                    out[:version] = event 
     | 
| 
       441 
     | 
    
         
            -
                    out[:prereqOf] = event 
     | 
| 
       442 
     | 
    
         
            -
                    out[: 
     | 
| 
       443 
     | 
    
         
            -
                     
     | 
| 
       444 
     | 
    
         
            -
                      out[:user] = process_user(event)
         
     | 
| 
       445 
     | 
    
         
            -
                    else
         
     | 
| 
       446 
     | 
    
         
            -
                      out[:userKey] = event[:user][:key]
         
     | 
| 
       447 
     | 
    
         
            -
                    end
         
     | 
| 
       448 
     | 
    
         
            -
                    out[:reason] = event[:reason] if !event[:reason].nil?
         
     | 
| 
      
 471 
     | 
    
         
            +
                    out[:default] = event.default unless event.default.nil?
         
     | 
| 
      
 472 
     | 
    
         
            +
                    out[:variation] = event.variation unless event.variation.nil?
         
     | 
| 
      
 473 
     | 
    
         
            +
                    out[:version] = event.version unless event.version.nil?
         
     | 
| 
      
 474 
     | 
    
         
            +
                    out[:prereqOf] = event.prereq_of unless event.prereq_of.nil?
         
     | 
| 
      
 475 
     | 
    
         
            +
                    out[:contextKeys] = event.context.keys
         
     | 
| 
      
 476 
     | 
    
         
            +
                    out[:reason] = event.reason unless event.reason.nil?
         
     | 
| 
       449 
477 
     | 
    
         
             
                    out
         
     | 
| 
       450 
     | 
    
         
            -
             
     | 
| 
      
 478 
     | 
    
         
            +
             
     | 
| 
      
 479 
     | 
    
         
            +
                  when LaunchDarkly::Impl::IdentifyEvent
         
     | 
| 
       451 
480 
     | 
    
         
             
                    {
         
     | 
| 
       452 
     | 
    
         
            -
                      kind:  
     | 
| 
       453 
     | 
    
         
            -
                      creationDate: event 
     | 
| 
       454 
     | 
    
         
            -
                      key: event 
     | 
| 
       455 
     | 
    
         
            -
                       
     | 
| 
      
 481 
     | 
    
         
            +
                      kind: IDENTIFY_KIND,
         
     | 
| 
      
 482 
     | 
    
         
            +
                      creationDate: event.timestamp,
         
     | 
| 
      
 483 
     | 
    
         
            +
                      key: event.context.fully_qualified_key,
         
     | 
| 
      
 484 
     | 
    
         
            +
                      context: @context_filter.filter(event.context),
         
     | 
| 
       456 
485 
     | 
    
         
             
                    }
         
     | 
| 
       457 
     | 
    
         
            -
             
     | 
| 
      
 486 
     | 
    
         
            +
             
     | 
| 
      
 487 
     | 
    
         
            +
                  when LaunchDarkly::Impl::CustomEvent
         
     | 
| 
       458 
488 
     | 
    
         
             
                    out = {
         
     | 
| 
       459 
     | 
    
         
            -
                      kind:  
     | 
| 
       460 
     | 
    
         
            -
                      creationDate: event 
     | 
| 
       461 
     | 
    
         
            -
                      key: event 
     | 
| 
      
 489 
     | 
    
         
            +
                      kind: CUSTOM_KIND,
         
     | 
| 
      
 490 
     | 
    
         
            +
                      creationDate: event.timestamp,
         
     | 
| 
      
 491 
     | 
    
         
            +
                      key: event.key,
         
     | 
| 
       462 
492 
     | 
    
         
             
                    }
         
     | 
| 
       463 
     | 
    
         
            -
                    out[:data] = event 
     | 
| 
       464 
     | 
    
         
            -
                     
     | 
| 
       465 
     | 
    
         
            -
             
     | 
| 
       466 
     | 
    
         
            -
                    else
         
     | 
| 
       467 
     | 
    
         
            -
                      out[:userKey] = event[:user][:key]
         
     | 
| 
       468 
     | 
    
         
            -
                    end
         
     | 
| 
       469 
     | 
    
         
            -
                    out[:metricValue] = event[:metricValue] if event.has_key?(:metricValue)
         
     | 
| 
       470 
     | 
    
         
            -
                    out[:contextKind] = event[:contextKind] if event.has_key?(:contextKind)
         
     | 
| 
      
 493 
     | 
    
         
            +
                    out[:data] = event.data unless event.data.nil?
         
     | 
| 
      
 494 
     | 
    
         
            +
                    out[:contextKeys] = event.context.keys
         
     | 
| 
      
 495 
     | 
    
         
            +
                    out[:metricValue] = event.metric_value unless event.metric_value.nil?
         
     | 
| 
       471 
496 
     | 
    
         
             
                    out
         
     | 
| 
       472 
     | 
    
         
            -
             
     | 
| 
      
 497 
     | 
    
         
            +
             
     | 
| 
      
 498 
     | 
    
         
            +
                  when LaunchDarkly::Impl::IndexEvent
         
     | 
| 
       473 
499 
     | 
    
         
             
                    {
         
     | 
| 
       474 
     | 
    
         
            -
                      kind:  
     | 
| 
       475 
     | 
    
         
            -
                      creationDate: event 
     | 
| 
       476 
     | 
    
         
            -
                       
     | 
| 
      
 500 
     | 
    
         
            +
                      kind: INDEX_KIND,
         
     | 
| 
      
 501 
     | 
    
         
            +
                      creationDate: event.timestamp,
         
     | 
| 
      
 502 
     | 
    
         
            +
                      context: @context_filter.filter(event.context),
         
     | 
| 
       477 
503 
     | 
    
         
             
                    }
         
     | 
| 
      
 504 
     | 
    
         
            +
             
     | 
| 
      
 505 
     | 
    
         
            +
                  when LaunchDarkly::Impl::DebugEvent
         
     | 
| 
      
 506 
     | 
    
         
            +
                    original = event.eval_event
         
     | 
| 
      
 507 
     | 
    
         
            +
                    out = {
         
     | 
| 
      
 508 
     | 
    
         
            +
                      kind: DEBUG_KIND,
         
     | 
| 
      
 509 
     | 
    
         
            +
                      creationDate: original.timestamp,
         
     | 
| 
      
 510 
     | 
    
         
            +
                      key: original.key,
         
     | 
| 
      
 511 
     | 
    
         
            +
                      context: @context_filter.filter(original.context),
         
     | 
| 
      
 512 
     | 
    
         
            +
                      value: original.value,
         
     | 
| 
      
 513 
     | 
    
         
            +
                    }
         
     | 
| 
      
 514 
     | 
    
         
            +
                    out[:default] = original.default unless original.default.nil?
         
     | 
| 
      
 515 
     | 
    
         
            +
                    out[:variation] = original.variation unless original.variation.nil?
         
     | 
| 
      
 516 
     | 
    
         
            +
                    out[:version] = original.version unless original.version.nil?
         
     | 
| 
      
 517 
     | 
    
         
            +
                    out[:prereqOf] = original.prereq_of unless original.prereq_of.nil?
         
     | 
| 
      
 518 
     | 
    
         
            +
                    out[:reason] = original.reason unless original.reason.nil?
         
     | 
| 
      
 519 
     | 
    
         
            +
                    out
         
     | 
| 
      
 520 
     | 
    
         
            +
             
     | 
| 
       478 
521 
     | 
    
         
             
                  else
         
     | 
| 
       479 
     | 
    
         
            -
                     
     | 
| 
      
 522 
     | 
    
         
            +
                    nil
         
     | 
| 
       480 
523 
     | 
    
         
             
                  end
         
     | 
| 
       481 
524 
     | 
    
         
             
                end
         
     | 
| 
       482 
525 
     | 
    
         | 
| 
       483 
526 
     | 
    
         
             
                # Transforms the summary data into the format used for event sending.
         
     | 
| 
       484 
     | 
    
         
            -
                def make_summary_event(summary)
         
     | 
| 
      
 527 
     | 
    
         
            +
                private def make_summary_event(summary)
         
     | 
| 
       485 
528 
     | 
    
         
             
                  flags = {}
         
     | 
| 
       486 
     | 
    
         
            -
                  summary 
     | 
| 
       487 
     | 
    
         
            -
                     
     | 
| 
       488 
     | 
    
         
            -
                     
     | 
| 
       489 
     | 
    
         
            -
                       
     | 
| 
       490 
     | 
    
         
            -
                         
     | 
| 
       491 
     | 
    
         
            -
             
     | 
| 
       492 
     | 
    
         
            -
             
     | 
| 
       493 
     | 
    
         
            -
             
     | 
| 
       494 
     | 
    
         
            -
             
     | 
| 
       495 
     | 
    
         
            -
             
     | 
| 
       496 
     | 
    
         
            -
             
     | 
| 
       497 
     | 
    
         
            -
             
     | 
| 
       498 
     | 
    
         
            -
             
     | 
| 
       499 
     | 
    
         
            -
             
     | 
| 
       500 
     | 
    
         
            -
             
     | 
| 
       501 
     | 
    
         
            -
             
     | 
| 
       502 
     | 
    
         
            -
                    if ckey[:version].nil?
         
     | 
| 
       503 
     | 
    
         
            -
                      c[:unknown] = true
         
     | 
| 
       504 
     | 
    
         
            -
                    else
         
     | 
| 
       505 
     | 
    
         
            -
                      c[:version] = ckey[:version]
         
     | 
| 
      
 529 
     | 
    
         
            +
                  summary.counters.each do |flagKey, flagInfo|
         
     | 
| 
      
 530 
     | 
    
         
            +
                    counters = []
         
     | 
| 
      
 531 
     | 
    
         
            +
                    flagInfo.versions.each do |version, variations|
         
     | 
| 
      
 532 
     | 
    
         
            +
                      variations.each do |variation, counter|
         
     | 
| 
      
 533 
     | 
    
         
            +
                        c = {
         
     | 
| 
      
 534 
     | 
    
         
            +
                          value: counter.value,
         
     | 
| 
      
 535 
     | 
    
         
            +
                          count: counter.count,
         
     | 
| 
      
 536 
     | 
    
         
            +
                        }
         
     | 
| 
      
 537 
     | 
    
         
            +
                        c[:variation] = variation unless variation.nil?
         
     | 
| 
      
 538 
     | 
    
         
            +
                        if version.nil?
         
     | 
| 
      
 539 
     | 
    
         
            +
                          c[:unknown] = true
         
     | 
| 
      
 540 
     | 
    
         
            +
                        else
         
     | 
| 
      
 541 
     | 
    
         
            +
                          c[:version] = version
         
     | 
| 
      
 542 
     | 
    
         
            +
                        end
         
     | 
| 
      
 543 
     | 
    
         
            +
                        counters.push(c)
         
     | 
| 
      
 544 
     | 
    
         
            +
                      end
         
     | 
| 
       506 
545 
     | 
    
         
             
                    end
         
     | 
| 
       507 
     | 
    
         
            -
                     
     | 
| 
       508 
     | 
    
         
            -
                   
     | 
| 
      
 546 
     | 
    
         
            +
                    flags[flagKey] = { default: flagInfo.default, counters: counters, contextKinds: flagInfo.context_kinds.to_a }
         
     | 
| 
      
 547 
     | 
    
         
            +
                  end
         
     | 
| 
       509 
548 
     | 
    
         
             
                  {
         
     | 
| 
       510 
     | 
    
         
            -
                    kind:  
     | 
| 
      
 549 
     | 
    
         
            +
                    kind: SUMMARY_KIND,
         
     | 
| 
       511 
550 
     | 
    
         
             
                    startDate: summary[:start_date],
         
     | 
| 
       512 
551 
     | 
    
         
             
                    endDate: summary[:end_date],
         
     | 
| 
       513 
     | 
    
         
            -
                    features: flags
         
     | 
| 
      
 552 
     | 
    
         
            +
                    features: flags,
         
     | 
| 
       514 
553 
     | 
    
         
             
                  }
         
     | 
| 
       515 
554 
     | 
    
         
             
                end
         
     | 
| 
       516 
555 
     | 
    
         
             
              end
         
     | 
| 
         @@ -2,7 +2,7 @@ require 'json' 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module LaunchDarkly
         
     | 
| 
       4 
4 
     | 
    
         
             
              #
         
     | 
| 
       5 
     | 
    
         
            -
              # A snapshot of the state of all feature flags with regard to a specific  
     | 
| 
      
 5 
     | 
    
         
            +
              # A snapshot of the state of all feature flags with regard to a specific context, generated by
         
     | 
| 
       6 
6 
     | 
    
         
             
              # calling the {LDClient#all_flags_state}. Serializing this object to JSON using
         
     | 
| 
       7 
7 
     | 
    
         
             
              # `JSON.generate` (or the `to_json` method) will produce the appropriate data structure for
         
     | 
| 
       8 
8 
     | 
    
         
             
              # bootstrapping the LaunchDarkly JavaScript client.
         
     | 
| 
         @@ -16,26 +16,37 @@ module LaunchDarkly 
     | 
|
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
                # Used internally to build the state map.
         
     | 
| 
       18 
18 
     | 
    
         
             
                # @private
         
     | 
| 
       19 
     | 
    
         
            -
                def add_flag( 
     | 
| 
       20 
     | 
    
         
            -
                  key =  
     | 
| 
       21 
     | 
    
         
            -
                  @flag_values[key] = value
         
     | 
| 
      
 19 
     | 
    
         
            +
                def add_flag(flag_state, with_reasons, details_only_if_tracked)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  key = flag_state[:key]
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @flag_values[key] = flag_state[:value]
         
     | 
| 
       22 
22 
     | 
    
         
             
                  meta = {}
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
                   
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  omit_details = false
         
     | 
| 
      
 25 
     | 
    
         
            +
                  if details_only_if_tracked
         
     | 
| 
      
 26 
     | 
    
         
            +
                    if !flag_state[:trackEvents] && !flag_state[:trackReason] && !(flag_state[:debugEventsUntilDate] && flag_state[:debugEventsUntilDate] > Impl::Util::current_time_millis)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      omit_details = true
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  reason = (!with_reasons and !flag_state[:trackReason]) ? nil : flag_state[:reason]
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  if !reason.nil? && !omit_details
         
     | 
| 
      
 34 
     | 
    
         
            +
                    meta[:reason] = reason
         
     | 
| 
       26 
35 
     | 
    
         
             
                  end
         
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
                    meta[: 
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  unless omit_details
         
     | 
| 
      
 38 
     | 
    
         
            +
                    meta[:version] = flag_state[:version]
         
     | 
| 
       30 
39 
     | 
    
         
             
                  end
         
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                  meta[: 
     | 
| 
       33 
     | 
    
         
            -
                  meta[: 
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  meta[:variation] = flag_state[:variation] unless flag_state[:variation].nil?
         
     | 
| 
      
 42 
     | 
    
         
            +
                  meta[:trackEvents] = true if flag_state[:trackEvents]
         
     | 
| 
      
 43 
     | 
    
         
            +
                  meta[:trackReason] = true if flag_state[:trackReason]
         
     | 
| 
      
 44 
     | 
    
         
            +
                  meta[:debugEventsUntilDate] = flag_state[:debugEventsUntilDate] if flag_state[:debugEventsUntilDate]
         
     | 
| 
       34 
45 
     | 
    
         
             
                  @flag_metadata[key] = meta
         
     | 
| 
       35 
46 
     | 
    
         
             
                end
         
     | 
| 
       36 
47 
     | 
    
         | 
| 
       37 
48 
     | 
    
         
             
                # Returns true if this object contains a valid snapshot of feature flag state, or false if the
         
     | 
| 
       38 
     | 
    
         
            -
                # state could not be computed (for instance, because the client was offline or there was no  
     | 
| 
      
 49 
     | 
    
         
            +
                # state could not be computed (for instance, because the client was offline or there was no context).
         
     | 
| 
       39 
50 
     | 
    
         
             
                def valid?
         
     | 
| 
       40 
51 
     | 
    
         
             
                  @valid
         
     | 
| 
       41 
52 
     | 
    
         
             
                end
         
     |