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: CollapseArray, Mash

Constant Summary collapse

VERSION =

Current library version

::Gem::Version.new('0.3.0')
AttributeHash =
Mash
VALID_CAMEL_STYLES =

Returns valid styles and mapped value.

Returns:

  • (Hash)

    valid styles and mapped value

{
  :bactrian => :no_leading,
  :no_leading_hump => :no_leading,
  :no_leading => :no_leading,
  :dromedary => :leading,
  :leading_hump => :leading,
  :leading => :leading
}

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



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/attribute_struct/attribute_struct.rb', line 102

def initialize(init_hash=nil, &block)
  @_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



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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/attribute_struct/attribute_struct.rb', line 196

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
      @table[sym] = base
      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
          unless(orig.is_a?(CollapseArray))
            orig = CollapseArray.new.push(orig)
          end
          orig << base
          @table[sym] = orig
        end
      end
    elsif(!args.empty? && block)
      result = leaf = base = @table.fetch(sym, _klass_new)
      @table[sym] = result

      args.flatten.each do |arg|
        leaf = base[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
      block.arity == 0 ? leaf._build(&block) : leaf._build(leaf, &block)
      if(orig)
        unless(orig.is_a?(CollapseArray))
          orig = CollapseArray.new.push(orig)
        end
        orig << leaf
      else
        orig = leaf
      end
    else
      if(args.size > 1 && args.all?{|i| i.is_a?(::String) || i.is_a?(::Symbol)} && !_state(:value_collapse))
        @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(args.size > 1)
          val = args.map do |v|
            if(v.is_a?(::Hash) && _state(:hash_load_struct))
              val = _klass_new
              val._load(v)
            else
              v
            end
          end
        else
          if(args.first.is_a?(::Hash) && _state(:hash_load_struct))
            val = _klass_new
            val._load(args.first)
          else
            val = args.first
          end
        end
        if(_state(:value_collapse) && !(leaf = @table[sym]).nil?)
          unless(leaf.is_a?(CollapseArray))
            leaf = CollapseArray.new.push(leaf)
          end
          leaf << val
          @table[sym] = leaf
        else
          @table[sym] = val
        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



20
21
22
# File 'lib/attribute_struct/attribute_struct.rb', line 20

def camel_keys
  @camel_keys
end

.camel_styleSymbol

Returns camel key style.

Returns:

  • (Symbol)

    camel key style



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

def camel_style
  @camel_style
end

Instance Attribute Details

#_arg_stateAtributeStruct::AttributeHash, Mash (readonly) Also known as: arg_state!

Returns holding space for state.

Returns:

  • (AtributeStruct::AttributeHash, Mash)

    holding space for state



95
96
97
# File 'lib/attribute_struct/attribute_struct.rb', line 95

def _arg_state
  @_arg_state
end

#_camel_keysTruthy, Falsey Also known as: camel_keys!

Returns current camelizing setting.

Returns:

  • (Truthy, Falsey)

    current camelizing setting



89
90
91
# File 'lib/attribute_struct/attribute_struct.rb', line 89

def _camel_keys
  @_camel_keys
end

#_camel_styleSymbol Also known as: camel_style!

Returns current camel style.

Returns:

  • (Symbol)

    current camel style



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

def _camel_style
  @_camel_style
end

Class Method Details

.build(&block) ⇒ Object

Create AttributeStruct instance and dump the resulting hash



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

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

.hashishAttributeStruct::AttributeHash



64
65
66
# File 'lib/attribute_struct/attribute_struct.rb', line 64

def hashish
  ::AttributeStruct::AttributeHash
end

.irb_compat!TrueClass

Note:

this will add methods required for working within IRB

Enable IRB compatibility mode

Returns:

  • (TrueClass)


78
79
80
81
# File 'lib/attribute_struct/attribute_struct.rb', line 78

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

.load_the_camelsObject

Loads helpers for camel casing



56
57
58
59
60
61
# File 'lib/attribute_struct/attribute_struct.rb', line 56

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

.validate_camel_style(style) ⇒ Symbol

Validate requested camel style and return mapped value used internally

Parameters:

  • style (Symbol)

Returns:

  • (Symbol)


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

def validate_camel_style(style)
  if(VALID_CAMEL_STYLES.has_key?(style))
    VALID_CAMEL_STYLES[style]
  else
    raise ArgumentError.new "Unsupported camel style provided `#{style.inspect}`! (Allowed: #{VALID_CAMEL_STYLES.keys(&:inspect).join(', ')})"
  end
end

Instance Method Details

#[](key) ⇒ Object

Direct data access

Parameters:

  • key (String, Symbol)

Returns:

  • (Object)


169
170
171
# File 'lib/attribute_struct/attribute_struct.rb', line 169

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

#__hashishClass

Returns hashish type available.

Returns:

  • (Class)

    hashish type available



432
433
434
# File 'lib/attribute_struct/attribute_struct.rb', line 432

def __hashish
  ::AttributeStruct::AttributeHash
end

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

Create an Array and evaluate discovered AttributeStructs

Parameters:

  • args (Object)

    array contents

Returns:

  • (Array)


551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
# File 'lib/attribute_struct/attribute_struct.rb', line 551

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 Also known as: build!

Execute block within current context

Yields:

  • block to execute

Returns:

  • (Object)


118
119
120
# File 'lib/attribute_struct/attribute_struct.rb', line 118

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



526
527
528
# File 'lib/attribute_struct/attribute_struct.rb', line 526

def _camel_keys_action
  @_camel_keys_set
end

#_camel_keys_set(v) ⇒ Symbol Also known as: camel_keys_set!

Set custom rule for processed keys at this context level

Parameters:

  • v (Symbol)

    :auto_disable or :auto_enable

Returns:

  • (Symbol)


520
521
522
# File 'lib/attribute_struct/attribute_struct.rb', line 520

def _camel_keys_set(v)
  @_camel_keys_set = v
end

#_dataAttributeStruct::AttributeHash, Mash Also known as: data!

Returns underlying struct data.

Returns:



327
328
329
# File 'lib/attribute_struct/attribute_struct.rb', line 327

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



453
454
455
456
457
458
459
460
461
462
463
464
# File 'lib/attribute_struct/attribute_struct.rb', line 453

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



336
337
338
# File 'lib/attribute_struct/attribute_struct.rb', line 336

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



441
442
443
444
445
446
447
# File 'lib/attribute_struct/attribute_struct.rb', line 441

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:



369
370
371
372
373
374
375
376
# File 'lib/attribute_struct/attribute_struct.rb', line 369

def _dump
  processed = @table.keys.map do |key|
    value = @table[key]
    val = _dump_unpacker(value)
    [_dump_unpacker(key), val] unless val == :__unset__
  end.compact
  __hashish[*processed.flatten(1)]
end

#_dump_unpacker(item) ⇒ Object

Process and unpack items for dumping within deeply nested enumerable types

Parameters:

  • item (Object)

Returns:

  • (Object)


346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/attribute_struct/attribute_struct.rb', line 346

def _dump_unpacker(item)
  if(item.is_a?(::Enumerable))
    if(item.respond_to?(:keys))
      item.class[
        *item.map do |entry|
          _dump_unpacker(entry)
        end.flatten(1)
      ]
    else
      item.class[
        *item.map do |entry|
          _dump_unpacker(entry)
        end
      ]
    end
  elsif(item.is_a?(::AttributeStruct))
    item.nil? ? :__unset__ : item._dump
  else
    item
  end
end

#_keysArray<String,Symbol> Also known as: keys!

Returns keys within struct.

Returns:

  • (Array<String,Symbol>)

    keys within struct



321
322
323
# File 'lib/attribute_struct/attribute_struct.rb', line 321

def _keys
  _data.keys
end

#_klassClass Also known as: klass!, class!, class

Returns this class.

Returns:

  • (Class)

    this class



496
497
498
# File 'lib/attribute_struct/attribute_struct.rb', line 496

def _klass
  ::AttributeStruct
end

#_klass_new(*args, &block) ⇒ AttributeStruct

Note:

will set self as parent and propogate camelizing status

Returns new struct instance.

Returns:



505
506
507
508
509
510
511
512
513
514
# File 'lib/attribute_struct/attribute_struct.rb', line 505

def _klass_new(*args, &block)
  n = _klass.new(*args, &block)
  unless(_camel_keys_action == :auto_discovery)
    n._camel_keys_set(_camel_keys_action)
  end
  n._camel_keys = _camel_keys
  n._camel_style = _camel_style if _camel_style
  n._parent(self)
  n
end

#_load(hashish) ⇒ self Also known as: load!

Clear current struct data and replace

Parameters:

  • hashish (Hash)

    hashish type instance

Returns:

  • (self)


383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/attribute_struct/attribute_struct.rb', line 383

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|
    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:



414
415
416
417
418
419
# File 'lib/attribute_struct/attribute_struct.rb', line 414

def _merge(overlay)
  source = _deep_copy
  dest = overlay._deep_copy
  result = source.deep_merge(dest)
  _klass_new(result)
end

#_merge!(overlay) ⇒ self

Perform deep merge in place

Parameters:

Returns:

  • (self)


425
426
427
428
429
# File 'lib/attribute_struct/attribute_struct.rb', line 425

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

#_parent(obj = nil) ⇒ AttributeStruct, NilClass Also known as: parent!

Returns parent of this struct.

Returns:



531
532
533
534
# File 'lib/attribute_struct/attribute_struct.rb', line 531

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)


471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
# File 'lib/attribute_struct/attribute_struct.rb', line 471

def _process_key(key, *args)
  key = ::CamelString.new(key.to_s)
  if(_camel_keys && _camel_keys_action && !key._hump_format_requested?)
    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)))
    camel_args = [key]
    if(key._hump_style || _camel_style == :no_leading)
      unless(key._hump_style == :leading_hump)
        camel_args << false
      end
    end
    ::Bogo::Utility.camel(*camel_args)
  else
    key
  end
end

#_rootAttributeStruct, NilClass Also known as: root!

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

Returns:

  • (AttributeStruct, NilClass)

    root of the struct or nil if self is root



538
539
540
541
542
543
544
# File 'lib/attribute_struct/attribute_struct.rb', line 538

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)


180
181
182
183
184
185
186
# File 'lib/attribute_struct/attribute_struct.rb', line 180

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 Also known as: set_state!

Set state into current context

Parameters:

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

    hashish type holding data for context

Returns:

  • (Hashish)


127
128
129
# File 'lib/attribute_struct/attribute_struct.rb', line 127

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

#_state(key, traverse = true) ⇒ Object, NilClass Also known as: state!

Value of requested state

Parameters:

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

    traverse towards root for matching key

Returns:

  • (Object, NilClass)


137
138
139
140
141
142
143
144
145
# File 'lib/attribute_struct/attribute_struct.rb', line 137

def _state(key, traverse=true)
  if(_arg_state.has_key?(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)


315
316
317
# File 'lib/attribute_struct/attribute_struct.rb', line 315

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)



302
303
304
# File 'lib/attribute_struct/attribute_struct.rb', line 302

def nil?
  _data.empty?
end

#present?TrueClass, FalseClass

Returns struct is present (not empty).

Returns:

  • (TrueClass, FalseClass)

    struct is present (not empty)



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

def present?
  !nil?
end

#respond_to?(name) ⇒ TrueClass, FalseClass

Instance responds to method name

Parameters:

  • name (Symbol, String)

Returns:

  • (TrueClass, FalseClass)


572
573
574
# File 'lib/attribute_struct/attribute_struct.rb', line 572

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