Class: LaunchDarkly::LDContext

Inherits:
Object
  • Object
show all
Defined in:
lib/ldclient-rb/context.rb

Overview

LDContext is a collection of attributes that can be referenced in flag evaluations and analytics events.

To create an LDContext of a single kind, such as a user, you may use LDContext.create or LDContext.with_key.

To create an LDContext with multiple kinds, use LDContext.create_multi.

Each factory method will always return an LDContext. However, that LDContext may be invalid. You can check the validity of the resulting context, and the associated errors by calling #valid? and #error

Constant Summary collapse

KIND_DEFAULT =
"user"
KIND_MULTI =
"multi"
ERR_PRIVATE_NON_ARRAY =
'context private attributes must be an array'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key, fully_qualified_key, kind, name = nil, anonymous = nil, attributes = nil, private_attributes = nil, error = nil, contexts = nil) ⇒ LDContext

Returns a new instance of LDContext.

Parameters:

  • key (String, nil)
  • fully_qualified_key (String, nil)
  • kind (String, nil)
  • name (String, nil) (defaults to: nil)
  • anonymous (Boolean, nil) (defaults to: nil)
  • attributes (Hash, nil) (defaults to: nil)
  • private_attributes (Array<String>, nil) (defaults to: nil)
  • error (String, nil) (defaults to: nil)
  • contexts (Array<LDContext>, nil) (defaults to: nil)


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/ldclient-rb/context.rb', line 65

def initialize(key, fully_qualified_key, kind, name = nil, anonymous = nil, attributes = nil, private_attributes = nil, error = nil, contexts = nil)
  @key = key
  @fully_qualified_key = fully_qualified_key
  @kind = kind
  @name = name
  @anonymous = anonymous || false
  @attributes = attributes
  @private_attributes = []
  (private_attributes || []).each do |attribute|
    reference = Reference.create(attribute)
    @private_attributes << reference if reference.error.nil?
  end
  @error = error
  @contexts = contexts
  @is_multi = !contexts.nil?
end

Instance Attribute Details

#errorString? (readonly)

Returns the error associated with this LDContext if invalid

Returns:

  • (String, nil)

    Returns the error associated with this LDContext if invalid



48
49
50
# File 'lib/ldclient-rb/context.rb', line 48

def error
  @error
end

#fully_qualified_keyString? (readonly)

Returns the fully qualified key for this context

Returns:

  • (String, nil)

    Returns the fully qualified key for this context



42
43
44
# File 'lib/ldclient-rb/context.rb', line 42

def fully_qualified_key
  @fully_qualified_key
end

#keyString? (readonly)

Returns the key for this context

Returns:

  • (String, nil)

    Returns the key for this context



39
40
41
# File 'lib/ldclient-rb/context.rb', line 39

def key
  @key
end

#kindString? (readonly)

Returns the kind for this context

Returns:

  • (String, nil)

    Returns the kind for this context



45
46
47
# File 'lib/ldclient-rb/context.rb', line 45

def kind
  @kind
end

#private_attributesArray<Reference> (readonly)

Returns the private attributes associated with this LDContext

Returns:

  • (Array<Reference>)

    Returns the private attributes associated with this LDContext



51
52
53
# File 'lib/ldclient-rb/context.rb', line 51

def private_attributes
  @private_attributes
end

Class Method Details

.create(data) ⇒ LDContext

Deprecated.

The old user format will be removed in 8.0.0. Please use the new context specific format.

Create a single kind context from the provided hash.

The provided hash must match the format as outlined in the SDK documentation.

Parameters:

  • data (Hash)

Returns:



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/ldclient-rb/context.rb', line 325

def self.create(data)
  return create_invalid_context(ERR_NOT_HASH) unless data.is_a?(Hash)

  kind = data[:kind]
  if kind == KIND_MULTI
    contexts = []
    data.each do |key, value|
      next if key == :kind
      contexts << create_single_context(value, key.to_s)
    end

    return create_multi(contexts)
  end

  create_single_context(data, kind)
end

.create_multi(contexts) ⇒ LDContext

Create a multi-kind context from the array of LDContexts provided.

A multi-kind context is comprised of two or more single kind contexts. You cannot include a multi-kind context instead another multi-kind context.

Additionally, the kind of each single-kind context must be unique. For instance, you cannot create a multi-kind context that includes two user kind contexts.

If you attempt to create a multi-kind context from one single-kind context, this method will return the single-kind context instead of a new multi-kind context wrapping that one single-kind.

Parameters:

Returns:



360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
# File 'lib/ldclient-rb/context.rb', line 360

def self.create_multi(contexts)
  return create_invalid_context(ERR_KIND_MULTI_NON_CONTEXT_ARRAY) unless contexts.is_a?(Array)
  return create_invalid_context(ERR_KIND_MULTI_WITH_NO_KINDS) if contexts.empty?

  kinds = Set.new
  contexts.each do |context|
    if !context.is_a?(LDContext)
      return create_invalid_context(ERR_KIND_MULTI_NON_CONTEXT_ARRAY)
    elsif !context.valid?
      return create_invalid_context(ERR_KIND_MULTI_NON_CONTEXT_ARRAY)
    elsif context.multi_kind?
      return create_invalid_context(ERR_KIND_MULTI_CANNOT_CONTAIN_MULTI)
    elsif kinds.include? context.kind
      return create_invalid_context(ERR_KIND_MULTI_DUPLICATES)
    end

    kinds.add(context.kind)
  end

  return contexts[0] if contexts.length == 1

  full_key = contexts.sort_by(&:kind)
                     .map { |c| LaunchDarkly::Impl::Context::canonicalize_key_for_kind(c.kind, c.key) }
                     .join(":")

  new(nil, full_key, "multi", nil, false, nil, nil, nil, contexts)
end

.with_key(key, kind = KIND_DEFAULT) ⇒ Object

Convenience method to create a simple single kind context providing only a key and kind type.

Parameters:

  • key (String)
  • kind (String) (defaults to: KIND_DEFAULT)


309
310
311
# File 'lib/ldclient-rb/context.rb', line 309

def self.with_key(key, kind = KIND_DEFAULT)
  create({key: key, kind: kind})
end

Instance Method Details

#get_custom_attribute_namesArray<Symbol>

Return an array of top level attribute keys (excluding built-in attributes)

Returns:

  • (Array<Symbol>)


126
127
128
129
130
# File 'lib/ldclient-rb/context.rb', line 126

def get_custom_attribute_names
  return [] if @attributes.nil?

  @attributes.keys
end

#get_value(attribute) ⇒ any

get_value looks up the value of any attribute of the Context by name. This includes only attributes that are addressable in evaluations– not metadata such as private attributes.

For a single-kind context, the attribute name can be any custom attribute. It can also be one of the built-in ones like “kind”, “key”, or “name”.

For a multi-kind context, the only supported attribute name is “kind”. Use #individual_context to inspect a Context for a particular kind and then get its attributes.

This method does not support complex expressions for getting individual values out of JSON objects or arrays, such as “/address/street”. Use #get_value_for_reference for that purpose.

If the value is found, the return value is the attribute value; otherwise, it is nil.

Parameters:

  • attribute (String, Symbol)

Returns:

  • (any)


154
155
156
157
# File 'lib/ldclient-rb/context.rb', line 154

def get_value(attribute)
  reference = Reference.create_literal(attribute)
  get_value_for_reference(reference)
end

#get_value_for_reference(reference) ⇒ any

get_value_for_reference looks up the value of any attribute of the Context, or a value contained within an attribute, based on a Reference instance. This includes only attributes that are addressable in evaluations– not metadata such as private attributes.

This implements the same behavior that the SDK uses to resolve attribute references during a flag evaluation. In a single-kind context, the Reference can represent a simple attribute name– either a built-in one like “name” or “key”, or a custom attribute – or, it can be a slash-delimited path using a JSON-Pointer-like syntax. See Reference for more details.

For a multi-kind context, the only supported attribute name is “kind”. Use #individual_context to inspect a Context for a particular kind and then get its attributes.

If the value is found, the return value is the attribute value; otherwise, it is nil.

Parameters:

Returns:

  • (any)


182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/ldclient-rb/context.rb', line 182

def get_value_for_reference(reference)
  return nil unless valid?
  return nil unless reference.is_a?(Reference)
  return nil unless reference.error.nil?

  first_component = reference.component(0)
  return nil if first_component.nil?

  if multi_kind?
    if reference.depth == 1 && first_component == :kind
      return kind
    end

    # Multi-kind contexts have no other addressable attributes
    return nil
  end

  value = get_top_level_addressable_attribute_single_kind(first_component)
  return nil if value.nil?

  (1...reference.depth).each do |i|
    name = reference.component(i)

    return nil unless value.is_a?(Hash)
    return nil unless value.has_key?(name)

    value = value[name]
  end

  value
end

#individual_context(kind) ⇒ LDContext?

Returns the single-kind LDContext corresponding to one of the kinds in this context.

The ‘kind` parameter can be either a number representing a zero-based index, or a string representing a context kind.

If this method is called on a single-kind LDContext, then the only allowable value for ‘kind` is either zero or the same value as #kind, and the return value on success is the same LDContext.

If the method is called on a multi-context, and ‘kind` is a number, it must be a non-negative index that is less than the number of kinds (that is, less than the return value of #individual_context_count, and the return value on success is one of the individual LDContexts within. Or, if `kind` is a string, it must match the context kind of one of the individual contexts.

If there is no context corresponding to ‘kind`, the method returns nil.

Parameters:

  • kind (Integer, String)

    the index or string value of a context kind

Returns:

  • (LDContext, nil)

    the context corresponding to that index or kind, or null if none.



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/ldclient-rb/context.rb', line 253

def individual_context(kind)
  return nil unless valid?

  if kind.is_a?(Integer)
    unless multi_kind?
      return kind == 0 ? self : nil
    end

    return kind >= 0 && kind < @contexts.count ? @contexts[kind] : nil
  end

  return nil unless kind.is_a?(String)

  unless multi_kind?
    return self.kind == kind ? self : nil
  end

  @contexts.each do |context|
    return context if context.kind == kind
  end

  nil
end

#individual_context_countInteger

Returns the number of context kinds in this context.

For a valid individual context, this returns 1. For a multi-context, it returns the number of context kinds. For an invalid context, it returns zero.

Returns:

  • (Integer)

    the number of context kinds



223
224
225
226
227
# File 'lib/ldclient-rb/context.rb', line 223

def individual_context_count
  return 0 unless valid?
  return 1 if @contexts.nil?
  @contexts.count
end

#keysHash<Symbol, String>

Returns a hash mapping each context’s kind to its key.

Returns:

  • (Hash<Symbol, String>)


102
103
104
105
106
107
# File 'lib/ldclient-rb/context.rb', line 102

def keys
  return {} unless valid?
  return Hash[kind, key] unless multi_kind?

  @contexts.map { |c| [c.kind, c.key] }.to_h
end

#kindsArray<String>

Returns an array of context kinds.

Returns:

  • (Array<String>)


114
115
116
117
118
119
# File 'lib/ldclient-rb/context.rb', line 114

def kinds
  return [] unless valid?
  return [kind] unless multi_kind?

  @contexts.map { |c| c.kind }
end

#multi_kind?Boolean

Returns Is this LDContext a multi-kind context?.

Returns:

  • (Boolean)

    Is this LDContext a multi-kind context?



86
87
88
# File 'lib/ldclient-rb/context.rb', line 86

def multi_kind?
  @is_multi
end

#valid?Boolean

Returns Determine if this LDContext is considered valid.

Returns:

  • (Boolean)

    Determine if this LDContext is considered valid



93
94
95
# File 'lib/ldclient-rb/context.rb', line 93

def valid?
  @error.nil?
end