Class: LaunchDarkly::Reference

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

Overview

Reference is an attribute name or path expression identifying a value within a Context.

This type is mainly intended to be used internally by LaunchDarkly SDK and service code, where efficiency is a major concern so it’s desirable to do any parsing or preprocessing just once. Applications are unlikely to need to use the Reference type directly.

It can be used to retrieve a value with LDContext.get_value_for_reference() or to identify an attribute or nested value that should be considered private.

Parsing and validation are done at the time that the Reference is constructed. If a Reference instance was created from an invalid string, it is considered invalid and its #error attribute will return a non-nil error.

## Syntax

The string representation of an attribute reference in LaunchDarkly JSON data uses the following syntax:

If the first character is not a slash, the string is interpreted literally as an attribute name. An attribute name can contain any characters, but must not be empty.

If the first character is a slash, the string is interpreted as a slash-delimited path where the first path component is an attribute name, and each subsequent path component is the name of a property in a JSON object. Any instances of the characters “/” or “~” in a path component are escaped as “~1” or “~0” respectively. This syntax deliberately resembles JSON Pointer, but no JSON Pointer behaviors other than those mentioned here are supported.

## Examples

Suppose there is a context whose JSON implementation looks like this:

{

"kind": "user",
"key": "value1",
"address": {
  "street": {
    "line1": "value2",
    "line2": "value3"
  },
  "city": "value4"
},
"good/bad": "value5"

}

The attribute references “key” and “/key” would both point to “value1”.

The attribute reference “/address/street/line1” would point to “value2”.

The attribute references “good/bad” and “/good~1bad” would both point to “value5”.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(raw_path, components = [], error = nil) ⇒ Reference

Returns a new instance of Reference.



104
105
106
107
108
109
# File 'lib/ldclient-rb/reference.rb', line 104

def initialize(raw_path, components = [], error = nil)
  @raw_path = raw_path
  # @type [Array<Symbol>]
  @components = components
  @error = error
end

Instance Attribute Details

#errorString? (readonly)

Returns nil for a valid Reference, or a non-nil error value for an invalid Reference.

A Reference is invalid if the input string is empty, or starts with a slash but is not a valid slash-delimited path, or starts with a slash and contains an invalid escape sequence.

Otherwise, the Reference is valid, but that does not guarantee that such an attribute exists in any given Context. For instance, Reference.create(“name”) is a valid Reference, but a specific Context might or might not have a name.

See comments on the Reference type for more details of the attribute reference syntax.

Returns:

  • (String, nil)


89
90
91
# File 'lib/ldclient-rb/reference.rb', line 89

def error
  @error
end

#raw_pathString? (readonly)

Returns the attribute reference as a string, in the same format provided to #create.

If the Reference was created with #create, this value is identical to the original string. If it was created with #create_literal, the value may be different due to unescaping (for instance, an attribute whose name is “/a” would be represented as “~1a”).

Returns:

  • (String, nil)


102
103
104
# File 'lib/ldclient-rb/reference.rb', line 102

def raw_path
  @raw_path
end

Class Method Details

.create(value) ⇒ Reference

Creates a Reference from a string. For the supported syntax and examples, see comments on the Reference type.

This constructor always returns a Reference that preserves the original string, even if validation fails, so that accessing #raw_path (or serializing the Reference to JSON) will produce the original string. If validation fails, #error will return a non-nil error and any SDK method that takes this Reference as a parameter will consider it invalid.

Parameters:

  • value (String, Symbol)

Returns:



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/ldclient-rb/reference.rb', line 127

def self.create(value)
  unless value.is_a?(String) || value.is_a?(Symbol)
    return new(value, [], ERR_EMPTY)
  end

  value = value.to_s if value.is_a?(Symbol)

  return new(value, [], ERR_EMPTY) if value.empty? || value == "/"

  unless value.start_with? "/"
    return new(value, [value.to_sym])
  end

  if value.end_with? "/"
    return new(value, [], ERR_DOUBLE_TRAILING_SLASH)
  end

  components = []
  value[1..].split("/").each do |component|
    if component.empty?
      return new(value, [], ERR_DOUBLE_TRAILING_SLASH)
    end

    path, error = unescape_path(component)

    if error
      return new(value, [], error)
    end

    components << path.to_sym
  end

  new(value, components)
end

.create_literal(value) ⇒ Reference

create_literal is similar to #create except that it always interprets the string as a literal attribute name, never as a slash-delimited path expression. There is no escaping or unescaping, even if the name contains literal ‘/’ or ‘~’ characters. Since an attribute name can contain any characters, this method always returns a valid Reference unless the name is empty.

For example: Reference.create_literal(“name”) is exactly equivalent to Reference.create(“name”). Reference.create_literal(“a/b”) is exactly equivalent to Reference.create(“a/b”) (since the syntax used by #create treats the whole string as a literal as long as it does not start with a slash), or to Reference.create(“/a~1b”).

Parameters:

  • value (String, Symbol)

Returns:



179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/ldclient-rb/reference.rb', line 179

def self.create_literal(value)
  unless value.is_a?(String) || value.is_a?(Symbol)
    return new(value, [], ERR_EMPTY)
  end

  value = value.to_s if value.is_a?(Symbol)

  return new(value, [], ERR_EMPTY) if value.empty?
  return new(value, [value.to_sym]) if value[0] != '/'

  escaped = "/" + value.gsub('~', '~0').gsub('/', '~1')
  new(escaped, [value.to_sym])
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



232
233
234
# File 'lib/ldclient-rb/reference.rb', line 232

def ==(other)
  self.error == other.error && self.components == other.components
end

#component(index) ⇒ Symbol?

Retrieves a single path component from the attribute reference.

For a simple attribute reference such as “name” with no leading slash, if index is zero, #component returns the attribute name as a symbol.

For an attribute reference with a leading slash, if index is non-negative and less than #depth, Component returns the path component as a symbol.

If index is out of range, it returns nil.

Reference.create(“a”).component(0) # returns “a” Reference.create(“/a/b”).component(1) # returns “b”

Parameters:

  • index (Integer)

Returns:

  • (Symbol, nil)


226
227
228
229
230
# File 'lib/ldclient-rb/reference.rb', line 226

def component(index)
  return nil if index < 0 || index >= depth

  @components[index]
end

#depthInteger

Returns the number of path components in the Reference.

For a simple attribute reference such as “name” with no leading slash, this returns 1.

For an attribute reference with a leading slash, it is the number of slash-delimited path components after the initial slash. For instance, NewRef(“/a/b”).Depth() returns 2.

Returns:

  • (Integer)


205
206
207
# File 'lib/ldclient-rb/reference.rb', line 205

def depth
  @components.size
end

#hashObject



237
238
239
# File 'lib/ldclient-rb/reference.rb', line 237

def hash
  ([error] + components).hash
end

#to_json(*args) ⇒ String

Convert the Reference to a JSON string.

Parameters:

  • args (Array)

Returns:

  • (String)


247
248
249
# File 'lib/ldclient-rb/reference.rb', line 247

def to_json(*args)
  JSON.generate(@raw_path, *args)
end