statsig 1.2.0 → 1.6.1
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/lib/dynamic_config.rb +3 -3
- data/lib/evaluation_helpers.rb +3 -11
- data/lib/evaluator.rb +32 -23
- data/lib/network.rb +26 -9
- data/lib/statsig_driver.rb +9 -6
- data/lib/statsig_event.rb +9 -2
- data/lib/statsig_logger.rb +11 -2
- data/lib/statsig_user.rb +10 -2
- metadata +44 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 8dfdb320cf540cd81046ba96aae19d9d3f947ec557ad7fb7b405a35065eb67fa
         | 
| 4 | 
            +
              data.tar.gz: 0e8d15d9c3f9aa0245c2e834e55394479775d2a9de23208e54dfb41be89e0326
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: e7a479f8ed9b2cbf49f4b567bdf16000b5ba23a88015593cec6e9806477419011a13b5ed64fd578208cec5f6633101c881e3fa0b98b0474a2faebc91aaf8be1e
         | 
| 7 | 
            +
              data.tar.gz: d731197be16b7ef43d6c9060f00254b3db3a205bef736243a326a382e9fca1aa72b1335102612ab13c044a03f4dc247e0b3443badfad147dffdd8be8391a038d
         | 
    
        data/lib/dynamic_config.rb
    CHANGED
    
    
    
        data/lib/evaluation_helpers.rb
    CHANGED
    
    | @@ -6,19 +6,11 @@ module EvaluationHelpers | |
| 6 6 | 
             
                func.call(a.to_f, b.to_f) rescue false
         | 
| 7 7 | 
             
              end
         | 
| 8 8 |  | 
| 9 | 
            -
              # returns true if array contains value, ignoring case when comparing strings
         | 
| 10 | 
            -
              def self.array_contains(array, value, ignore_case)
         | 
| 11 | 
            -
                return false unless array.is_a?(Array) && !value.nil?
         | 
| 12 | 
            -
                if value.is_a?(String) && match_string_in_array(array, value, ignore_case, ->(a, b) { a == b })
         | 
| 13 | 
            -
                  return true
         | 
| 14 | 
            -
                end
         | 
| 15 | 
            -
                return array.include?(value)
         | 
| 16 | 
            -
              end
         | 
| 17 | 
            -
             | 
| 18 9 | 
             
              # returns true if array has any element that evaluates to true with value using func lambda, ignoring case
         | 
| 19 10 | 
             
              def self.match_string_in_array(array, value, ignore_case, func)
         | 
| 20 | 
            -
                return false unless array.is_a?(Array) && value. | 
| 21 | 
            -
                 | 
| 11 | 
            +
                return false unless array.is_a?(Array) && !value.nil?
         | 
| 12 | 
            +
                str_value = value.to_s
         | 
| 13 | 
            +
                array.any?{ |s| !s.nil? && ((ignore_case && func.call(str_value.downcase, s.to_s.downcase)) || func.call(str_value, s.to_s)) } rescue false
         | 
| 22 14 | 
             
              end
         | 
| 23 15 |  | 
| 24 16 | 
             
              def self.compare_times(a, b, func)
         | 
    
        data/lib/evaluator.rb
    CHANGED
    
    | @@ -79,16 +79,15 @@ class Evaluator | |
| 79 79 | 
             
                case type
         | 
| 80 80 | 
             
                when 'public'
         | 
| 81 81 | 
             
                  return true
         | 
| 82 | 
            -
                when 'fail_gate'
         | 
| 83 | 
            -
                when 'pass_gate'
         | 
| 82 | 
            +
                when 'fail_gate', 'pass_gate'
         | 
| 84 83 | 
             
                  other_gate_result = self.check_gate(user, target)
         | 
| 85 84 | 
             
                  return $fetch_from_server if other_gate_result == $fetch_from_server
         | 
| 86 85 | 
             
                  return type == 'pass_gate' ? other_gate_result.gate_value : !other_gate_result.gate_value
         | 
| 87 86 | 
             
                when 'ip_based'
         | 
| 88 | 
            -
                  value = get_value_from_user(user, field) || get_value_from_ip(user | 
| 87 | 
            +
                  value = get_value_from_user(user, field) || get_value_from_ip(user, field)
         | 
| 89 88 | 
             
                  return $fetch_from_server if value == $fetch_from_server
         | 
| 90 89 | 
             
                when 'ua_based'
         | 
| 91 | 
            -
                  value = get_value_from_user(user, field) || get_value_from_ua(user | 
| 90 | 
            +
                  value = get_value_from_user(user, field) || get_value_from_ua(user, field)
         | 
| 92 91 | 
             
                  return $fetch_from_server if value == $fetch_from_server
         | 
| 93 92 | 
             
                when 'user_field'
         | 
| 94 93 | 
             
                  value = get_value_from_user(user, field)
         | 
| @@ -109,10 +108,7 @@ class Evaluator | |
| 109 108 | 
             
                  return $fetch_from_server
         | 
| 110 109 | 
             
                end
         | 
| 111 110 |  | 
| 112 | 
            -
                return $fetch_from_server if value == $fetch_from_server
         | 
| 113 | 
            -
                return false if value.nil?
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                return $fetch_from_server unless operator.is_a?(String)
         | 
| 111 | 
            +
                return $fetch_from_server if value == $fetch_from_server || !operator.is_a?(String)
         | 
| 116 112 | 
             
                operator = operator.downcase
         | 
| 117 113 |  | 
| 118 114 | 
             
                case operator
         | 
| @@ -142,13 +138,13 @@ class Evaluator | |
| 142 138 |  | 
| 143 139 | 
             
                  # array operations
         | 
| 144 140 | 
             
                when 'any'
         | 
| 145 | 
            -
                  return EvaluationHelpers:: | 
| 141 | 
            +
                  return EvaluationHelpers::match_string_in_array(target, value, true, ->(a, b) { a == b })
         | 
| 146 142 | 
             
                when 'none'
         | 
| 147 | 
            -
                  return !EvaluationHelpers:: | 
| 143 | 
            +
                  return !EvaluationHelpers::match_string_in_array(target, value, true, ->(a, b) { a == b })
         | 
| 148 144 | 
             
                when 'any_case_sensitive'
         | 
| 149 | 
            -
                  return EvaluationHelpers:: | 
| 145 | 
            +
                  return EvaluationHelpers::match_string_in_array(target, value, false, ->(a, b) { a == b })
         | 
| 150 146 | 
             
                when 'none_case_sensitive'
         | 
| 151 | 
            -
                  return !EvaluationHelpers:: | 
| 147 | 
            +
                  return !EvaluationHelpers::match_string_in_array(target, value, false, ->(a, b) { a == b })
         | 
| 152 148 |  | 
| 153 149 | 
             
                  #string
         | 
| 154 150 | 
             
                when 'str_starts_with_any'
         | 
| @@ -157,6 +153,8 @@ class Evaluator | |
| 157 153 | 
             
                  return EvaluationHelpers::match_string_in_array(target, value, true, ->(a, b) { a.end_with?(b) })
         | 
| 158 154 | 
             
                when 'str_contains_any'
         | 
| 159 155 | 
             
                  return EvaluationHelpers::match_string_in_array(target, value, true, ->(a, b) { a.include?(b) })
         | 
| 156 | 
            +
                when 'str_contains_none'
         | 
| 157 | 
            +
                  return !EvaluationHelpers::match_string_in_array(target, value, true, ->(a, b) { a.include?(b) })
         | 
| 160 158 | 
             
                when 'str_matches'
         | 
| 161 159 | 
             
                  return (value.is_a?(String) && !(value =~ Regexp.new(target)).nil? rescue false)
         | 
| 162 160 | 
             
                when 'eq'
         | 
| @@ -181,13 +179,22 @@ class Evaluator | |
| 181 179 |  | 
| 182 180 | 
             
                user_lookup_table = user&.value_lookup
         | 
| 183 181 | 
             
                return nil unless user_lookup_table.is_a?(Hash)
         | 
| 184 | 
            -
                return user_lookup_table[field.downcase] if user_lookup_table.has_key?(field.downcase)
         | 
| 182 | 
            +
                return user_lookup_table[field.downcase] if user_lookup_table.has_key?(field.downcase) && !user_lookup_table[field.downcase].nil?
         | 
| 185 183 |  | 
| 186 184 | 
             
                user_custom = user_lookup_table['custom']
         | 
| 187 | 
            -
                 | 
| 188 | 
            -
             | 
| 189 | 
            -
             | 
| 185 | 
            +
                if user_custom.is_a?(Hash)
         | 
| 186 | 
            +
                  user_custom.each do |key, value|
         | 
| 187 | 
            +
                    return value if key.downcase.casecmp?(field.downcase) && !value.nil?
         | 
| 188 | 
            +
                  end
         | 
| 189 | 
            +
                end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                private_attributes = user_lookup_table['privateAttributes']
         | 
| 192 | 
            +
                if private_attributes.is_a?(Hash)
         | 
| 193 | 
            +
                  private_attributes.each do |key, value|
         | 
| 194 | 
            +
                    return value if key.downcase.casecmp?(field.downcase) && !value.nil?
         | 
| 195 | 
            +
                  end
         | 
| 190 196 | 
             
                end
         | 
| 197 | 
            +
             | 
| 191 198 | 
             
                nil
         | 
| 192 199 | 
             
              end
         | 
| 193 200 |  | 
| @@ -201,17 +208,19 @@ class Evaluator | |
| 201 208 | 
             
                nil
         | 
| 202 209 | 
             
              end
         | 
| 203 210 |  | 
| 204 | 
            -
              def get_value_from_ip( | 
| 205 | 
            -
                return nil unless  | 
| 211 | 
            +
              def get_value_from_ip(user, field)
         | 
| 212 | 
            +
                return nil unless user.is_a?(StatsigUser) && field.is_a?(String) && field.downcase == 'country'
         | 
| 213 | 
            +
                ip = get_value_from_user(user, 'ip')
         | 
| 214 | 
            +
                return nil unless ip.is_a?(String)
         | 
| 206 215 |  | 
| 207 | 
            -
                if field.downcase != 'country'
         | 
| 208 | 
            -
                  return $fetch_from_server
         | 
| 209 | 
            -
                end
         | 
| 210 216 | 
             
                CountryLookup.lookup_ip_string(ip)
         | 
| 211 217 | 
             
              end
         | 
| 212 218 |  | 
| 213 | 
            -
              def get_value_from_ua( | 
| 214 | 
            -
                return nil unless  | 
| 219 | 
            +
              def get_value_from_ua(user, field)
         | 
| 220 | 
            +
                return nil unless user.is_a?(StatsigUser) && field.is_a?(String)
         | 
| 221 | 
            +
                ua = get_value_from_user(user, 'userAgent')
         | 
| 222 | 
            +
                return nil unless ua.is_a?(String)
         | 
| 223 | 
            +
             | 
| 215 224 | 
             
                parsed = @ua_parser.parse ua
         | 
| 216 225 | 
             
                os = parsed.os
         | 
| 217 226 | 
             
                case field.downcase
         | 
    
        data/lib/network.rb
    CHANGED
    
    | @@ -2,8 +2,10 @@ require 'http' | |
| 2 2 | 
             
            require 'json'
         | 
| 3 3 | 
             
            require 'dynamic_config'
         | 
| 4 4 |  | 
| 5 | 
            +
            $retry_codes = [408, 500, 502, 503, 504, 522, 524, 599]
         | 
| 6 | 
            +
             | 
| 5 7 | 
             
            class Network
         | 
| 6 | 
            -
              def initialize(server_secret, api)
         | 
| 8 | 
            +
              def initialize(server_secret, api, backoff_mult = 10)
         | 
| 7 9 | 
             
                super()
         | 
| 8 10 | 
             
                unless api.end_with?('/')
         | 
| 9 11 | 
             
                  api += '/'
         | 
| @@ -11,22 +13,36 @@ class Network | |
| 11 13 | 
             
                @server_secret = server_secret
         | 
| 12 14 | 
             
                @api = api
         | 
| 13 15 | 
             
                @last_sync_time = 0
         | 
| 16 | 
            +
                @backoff_multiplier = backoff_mult
         | 
| 14 17 | 
             
              end
         | 
| 15 18 |  | 
| 16 | 
            -
              def post_helper(endpoint, body)
         | 
| 19 | 
            +
              def post_helper(endpoint, body, retries = 0, backoff = 1)
         | 
| 17 20 | 
             
                http = HTTP.headers(
         | 
| 18 21 | 
             
                  {"STATSIG-API-KEY" => @server_secret,
         | 
| 19 22 | 
             
                   "STATSIG-CLIENT-TIME" => (Time.now.to_f * 1000).to_s,
         | 
| 20 23 | 
             
                   "Content-Type" => "application/json; charset=UTF-8"
         | 
| 21 24 | 
             
                  }).accept(:json)
         | 
| 22 | 
            -
                 | 
| 25 | 
            +
                begin
         | 
| 26 | 
            +
                  res = http.post(@api + endpoint, body: body)
         | 
| 27 | 
            +
                rescue
         | 
| 28 | 
            +
                  ## network error retry
         | 
| 29 | 
            +
                  return nil unless retries > 0
         | 
| 30 | 
            +
                  sleep backoff
         | 
| 31 | 
            +
                  return post_helper(endpoint, body, retries - 1, backoff * @backoff_multiplier)
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
                return res unless !res.status.success?
         | 
| 34 | 
            +
                return nil unless retries > 0 && $retry_codes.include?(res.code)
         | 
| 35 | 
            +
                ## status code retry
         | 
| 36 | 
            +
                sleep backoff
         | 
| 37 | 
            +
                post_helper(endpoint, body, retries - 1, backoff * @backoff_multiplier)
         | 
| 23 38 | 
             
              end
         | 
| 24 39 |  | 
| 25 40 | 
             
              def check_gate(user, gate_name)
         | 
| 26 41 | 
             
                begin
         | 
| 27 | 
            -
                  request_body = JSON.generate({'user' => user&.serialize, 'gateName' => gate_name})
         | 
| 42 | 
            +
                  request_body = JSON.generate({'user' => user&.serialize(false), 'gateName' => gate_name})
         | 
| 28 43 | 
             
                  response = post_helper('check_gate', request_body)
         | 
| 29 | 
            -
                  return JSON.parse(response.body)
         | 
| 44 | 
            +
                  return JSON.parse(response.body) unless response.nil?
         | 
| 45 | 
            +
                  false
         | 
| 30 46 | 
             
                rescue
         | 
| 31 47 | 
             
                  return false
         | 
| 32 48 | 
             
                end
         | 
| @@ -34,9 +50,10 @@ class Network | |
| 34 50 |  | 
| 35 51 | 
             
              def get_config(user, dynamic_config_name)
         | 
| 36 52 | 
             
                begin
         | 
| 37 | 
            -
                  request_body = JSON.generate({'user' => user&.serialize, 'configName' => dynamic_config_name})
         | 
| 53 | 
            +
                  request_body = JSON.generate({'user' => user&.serialize(false), 'configName' => dynamic_config_name})
         | 
| 38 54 | 
             
                  response = post_helper('get_config', request_body)
         | 
| 39 | 
            -
                  return JSON.parse(response.body)
         | 
| 55 | 
            +
                  return JSON.parse(response.body) unless response.nil?
         | 
| 56 | 
            +
                  nil
         | 
| 40 57 | 
             
                rescue
         | 
| 41 58 | 
             
                  return nil
         | 
| 42 59 | 
             
                end
         | 
| @@ -45,6 +62,7 @@ class Network | |
| 45 62 | 
             
              def download_config_specs
         | 
| 46 63 | 
             
                begin
         | 
| 47 64 | 
             
                  response = post_helper('download_config_specs', JSON.generate({'sinceTime' => @last_sync_time}))
         | 
| 65 | 
            +
                  return nil unless !response.nil?
         | 
| 48 66 | 
             
                  json_body = JSON.parse(response.body)
         | 
| 49 67 | 
             
                  @last_sync_time = json_body['time']
         | 
| 50 68 | 
             
                  return json_body
         | 
| @@ -68,9 +86,8 @@ class Network | |
| 68 86 | 
             
              def post_logs(events, statsig_metadata)
         | 
| 69 87 | 
             
                begin
         | 
| 70 88 | 
             
                  json_body = JSON.generate({'events' => events, 'statsigMetadata' => statsig_metadata})
         | 
| 71 | 
            -
                  post_helper('log_event', body: json_body)
         | 
| 89 | 
            +
                  post_helper('log_event', body: json_body, retries: 5)
         | 
| 72 90 | 
             
                rescue
         | 
| 73 | 
            -
                  # TODO: retries
         | 
| 74 91 | 
             
                end
         | 
| 75 92 | 
             
              end
         | 
| 76 93 | 
             
            end
         | 
    
        data/lib/statsig_driver.rb
    CHANGED
    
    | @@ -56,9 +56,11 @@ class StatsigDriver | |
| 56 56 |  | 
| 57 57 | 
             
                if res == $fetch_from_server
         | 
| 58 58 | 
             
                  res = check_gate_fallback(user, gate_name)
         | 
| 59 | 
            +
                  # exposure logged by the server
         | 
| 60 | 
            +
                else
         | 
| 61 | 
            +
                  @logger.log_gate_exposure(user, res.name, res.gate_value, res.rule_id)
         | 
| 59 62 | 
             
                end
         | 
| 60 63 |  | 
| 61 | 
            -
                @logger.log_gate_exposure(user, res.name, res.gate_value, res.rule_id)
         | 
| 62 64 | 
             
                res.gate_value
         | 
| 63 65 | 
             
              end
         | 
| 64 66 |  | 
| @@ -80,11 +82,12 @@ class StatsigDriver | |
| 80 82 |  | 
| 81 83 | 
             
                if res == $fetch_from_server
         | 
| 82 84 | 
             
                  res = get_config_fallback(user, dynamic_config_name)
         | 
| 85 | 
            +
                  # exposure logged by the server
         | 
| 86 | 
            +
                else
         | 
| 87 | 
            +
                  @logger.log_config_exposure(user, res.name, res.rule_id)
         | 
| 83 88 | 
             
                end
         | 
| 84 89 |  | 
| 85 | 
            -
                 | 
| 86 | 
            -
                @logger.log_config_exposure(user, result_config.name, result_config.rule_id)
         | 
| 87 | 
            -
                result_config
         | 
| 90 | 
            +
                DynamicConfig.new(res.name, res.json_value, res.rule_id)
         | 
| 88 91 | 
             
              end
         | 
| 89 92 |  | 
| 90 93 | 
             
              def log_event(user, event_name, value = nil, metadata = nil)
         | 
| @@ -96,7 +99,7 @@ class StatsigDriver | |
| 96 99 | 
             
                user = normalize_user(user)
         | 
| 97 100 |  | 
| 98 101 | 
             
                event = StatsigEvent.new(event_name)
         | 
| 99 | 
            -
                event.user = user | 
| 102 | 
            +
                event.user = user
         | 
| 100 103 | 
             
                event.value = value
         | 
| 101 104 | 
             
                event.metadata = metadata
         | 
| 102 105 | 
             
                event.statsig_metadata = @statsig_metadata
         | 
| @@ -105,7 +108,7 @@ class StatsigDriver | |
| 105 108 |  | 
| 106 109 | 
             
              def shutdown
         | 
| 107 110 | 
             
                @shutdown = true
         | 
| 108 | 
            -
                @logger.flush
         | 
| 111 | 
            +
                @logger.flush(true)
         | 
| 109 112 | 
             
                @polling_thread&.exit
         | 
| 110 113 | 
             
              end
         | 
| 111 114 |  | 
    
        data/lib/statsig_event.rb
    CHANGED
    
    | @@ -1,15 +1,22 @@ | |
| 1 1 | 
             
            class StatsigEvent
         | 
| 2 2 | 
             
              attr_accessor :value
         | 
| 3 | 
            -
              attr_accessor :user
         | 
| 4 3 | 
             
              attr_accessor :metadata
         | 
| 5 4 | 
             
              attr_accessor :statsig_metadata
         | 
| 5 | 
            +
              attr_reader :user
         | 
| 6 | 
            +
             | 
| 6 7 | 
             
              def initialize(event_name)
         | 
| 7 8 | 
             
                @event_name = event_name
         | 
| 8 9 | 
             
                @time = Time.now.to_f * 1000
         | 
| 9 10 | 
             
              end
         | 
| 10 11 |  | 
| 12 | 
            +
              def user=(value)
         | 
| 13 | 
            +
                if value.is_a?(StatsigUser)
         | 
| 14 | 
            +
                  @user = value.serialize(true)
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 11 18 | 
             
              def serialize
         | 
| 12 | 
            -
                 | 
| 19 | 
            +
                {
         | 
| 13 20 | 
             
                  'eventName' => @event_name,
         | 
| 14 21 | 
             
                  'metadata' => @metadata,
         | 
| 15 22 | 
             
                  'value' => @value,
         | 
    
        data/lib/statsig_logger.rb
    CHANGED
    
    | @@ -8,6 +8,10 @@ class StatsigLogger | |
| 8 8 | 
             
                @network = network
         | 
| 9 9 | 
             
                @statsig_metadata = statsig_metadata
         | 
| 10 10 | 
             
                @events = []
         | 
| 11 | 
            +
                @background_flush = Thread.new do
         | 
| 12 | 
            +
                  sleep 60
         | 
| 13 | 
            +
                  flush
         | 
| 14 | 
            +
                end
         | 
| 11 15 | 
             
              end
         | 
| 12 16 |  | 
| 13 17 | 
             
              def log_event(event)
         | 
| @@ -40,13 +44,18 @@ class StatsigLogger | |
| 40 44 | 
             
                log_event(event)
         | 
| 41 45 | 
             
              end
         | 
| 42 46 |  | 
| 43 | 
            -
              def flush
         | 
| 47 | 
            +
              def flush(closing = false)
         | 
| 48 | 
            +
                if closing
         | 
| 49 | 
            +
                  @background_flush.exit
         | 
| 50 | 
            +
                end
         | 
| 44 51 | 
             
                if @events.length == 0
         | 
| 45 52 | 
             
                  return
         | 
| 46 53 | 
             
                end
         | 
| 47 54 | 
             
                flush_events = @events.map { |e| e.serialize() }
         | 
| 48 55 | 
             
                @events = []
         | 
| 49 56 |  | 
| 50 | 
            -
                 | 
| 57 | 
            +
                Thread.new do
         | 
| 58 | 
            +
                  @network.post_logs(flush_events, @statsig_metadata)
         | 
| 59 | 
            +
                end
         | 
| 51 60 | 
             
              end
         | 
| 52 61 | 
             
            end
         | 
    
        data/lib/statsig_user.rb
    CHANGED
    
    | @@ -7,6 +7,7 @@ class StatsigUser | |
| 7 7 | 
             
              attr_accessor :locale
         | 
| 8 8 | 
             
              attr_accessor :app_version
         | 
| 9 9 | 
             
              attr_accessor :statsig_environment
         | 
| 10 | 
            +
              attr_accessor :private_attributes
         | 
| 10 11 |  | 
| 11 12 | 
             
              def custom
         | 
| 12 13 | 
             
                @custom
         | 
| @@ -29,11 +30,12 @@ class StatsigUser | |
| 29 30 | 
             
                  @app_version = user_hash['appVersion'] || user_hash['app_version']
         | 
| 30 31 | 
             
                  @custom = user_hash['custom']
         | 
| 31 32 | 
             
                  @statsig_environment = user_hash['statsigEnvironment']
         | 
| 33 | 
            +
                  @private_attributes = user_hash['privateAttributes']
         | 
| 32 34 | 
             
                end
         | 
| 33 35 | 
             
              end
         | 
| 34 36 |  | 
| 35 | 
            -
              def serialize
         | 
| 36 | 
            -
                {
         | 
| 37 | 
            +
              def serialize(for_logging)
         | 
| 38 | 
            +
                hash = {
         | 
| 37 39 | 
             
                  'userID' => @user_id,
         | 
| 38 40 | 
             
                  'email' => @email,
         | 
| 39 41 | 
             
                  'ip' => @ip,
         | 
| @@ -43,7 +45,12 @@ class StatsigUser | |
| 43 45 | 
             
                  'appVersion' => @app_version,
         | 
| 44 46 | 
             
                  'custom' => @custom,
         | 
| 45 47 | 
             
                  'statsigEnvironment' => @statsig_environment,
         | 
| 48 | 
            +
                  'privateAttributes' => @private_attributes,
         | 
| 46 49 | 
             
                }
         | 
| 50 | 
            +
                if for_logging
         | 
| 51 | 
            +
                  hash.delete('privateAttributes')
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
                hash
         | 
| 47 54 | 
             
              end
         | 
| 48 55 |  | 
| 49 56 | 
             
              def value_lookup
         | 
| @@ -62,6 +69,7 @@ class StatsigUser | |
| 62 69 | 
             
                  'appversion' => @app_version,
         | 
| 63 70 | 
             
                  'app_version' => @app_version,
         | 
| 64 71 | 
             
                  'custom' => @custom,
         | 
| 72 | 
            +
                  'privateAttributes' => @private_attributes,
         | 
| 65 73 | 
             
                }
         | 
| 66 74 | 
             
              end
         | 
| 67 75 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: statsig
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.6.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Statsig, Inc
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2021- | 
| 11 | 
            +
            date: 2021-09-11 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bundler
         | 
| @@ -24,6 +24,48 @@ dependencies: | |
| 24 24 | 
             
                - - "~>"
         | 
| 25 25 | 
             
                  - !ruby/object:Gem::Version
         | 
| 26 26 | 
             
                    version: '2.1'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: webmock
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '3.13'
         | 
| 34 | 
            +
              type: :development
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '3.13'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: minitest
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - "~>"
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '5.14'
         | 
| 48 | 
            +
              type: :development
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - "~>"
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '5.14'
         | 
| 55 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            +
              name: spy
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - "~>"
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: '1.0'
         | 
| 62 | 
            +
              type: :development
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - "~>"
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: '1.0'
         | 
| 27 69 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 70 | 
             
              name: user_agent_parser
         | 
| 29 71 | 
             
              requirement: !ruby/object:Gem::Requirement
         |