Class: Doodle::DoodleInfo

Inherits:
Object
  • Object
show all
Defined in:
lib/doodle.rb

Overview

place to stash bookkeeping info

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(object) ⇒ DoodleInfo

Returns a new instance of DoodleInfo.



318
319
320
321
322
323
324
325
326
327
328
# File 'lib/doodle.rb', line 318

def initialize(object)
  @this = object
  @local_attributes = Doodle::OrderedHash.new
  @local_validations = []
  @validation_on = true
  @local_conversions = {}
  @arg_order = []
  @errors = []
  #@parent = nil
  @parent = Doodle.parent
end

Instance Attribute Details

#arg_orderObject

Returns the value of attribute arg_order.



314
315
316
# File 'lib/doodle.rb', line 314

def arg_order
  @arg_order
end

#errorsObject

Returns the value of attribute errors.



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

def errors
  @errors
end

#local_attributesObject

Returns the value of attribute local_attributes.



310
311
312
# File 'lib/doodle.rb', line 310

def local_attributes
  @local_attributes
end

#local_conversionsObject

Returns the value of attribute local_conversions.



312
313
314
# File 'lib/doodle.rb', line 312

def local_conversions
  @local_conversions
end

#local_validationsObject

Returns the value of attribute local_validations.



311
312
313
# File 'lib/doodle.rb', line 311

def local_validations
  @local_validations
end

#parentObject

Returns the value of attribute parent.



316
317
318
# File 'lib/doodle.rb', line 316

def parent
  @parent
end

#thisObject

Returns the value of attribute this.



309
310
311
# File 'lib/doodle.rb', line 309

def this
  @this
end

#validation_onObject

Returns the value of attribute validation_on.



313
314
315
# File 'lib/doodle.rb', line 313

def validation_on
  @validation_on
end

Instance Method Details

#attributes(tf = true) ⇒ Object

returns array of Attributes

  • if tf == true, returns all inherited attributes

  • if tf == false, returns only those attributes defined in the current object/class



391
392
393
394
395
396
397
398
# File 'lib/doodle.rb', line 391

def attributes(tf = true)
  results = handle_inherited_hash(tf, :local_attributes)
  # if an instance, include the singleton_class attributes
  if !@this.kind_of?(Class) && @this.singleton_class.doodle.respond_to?(:attributes)
    results = results.merge(@this.singleton_class.doodle.attributes)
  end
  results
end

#class_attributesObject

return class level attributes



408
409
410
411
412
413
414
415
416
417
418
# File 'lib/doodle.rb', line 408

def class_attributes
  attrs = Doodle::OrderedHash.new
  if @this.kind_of?(Class)
    attrs = collect_inherited(:class_attributes).inject(Doodle::OrderedHash.new){ |hash, item|
      hash.merge(Doodle::OrderedHash[*item])
    }.merge(@this.singleton_class.doodle.respond_to?(:attributes) ? @this.singleton_class.doodle.attributes : { })
    attrs
  else
    @this.class.doodle.class_attributes
  end
end

#collect_inherited(message) ⇒ Object

send message to all doodle_parents and collect results



366
367
368
369
370
371
372
373
374
375
376
# File 'lib/doodle.rb', line 366

def collect_inherited(message)
  result = []
  parents.each do |klass|
    if klass.respond_to?(:doodle) && klass.doodle.respond_to?(message)
      result.unshift(*klass.doodle.__send__(message))
    else
      break
    end
  end
  result
end

#conversions(tf = true) ⇒ Object

returns hash of conversions

  • if tf == true, returns all inherited conversions

  • if tf == false, returns only those conversions defined in the current object/class



447
448
449
# File 'lib/doodle.rb', line 447

def conversions(tf = true)
  handle_inherited_hash(tf, :local_conversions)
end

#defer_validation(&block) ⇒ Object

turn off validation, execute block, then set validation to same state as it was before defer_validation was called - can be nested



483
484
485
486
487
488
489
490
491
492
493
494
# File 'lib/doodle.rb', line 483

def defer_validation(&block)
  old_validation = self.validation_on
  self.validation_on = false
  v = nil
  begin
    v = @this.instance_eval(&block)
  ensure
    self.validation_on = old_validation
  end
  @this.validate!(false)
  v
end

#handle_error(name, *args) ⇒ Object

handle errors either by collecting in :errors or raising an exception



339
340
341
342
343
344
345
346
347
# File 'lib/doodle.rb', line 339

def handle_error(name, *args)
  # don't include duplicates (FIXME: hacky - shouldn't have duplicates in the first place)
  if !errors.include?([name, *args])
    errors << [name, *args]
  end
  if Doodle.raise_exception_on_error
    raise(*args)
  end
end

#handle_inherited_hash(tf, method) ⇒ Object



378
379
380
381
382
383
384
385
386
# File 'lib/doodle.rb', line 378

def handle_inherited_hash(tf, method)
  if tf
    collect_inherited(method).inject(Doodle::OrderedHash.new){ |hash, item|
      hash.merge(Doodle::OrderedHash[*item])
    }.merge(@this.doodle.__send__(method))
  else
    @this.doodle.__send__(method)
  end
end

#initial_values(tf = true) ⇒ Object



451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/doodle.rb', line 451

def initial_values(tf = true)
  attributes(tf).select{|n, a| a.init_defined? }.inject({}) {|hash, (n, a)|
    #p [:initial_values, a.name]
    hash[n] = case a.init
              when NilClass, TrueClass, FalseClass, Fixnum, Float, Bignum, Symbol
                # uncloneable values
                #p [:initial_values, :special, a.name, a.init]
                a.init
              when DeferredBlock
                #p [:initial_values, self, DeferredBlock, a.name]
                begin
                  @this.instance_eval(&a.init.block)
                rescue Object => e
                  #p [:exception_in_deferred_block, e]
                  raise
                end
              else
                #p [:initial_values, :clone, a.name]
                begin
                  a.init.clone
                rescue Exception => e
                  warn "tried to clone #{a.init.class} in :init option (#{e})"
                  #p [:initial_values, :exception, a.name, e]
                  a.init
                end
              end
    hash
  }
end

#initialize_from_hash(*args) ⇒ Object

helper function to initialize from hash - this is safe to use after initialization (validate! is called if this method is called after initialization)



499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
# File 'lib/doodle.rb', line 499

def initialize_from_hash(*args)
  # p [:doodle_initialize_from_hash, :args, *args]
  defer_validation do
    # hash initializer
    # separate into array of hashes of form [{:k1 => v1}, {:k2 => v2}] and positional args
    key_values, args = args.partition{ |x| x.kind_of?(Hash)}
    #DBG: Doodle::Debug.d { [self.class, :doodle_initialize_from_hash, :key_values, key_values, :args, args] }
    #!p [self.class, :doodle_initialize_from_hash, :key_values, key_values, :args, args]

    # set up initial values with ~clones~ of specified values (so not shared between instances)
    #init_values = initial_values
    #!p [:init_values, init_values]

    # match up positional args with attribute names (from arg_order) using idiom to create hash from array of assocs
    #arg_keywords = init_values.merge(Hash[*(Utils.flatten_first_level(self.class.arg_order[0...args.size].zip(args)))])
    arg_keywords = Hash[*(Utils.flatten_first_level(self.class.arg_order[0...args.size].zip(args)))]
    #!p [self.class, :doodle_initialize_from_hash, :arg_keywords, arg_keywords]

    # merge all hash args into one
    key_values = key_values.inject(arg_keywords) { |hash, item|
      #!p [self.class, :doodle_initialize_from_hash, :merge, hash, item]
      hash.merge(item)
    }
    #!p [self.class, :doodle_initialize_from_hash, :key_values2, key_values]

    # convert keys to symbols (note not recursively - only first level == doodle keywords)
    Doodle::Utils.symbolize_keys!(key_values)
    #DBG: Doodle::Debug.d { [self.class, :doodle_initialize_from_hash, :key_values2, key_values, :args2, args] }
    #!p [self.class, :doodle_initialize_from_hash, :key_values3, key_values]

    # create attributes
    key_values.keys.each do |key|
      #DBG: Doodle::Debug.d { [self.class, :doodle_initialize_from_hash, :setting, key, key_values[key]] }
      #p [self.class, :doodle_initialize_from_hash, :setting, key, key_values[key]]
      if respond_to?(key)
        __send__(key, key_values[key])
      else
        # raise error if not defined
        __doodle__.handle_error key, Doodle::UnknownAttributeError, "unknown attribute '#{key}' => #{key_values[key].inspect} for #{self} #{doodle.attributes.map{ |k,v| k.inspect}.join(', ')}", Doodle::Utils.doodle_caller
      end
    end
    # do init_values after user supplied values so init blocks can depend on user supplied values
    #p [:getting_init_values, instance_variables]
    __doodle__.initial_values.each do |key, value|
      if !key_values.key?(key) && respond_to?(key)
        #p [:initial_values, key, value]
        __send__(key, value)
      end
    end
  end
end

#inspectObject



334
335
336
# File 'lib/doodle.rb', line 334

def inspect
  ''
end

#lookup_attribute(name) ⇒ Object



434
435
436
437
438
439
440
441
442
# File 'lib/doodle.rb', line 434

def lookup_attribute(name)
  # (look at singleton attributes first)
  # fixme[this smells like a hack to me]
  if @this.class == Class
    class_attributes[name]
  else
    attributes[name]
  end
end

#parentsObject

provide an alternative inheritance chain that works for singleton classes as well as modules, classes and instances



351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/doodle.rb', line 351

def parents
  anc = if @this.respond_to?(:ancestors)
          if @this.ancestors.include?(@this)
            @this.ancestors[1..-1]
          else
            # singletons have no doodle_parents (they're orphans)
            []
          end
        else
          @this.class.ancestors
        end
  anc.select{|x| x.kind_of?(Class)}
end

#validations(tf = true) ⇒ Object



420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'lib/doodle.rb', line 420

def validations(tf = true)
  if tf
    # note: validations are handled differently to attributes and
    # conversions because ~all~ validations apply (so are stored
    # as an array), whereas attributes and conversions are keyed
    # by name and kind respectively, so only the most recent
    # applies

    local_validations + collect_inherited(:local_validations)
  else
    local_validations
  end
end

#values(tf = true) ⇒ Object

returns array of values

  • if tf == true, returns all inherited values (default)

  • if tf == false, returns only those values defined in current object



403
404
405
# File 'lib/doodle.rb', line 403

def values(tf = true)
  attributes(tf).map{ |k, a| @this.send(k)}
end