Class: AttributeStruct

Inherits:
BasicObject
Defined in:
lib/attribute_struct/version.rb,
lib/attribute_struct/attribute_hash.rb,
lib/attribute_struct/attribute_struct.rb

Defined Under Namespace

Classes: AttributeHash, Version

Constant Summary collapse

VERSION =

Current library version

Version.new('0.2.2')

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(init_hash = nil) { ... } ⇒ AttributeStruct

Create new instance

Parameters:

  • init_hash (Hash) (defaults to: nil)

    hash to initialize struct

Yields:

  • block to execute within struct context



75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/attribute_struct/attribute_struct.rb', line 75

def initialize(init_hash=nil, &block)
  _klass.load_the_hash
  @_camel_keys = _klass.camel_keys
  @_arg_state = __hashish.new
  @table = __hashish.new
  if(init_hash)
    _load(init_hash)
  end
  if(block)
    self.instance_exec(&block)
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(sym, *args) { ... } ⇒ Object

Note:

Dragons and unicorns all over in here

Provides struct DSL behavior

Parameters:

  • sym (Symbol, String)

    method name

  • args (Object)

    argument list

Yields:

  • provided block

Returns:

  • (Object)

    existing value or newly set value



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/attribute_struct/attribute_struct.rb', line 159

def method_missing(sym, *args, &block)
  if((s = sym.to_s).end_with?('='))
    s.slice!(-1, s.length)
    sym = s
  end
  sym = _process_key(sym)
  if(!args.empty? || block)
    if(args.empty? && block)
      base = @table.fetch(sym, :__unset__)
      if(_state(:value_collapse) && !base.is_a?(self.class!))
        orig = base
        base = _klass_new
      else
        unless(base.is_a?(self.class!))
          base = _klass_new
        end
      end
      if(block.arity == 0)
        base.instance_exec(&block)
      else
        base.instance_exec(base, &block)
      end
      if(orig.is_a?(::NilClass))
        @table[sym] = base
      else
        if(orig == :__unset__)
          @table[sym] = base
        else
          orig = [orig] unless orig.is_a?(::Array)
          orig << base
          @table[sym] = orig
        end
      end
    elsif(!args.empty? && block)
      base = @table[sym]
      leaf = base
      key = sym
      args.each do |arg|
        leaf = base[arg]
        key = arg
        unless(leaf.is_a?(_klass))
          leaf = _klass_new
          base._set(arg, leaf)
          base = leaf
        end
      end
      if(!leaf.nil? && _state(:value_collapse))
        orig = leaf
        leaf = orig.parent._klass_new
      end
      if(block.arity == 0)
        leaf.instance_exec(&block)
      else
        leaf.instance_exec(leaf, &block)
      end
      if(orig)
        orig = [orig] unless orig.is_a?(::Array)
        orig << leaf
      else
        orig = leaf
      end
      @table[sym] = orig
    else
      if(args.size > 1)
        @table[sym] = _klass_new unless @table[sym].is_a?(_klass)
        endpoint = args.inject(@table[sym]) do |memo, k|
          unless(memo[k].is_a?(_klass))
            memo._set(k, _klass_new)
          end
          memo[k]
        end
        return endpoint # custom break out
      else
        if(_state(:value_collapse) && !(leaf = @table[sym]).nil?)
          leaf = [leaf] unless leaf.is_a?(::Array)
          leaf << args.first
          @table[sym] = leaf
        else
          @table[sym] = args.first
        end
      end
    end
  end
  @table[sym] = _klass_new if @table[sym].nil? && !@table[sym].is_a?(_klass)
  @table[sym]
end

Class Attribute Details

.camel_keysTruthy, Falsey

Returns global flag for camel keys.

Returns:

  • (Truthy, Falsey)

    global flag for camel keys



8
9
10
# File 'lib/attribute_struct/attribute_struct.rb', line 8

def camel_keys
  @camel_keys
end

.force_chefTruthy, Falsey

Returns force chef tooling (Mash).

Returns:

  • (Truthy, Falsey)

    force chef tooling (Mash)



10
11
12
# File 'lib/attribute_struct/attribute_struct.rb', line 10

def force_chef
  @force_chef
end

Instance Attribute Details

#_arg_stateAtributeStruct::AttributeHash, Mash (readonly)

Returns holding space for state.

Returns:

  • (AtributeStruct::AttributeHash, Mash)

    holding space for state



69
70
71
# File 'lib/attribute_struct/attribute_struct.rb', line 69

def _arg_state
  @_arg_state
end

#_camel_keysTruthy, Falsey

Returns current camelizing setting.

Returns:

  • (Truthy, Falsey)

    current camelizing setting



67
68
69
# File 'lib/attribute_struct/attribute_struct.rb', line 67

def _camel_keys
  @_camel_keys
end

Class Method Details

.build(&block) ⇒ Object

Create AttributeStruct instance and dump the resulting hash



50
51
52
53
# File 'lib/attribute_struct/attribute_struct.rb', line 50

def build(&block)
  raise ArgumentError.new 'Block required for build!' unless block
  new(&block)._dump
end

.hashishAttributeStruct::AttributeHash, Mash



44
45
46
47
# File 'lib/attribute_struct/attribute_struct.rb', line 44

def hashish
  load_the_hash
  @hash_loaded == :chef ? ::Mash : ::AttributeStruct::AttributeHash
end

.irb_compat!TrueClass

Note:

this will add methods required for working within IRB

Enable IRB compatibility mode

Returns:

  • (TrueClass)


59
60
61
62
# File 'lib/attribute_struct/attribute_struct.rb', line 59

def irb_compat!
  self.send(:include, IrbCompat)
  true
end

.load_the_camelsObject

Loads helpers for camel casing



22
23
24
25
26
27
# File 'lib/attribute_struct/attribute_struct.rb', line 22

def load_the_camels
  unless(@camels_loaded)
    require 'attribute_struct/monkey_camels'
    @camels_loaded = true
  end
end

.load_the_hashObject

Determine what hash library to load based on availability



30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/attribute_struct/attribute_struct.rb', line 30

def load_the_hash
  unless(@hash_loaded)
    if(defined?(Chef) || force_chef)
      require 'chef/mash'
      require 'chef/mixin/deep_merge'
      @hash_loaded = :chef
    else
      require 'attribute_struct/attribute_hash'
      @hash_loaded = :attribute_hash
    end
  end
end

Instance Method Details

#[](key) ⇒ Object

Direct data access

Parameters:

  • key (String, Symbol)

Returns:

  • (Object)


132
133
134
# File 'lib/attribute_struct/attribute_struct.rb', line 132

def [](key)
  _data[_process_key(key)]
end

#__hashishClass

Returns hashish type available.

Returns:

  • (Class)

    hashish type available



355
356
357
# File 'lib/attribute_struct/attribute_struct.rb', line 355

def __hashish
  defined?(::Chef) ? ::Mash : ::AttributeStruct::AttributeHash
end

#_array(*args) ⇒ Array Also known as: array!

Create an Array and evaluate discovered AttributeStructs

Parameters:

  • args (Object)

    array contents

Returns:

  • (Array)


469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
# File 'lib/attribute_struct/attribute_struct.rb', line 469

def _array(*args)
  args.map do |maybe_block|
    if(maybe_block.is_a?(::Proc))
      klass = _klass_new
      if(maybe_block.arity > 0)
        klass.instance_exec(klass, &maybe_block)
      else
        klass.instance_exec(&maybe_block)
      end
      klass
    else
      maybe_block
    end
  end
end

#_build { ... } ⇒ Object

Execute block within current context

Yields:

  • block to execute

Returns:

  • (Object)


92
93
94
# File 'lib/attribute_struct/attribute_struct.rb', line 92

def _build(&block)
  self.instance_exec(&block)
end

#_camel_keys_actionSymbol, NilClass

Returns :auto_disable or :auto_enable.

Returns:

  • (Symbol, NilClass)

    :auto_disable or :auto_enable



446
447
448
# File 'lib/attribute_struct/attribute_struct.rb', line 446

def _camel_keys_action
  @_camel_keys_set
end

#_camel_keys_set(v) ⇒ Symbol

Set custom rule for processed keys at this context level

Parameters:

  • v (Symbol)

    :auto_disable or :auto_enable

Returns:

  • (Symbol)


441
442
443
# File 'lib/attribute_struct/attribute_struct.rb', line 441

def _camel_keys_set(v)
  @_camel_keys_set = v
end

#_dataAttributeStruct::AttributeHash, Mash

Returns underlying struct data.

Returns:



266
267
268
# File 'lib/attribute_struct/attribute_struct.rb', line 266

def _data
  @table
end

#_deep_copy(thing = nil) ⇒ Object

Create a “deep” copy

Parameters:

  • thing (Object) (defaults to: nil)

    struct to copy. defaults to self

Returns:

  • (Object)

    new instance



376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/attribute_struct/attribute_struct.rb', line 376

def _deep_copy(thing=nil)
  thing ||= _dump
  if(thing.is_a?(::Enumerable))
    val = thing.map{|v| v.is_a?(::Enumerable) ? _deep_copy(v) : _do_dup(v) }
  else
    val = _do_dup(thing)
  end
  if(thing.is_a?(::Hash))
    val = __hashish[*val.flatten(1)]
  end
  val
end

#_delete(key) ⇒ Object Also known as: delete!

Delete entry from struct

Parameters:

  • key (String, Symbol)

Returns:

  • (Object)

    value of entry



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

def _delete(key)
  _data.delete(_process_key(key))
end

#_do_dup(v) ⇒ Object

Note:

if Symbol provided, String is returned

Provide dup of instance

Parameters:

  • v (Object)

Returns:

  • (Object)

    duped instance



364
365
366
367
368
369
370
# File 'lib/attribute_struct/attribute_struct.rb', line 364

def _do_dup(v)
  begin
    v.dup
  rescue
    v.is_a?(::Symbol) ? v.to_s : v
  end
end

#_dumpAttributeStruct::AttributeHash, Mash Also known as: dump!

Returns dump struct to hashish.

Returns:



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/attribute_struct/attribute_struct.rb', line 280

def _dump
  processed = @table.map do |key, value|
    if(value.is_a?(::Enumerable))
      flat = value.map do |v|
        v.is_a?(_klass) ? v._dump : v
      end
      val = value.is_a?(::Hash) ? __hashish[*flat.flatten(1)] : flat
    elsif(value.is_a?(_klass))
      val = value._dump
    else
      val = value
    end
    [key, val]
  end
  __hashish[*processed.flatten(1)]
end

#_keysArray<String,Symbol>

Returns keys within struct.

Returns:

  • (Array<String,Symbol>)

    keys within struct



261
262
263
# File 'lib/attribute_struct/attribute_struct.rb', line 261

def _keys
  _data.keys
end

#_klassClass Also known as: klass!, class!

Returns this class.

Returns:

  • (Class)

    this class



419
420
421
# File 'lib/attribute_struct/attribute_struct.rb', line 419

def _klass
  ::AttributeStruct
end

#_klass_newAttributeStruct

Note:

will set self as parent and propogate camelizing status

Returns new struct instance.

Returns:



427
428
429
430
431
432
433
434
435
# File 'lib/attribute_struct/attribute_struct.rb', line 427

def _klass_new
  n = _klass.new
  unless(_camel_keys_action == :auto_discovery)
    n._camel_keys_set(_camel_keys_action)
  end
  n._camel_keys = _camel_keys
  n._parent(self)
  n
end

#_load(hashish) ⇒ self

Clear current struct data and replace

Parameters:

  • hashish (Hash)

    hashish type instance

Returns:

  • (self)


302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/attribute_struct/attribute_struct.rb', line 302

def _load(hashish)
  @table.clear
  if(_root._camel_keys_action == :auto_discovery)
    starts = hashish.keys.map{|k|k[0,1]}
    unless(starts.detect{|k| k =~ /[A-Z]/})
      _camel_keys_set(:auto_disable)
    else
      _camel_keys_set(:auto_enable) unless _parent.nil?
    end
  end
  hashish.each do |key, value|
    key = key.dup
    if(value.is_a?(::Enumerable))
      flat = value.map do |v|
        v.is_a?(::Hash) ? _klass.new(v) : v
      end
      value = value.is_a?(::Hash) ? __hashish[*flat.flatten(1)] : flat
    end
    if(value.is_a?(::Hash))
      self._set(key)._load(value)
    else
      self._set(key, value)
    end
  end
  self
end

#_merge(overlay) ⇒ AttributeStruct

Perform deep merge

Parameters:

Returns:



333
334
335
336
337
338
339
340
341
342
# File 'lib/attribute_struct/attribute_struct.rb', line 333

def _merge(overlay)
  source = _deep_copy
  dest = overlay._deep_copy
  if(defined?(::Chef))
    result = ::Chef::Mixin::DeepMerge.merge(source, dest)
  else
    result = source.deep_merge(dest)
  end
  _klass.new(result)
end

#_merge!(overlay) ⇒ self

Perform deep merge in place

Parameters:

Returns:

  • (self)


348
349
350
351
352
# File 'lib/attribute_struct/attribute_struct.rb', line 348

def _merge!(overlay)
  result = _merge(overlay)._dump
  _load(result)
  self
end

#_parent(obj = nil) ⇒ AttributeStruct, NilClass

Returns parent of this struct.

Returns:



451
452
453
454
# File 'lib/attribute_struct/attribute_struct.rb', line 451

def _parent(obj=nil)
  @_parent = obj if obj
  @_parent
end

#_process_key(key, *args) ⇒ String, Symbol Also known as: process_key!

Provide expected key format based on context

Parameters:

  • key (String, Symbol)
  • args (Object)

    argument list (:force will force processing)

Returns:

  • (String, Symbol)


394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/attribute_struct/attribute_struct.rb', line 394

def _process_key(key, *args)
  key = key.to_s
  if(_camel_keys && _camel_keys_action)
    case _camel_keys_action
    when :auto_disable
      key._no_hump
    when :auto_enable
      key._hump
    end
  end
  if((_camel_keys && key._camel?) || args.include?(:force))
    key.to_s.split('_').map do |part|
      "#{part[0,1].upcase}#{part[1,part.size]}"
    end.join.to_sym
  else
    if(_camel_keys)
      # Convert so Hash doesn't make a new one and lose the meta
      key = ::CamelString.new(key) unless key.is_a?(::CamelString)
    end
    key
  end
end

#_rootAttributeStruct, NilClass

Returns root of the struct or nil if self is root.

Returns:

  • (AttributeStruct, NilClass)

    root of the struct or nil if self is root



457
458
459
460
461
462
463
# File 'lib/attribute_struct/attribute_struct.rb', line 457

def _root
  r = self
  until(r._parent.nil?)
    r = r._parent
  end
  r
end

#_set(key, val = nil) { ... } ⇒ Object Also known as: set!

Directly set value into struct. Useful when the key is not valid ruby syntax for a method

Parameters:

  • key (String, Symbol)
  • val (Object) (defaults to: nil)

Yields:

  • block to execute within context

Returns:

  • (Object)


143
144
145
146
147
148
149
# File 'lib/attribute_struct/attribute_struct.rb', line 143

def _set(key, val=nil, &block)
  if(val)
    self.method_missing(key, val, &block)
  else
    self.method_missing(key, &block)
  end
end

#_set_state(args = {}) ⇒ Hashish

Set state into current context

Parameters:

  • args (Hashish) (defaults to: {})

    hashish type holding data for context

Returns:

  • (Hashish)


100
101
102
# File 'lib/attribute_struct/attribute_struct.rb', line 100

def _set_state(args={})
  _arg_state.merge!(args)
end

#_state(key, traverse = true) ⇒ Object, NilClass

Value of requested state

Parameters:

  • key (Symbol, String)
  • traverse (TrueClass, FalseClass) (defaults to: true)

    traverse towards root for matching key

Returns:

  • (Object, NilClass)


109
110
111
112
113
114
115
116
117
# File 'lib/attribute_struct/attribute_struct.rb', line 109

def _state(key, traverse=true)
  if(_arg_state.keys.include?(key))
    _arg_state[key]
  else
    if(traverse && _parent)
      _parent._state(key)
    end
  end
end

#is_a?(klass) ⇒ TrueClass, FalseClass Also known as: kind_of?

Determine if self is a class

Parameters:

  • klass (Class)

Returns:

  • (TrueClass, FalseClass)


255
256
257
# File 'lib/attribute_struct/attribute_struct.rb', line 255

def is_a?(klass)
  (_klass.ancestors + [::AttributeStruct]).include?(klass)
end

#nil?TrueClass, FalseClass

Returns struct is nil (empty data).

Returns:

  • (TrueClass, FalseClass)

    struct is nil (empty data)



247
248
249
# File 'lib/attribute_struct/attribute_struct.rb', line 247

def nil?
  _data.empty?
end

#respond_to?(name) ⇒ TrueClass, FalseClass

Instance responds to method name

Parameters:

  • name (Symbol, String)

Returns:

  • (TrueClass, FalseClass)


490
491
492
# File 'lib/attribute_struct/attribute_struct.rb', line 490

def respond_to?(name)
  _klass.instance_methods.map(&:to_sym).include?(name.to_sym)
end