Class: Philiprehberger::DotAccess::Wrapper

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/philiprehberger/dot_access.rb

Overview

Dot-notation wrapper for a hash

Instance Method Summary collapse

Constructor Details

#initialize(hash) ⇒ Wrapper

Returns a new instance of Wrapper.

Parameters:

  • hash (Hash)

    the hash to wrap



100
101
102
103
104
105
# File 'lib/philiprehberger/dot_access.rb', line 100

def initialize(hash)
  @data = hash.each_with_object({}) do |(key, value), memo|
    memo[key.is_a?(String) ? key.to_sym : key] = value
  end
  freeze
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ Object (private)



320
321
322
323
324
325
326
327
328
# File 'lib/philiprehberger/dot_access.rb', line 320

def method_missing(name, *args)
  if @data.key?(name)
    wrap_value(@data[name])
  elsif name.to_s.end_with?('=') || !args.empty?
    super
  else
    NullAccess.new
  end
end

Instance Method Details

#==(other) ⇒ Boolean

Returns:

  • (Boolean)


312
313
314
315
316
# File 'lib/philiprehberger/dot_access.rb', line 312

def ==(other)
  return to_h == other.to_h if other.is_a?(Wrapper)

  false
end

#compactWrapper

Return a new Wrapper with all nil values removed at every depth

Returns:

  • (Wrapper)

    a new wrapper with nils removed from hashes and arrays



220
221
222
# File 'lib/philiprehberger/dot_access.rb', line 220

def compact
  Philiprehberger::DotAccess.wrap(deep_compact(to_h))
end

#delete(path) ⇒ Wrapper

Remove a key at a dot-path, returning a new Wrapper

Parameters:

  • path (String)

    dot-separated key path

Returns:

  • (Wrapper)

    a new wrapper without the specified path



204
205
206
207
208
# File 'lib/philiprehberger/dot_access.rb', line 204

def delete(path)
  keys = path.to_s.split('.')
  new_data = deep_delete(to_h, keys)
  Wrapper.new(new_data)
end

#dig(key) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Dig into nested keys

Parameters:

  • key (Symbol)

    the key to look up

Returns:

  • (Object)

    the value



301
302
303
304
# File 'lib/philiprehberger/dot_access.rb', line 301

def dig(key)
  value = @data[key]
  wrap_value(value)
end

#each {|Symbol, Object| ... } ⇒ Enumerator Also known as: each_pair

Iterate over top-level key-value pairs

Yields:

  • (Symbol, Object)

    each key and its wrapped value

Returns:

  • (Enumerator)

    if no block given



238
239
240
241
242
243
244
# File 'lib/philiprehberger/dot_access.rb', line 238

def each(&)
  return enum_for(:each) unless block_given?

  @data.each do |key, value|
    yield key, wrap_value(value)
  end
end

#empty?Boolean

Check if the wrapped hash has no keys

Returns:

  • (Boolean)


251
252
253
# File 'lib/philiprehberger/dot_access.rb', line 251

def empty?
  @data.empty?
end

#exists?(path) ⇒ Boolean

Check if a dot-separated path exists in the wrapped hash

Parameters:

  • path (String)

    dot-separated key path

Returns:

  • (Boolean)

    true if the path exists (even if value is nil)



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/philiprehberger/dot_access.rb', line 140

def exists?(path)
  keys = path.to_s.split('.')
  current = @data
  keys.each do |key|
    case current
    when Hash
      return false unless current.key?(key.to_sym)

      current = current[key.to_sym]
    when Wrapper
      return false unless current.key?(key.to_sym)

      current = current[key.to_sym]
    else
      return false
    end
  end
  true
end

#fetch!(path) ⇒ Object

Fetch a value at a dot-path, raising if missing

Parameters:

  • path (String)

    dot-separated key path

Returns:

  • (Object)

    the value at the path

Raises:

  • (KeyError)

    if the path does not exist



173
174
175
176
177
# File 'lib/philiprehberger/dot_access.rb', line 173

def fetch!(path)
  raise KeyError, "path not found: #{path.inspect}" unless exists?(path)

  get(path)
end

#flattenHash

Flatten nested structure into a hash with dot-path keys

Returns:

  • (Hash)

    flat hash where keys are dot-path strings



213
214
215
# File 'lib/philiprehberger/dot_access.rb', line 213

def flatten
  flatten_hash(@data, '')
end

#get(path, default: nil) ⇒ Object

Access a value by dot-path string

Parameters:

  • path (String)

    dot-separated key path

  • default (Object) (defaults to: nil)

    value to return if path is not found

Returns:

  • (Object)

    the value at the path, or default



112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/philiprehberger/dot_access.rb', line 112

def get(path, default: nil)
  keys = path.to_s.split('.')
  result = keys.reduce(@data) do |current, key|
    case current
    when Hash then current[key.to_sym]
    when Wrapper then current[key.to_sym]
    else return default
    end
  end

  result.nil? ? default : result
end

#inspectString

Returns:

  • (String)


307
308
309
# File 'lib/philiprehberger/dot_access.rb', line 307

def inspect
  "#<DotAccess::Wrapper #{to_h.inspect}>"
end

#key?(key) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if a key exists in the underlying data

Parameters:

  • key (Symbol)

    the key to check

Returns:

  • (Boolean)


283
284
285
# File 'lib/philiprehberger/dot_access.rb', line 283

def key?(key)
  @data.key?(key)
end

#keys(depth: nil) ⇒ Array<String>

Return all dot-path keys as strings

Parameters:

  • depth (Integer, nil) (defaults to: nil)

    maximum traversal depth (nil for unlimited)

Returns:

  • (Array<String>)

    array of dot-path strings



164
165
166
# File 'lib/philiprehberger/dot_access.rb', line 164

def keys(depth: nil)
  collect_keys(@data, '', depth, 1)
end

#merge(other) ⇒ Wrapper

Deep merge with another Wrapper or Hash

Parameters:

  • other (Wrapper, Hash)

    the other structure to merge

Returns:

  • (Wrapper)

    a new wrapper with merged values



228
229
230
231
232
# File 'lib/philiprehberger/dot_access.rb', line 228

def merge(other)
  other_hash = other.is_a?(Wrapper) ? other.to_h : symbolize_keys(other)
  merged = deep_merge(to_h, other_hash)
  Wrapper.new(merged)
end

#set(path, value) ⇒ Wrapper

Set a value at a dot-path, returning a new Wrapper

Parameters:

  • path (String)

    dot-separated key path

  • value (Object)

    the value to set

Returns:

  • (Wrapper)

    a new wrapper with the updated value



130
131
132
133
134
# File 'lib/philiprehberger/dot_access.rb', line 130

def set(path, value)
  keys = path.to_s.split('.')
  new_data = deep_set(to_h, keys, value)
  Wrapper.new(new_data)
end

#sizeInteger Also known as: count

Return the number of top-level keys

Returns:

  • (Integer)


258
259
260
# File 'lib/philiprehberger/dot_access.rb', line 258

def size
  @data.size
end

#slice(*paths) ⇒ Wrapper

Return a new Wrapper containing only the specified dot-paths

Parameters:

  • paths (Array<String>)

    dot-separated key paths to retain

Returns:

  • (Wrapper)

    a new wrapper with only the given paths



183
184
185
186
187
188
189
190
# File 'lib/philiprehberger/dot_access.rb', line 183

def slice(*paths)
  new_data = paths.reduce({}) do |acc, path|
    next acc unless exists?(path)

    deep_set(acc, path.to_s.split('.'), get(path))
  end
  Wrapper.new(new_data)
end

#to_hHash

Return the underlying hash

Returns:

  • (Hash)

    the original hash with symbol keys



290
291
292
293
294
# File 'lib/philiprehberger/dot_access.rb', line 290

def to_h
  @data.each_with_object({}) do |(key, value), memo|
    memo[key] = value.is_a?(Wrapper) ? value.to_h : value
  end
end

#to_json(*args) ⇒ String

Serialize the wrapped hash to a JSON string

Returns:

  • (String)

    JSON representation



267
268
269
# File 'lib/philiprehberger/dot_access.rb', line 267

def to_json(*args)
  to_h.to_json(*args)
end

#to_yaml(*args) ⇒ String

Serialize the wrapped hash to a YAML string

Returns:

  • (String)

    YAML representation



274
275
276
# File 'lib/philiprehberger/dot_access.rb', line 274

def to_yaml(*args)
  to_h.to_yaml(*args)
end

#values_at(*paths) ⇒ Array<Object>

Return values at the given dot-paths as an array

Parameters:

  • paths (Array<String>)

    dot-separated key paths

Returns:

  • (Array<Object>)

    values in the order of the given paths



196
197
198
# File 'lib/philiprehberger/dot_access.rb', line 196

def values_at(*paths)
  paths.map { |path| get(path) }
end