Module: Doodle::BaseMethods
- Includes:
- SelfClass, SmokeAndMirrors
- Defined in:
- lib/doodle.rb
Overview
the core module of Doodle - however, to get most facilities provided by Doodle without inheriting from Doodle, include Doodle::Core, not this module
Instance Method Summary collapse
-
#arg_order(*args) ⇒ Object
define order for positional arguments.
-
#convert(owner, *args) ⇒ Object
convert a value according to conversion rules fixme: move.
-
#datatypes(*mods) ⇒ Object
set up global datatypes.
-
#doodle(*mods, &block) ⇒ Object
vector through this method to get to doodle info or enable global datatypes and provide an interface that allows you to add your own datatypes to this declaration.
-
#from(*args, &block) ⇒ Object
if block passed, define a conversion from class if no args, apply conversion to arguments.
-
#has(*args, &block) ⇒ Object
has
is an extendedattr_accessor
. -
#initialize(*args, &block) ⇒ Object
object can be initialized from a mixture of positional arguments, hash of keyword value pairs and a block which is instance_eval’d.
-
#kind(*args, &block) ⇒ Object
add a validation that attribute must be of class <= kind.
-
#marshal_dump ⇒ Object
helper for Marshal.dump.
-
#marshal_load(data) ⇒ Object
helper for Marshal.load.
-
#must(constraint = 'be valid', &block) ⇒ Object
add a validation.
-
#validate(owner, *args) ⇒ Object
validate that args meet rules defined with
must
fixme: move. -
#validate!(all = true) ⇒ Object
validate this object by applying all validations in sequence - if all == true, validate all attributes, e.g.
Methods included from SmokeAndMirrors
Methods included from SelfClass
Instance Method Details
#arg_order(*args) ⇒ Object
define order for positional arguments
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 |
# File 'lib/doodle.rb', line 757 def arg_order(*args) if args.size > 0 begin args = args.uniq args.each do |x| __doodle__.handle_error :arg_order, ArgumentError, "#{x} not a Symbol", (caller) if !(x.class <= Symbol) __doodle__.handle_error :arg_order, NameError, "#{x} not an attribute name", (caller) if !doodle.attributes.keys.include?(x) end __doodle__.arg_order = args rescue Exception => e __doodle__.handle_error :arg_order, InvalidOrderError, e.to_s, (caller) end else __doodle__.arg_order + (__doodle__.attributes.keys - __doodle__.arg_order) end end |
#convert(owner, *args) ⇒ Object
convert a value according to conversion rules fixme: move
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 |
# File 'lib/doodle.rb', line 620 def convert(owner, *args) #!p [:convert, 1, owner, args, __doodle__.conversions] begin args = args.map do |value| #!p [:convert, 2, value] if (converter = __doodle__.conversions[value.class]) #!p [:convert, 3, value] value = converter[*args] #!p [:convert, 4, value] else #!p [:convert, 5, value] # try to find nearest ancestor this_ancestors = value.class.ancestors #!p [:convert, 6, this_ancestors] matches = this_ancestors & __doodle__.conversions.keys #!p [:convert, 7, matches] indexed_matches = matches.map{ |x| this_ancestors.index(x)} #!p [:convert, 8, indexed_matches] if indexed_matches.size > 0 #!p [:convert, 9] converter_class = this_ancestors[indexed_matches.min] #!p [:convert, 10, converter_class] if converter = __doodle__.conversions[converter_class] #!p [:convert, 11, converter] value = converter[*args] #!p [:convert, 12, value] end else #!p [:convert, 13, :kind, kind, name, value] mappable_kinds = kind.select{ |x| x <= Doodle::Core } #!p [:convert, 13.1, :kind, kind, mappable_kinds] if mappable_kinds.size > 0 mappable_kinds.each do |mappable_kind| #!p [:convert, 14, :kind_is_a_doodle, value.class, mappable_kind, mappable_kind.doodle.conversions, args] if converter = mappable_kind.doodle.conversions[value.class] #!p [:convert, 15, value, mappable_kind, args] value = converter[*args] break else #!p [:convert, 16, :no_conversion_for, value.class] end end else #!p [:convert, 17, :kind_has_no_conversions] end end end #!p [:convert, 18, value] value end rescue Exception => e owner.__doodle__.handle_error name, ConversionError, "#{e.}", (caller) end if args.size > 1 args else args.first end end |
#datatypes(*mods) ⇒ Object
set up global datatypes
466 467 468 469 470 |
# File 'lib/doodle.rb', line 466 def datatypes(*mods) mods.each do |mod| DataTypeHolder.class_eval { include mod } end end |
#doodle(*mods, &block) ⇒ Object
vector through this method to get to doodle info or enable global datatypes and provide an interface that allows you to add your own datatypes to this declaration
475 476 477 478 479 480 481 482 483 484 485 |
# File 'lib/doodle.rb', line 475 def doodle(*mods, &block) if mods.size == 0 && !block_given? __doodle__ else dh = Doodle::DataTypeHolder.new(self) mods.each do |mod| dh.extend(mod) end dh.instance_eval(&block) end end |
#from(*args, &block) ⇒ Object
if block passed, define a conversion from class if no args, apply conversion to arguments
579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/doodle.rb', line 579 def from(*args, &block) #p [:from, self, args] if block_given? # set the rule for each arg given args.each do |arg| __doodle__.local_conversions[arg] = block end else convert(self, *args) end end |
#has(*args, &block) ⇒ Object
has
is an extended attr_accessor
simple usage - just like attr_accessor
:
class Event
has :date
end
set default value:
class Event
has :date, :default => Date.today
end
set lazily evaluated default value:
class Event
has :date do
default { Date.today }
end
end
742 743 744 745 746 747 748 749 750 751 752 753 754 |
# File 'lib/doodle.rb', line 742 def has(*args, &block) #DBG: Doodle::Debug.d { [:has, self, self.class, args] } params = DoodleAttribute.params_from_args(self, *args) # get specialized attribute class or use default attribute_class = params.delete(:using) || DoodleAttribute # could this be handled in DoodleAttribute? # define getter setter before setting up attribute define_getter_setter params[:name], *args, &block #p [:attribute, attribute_class, params] __doodle__.local_attributes[params[:name]] = attribute_class.new(params, &block) end |
#initialize(*args, &block) ⇒ Object
object can be initialized from a mixture of positional arguments, hash of keyword value pairs and a block which is instance_eval’d
834 835 836 837 838 839 840 841 842 843 844 845 846 847 |
# File 'lib/doodle.rb', line 834 def initialize(*args, &block) built_in = Doodle::BuiltIns::BUILTINS.select{ |x| self.kind_of?(x) }.first if built_in super end __doodle__.validation_on = true __doodle__.parent = Doodle.context[-1] Doodle.context.push(self) __doodle__.defer_validation do doodle.initialize_from_hash(*args) instance_eval(&block) if block_given? end Doodle.context.pop end |
#kind(*args, &block) ⇒ Object
add a validation that attribute must be of class <= kind
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
# File 'lib/doodle.rb', line 602 def kind(*args, &block) @kind ||= [] if args.size > 0 @kind = [args].flatten # todo[figure out how to handle kind being specified twice?] if @kind.size > 2 kind_text = "a kind of #{ @kind[0..-2].map{ |x| x.to_s }.join(', ') } or #{@kind[-1].to_s}" # => else kind_text = "a kind of #{@kind.to_s}" end __doodle__.local_validations << (Validation.new(kind_text) { |x| @kind.any? { |klass| x.kind_of?(klass) } }) else @kind end end |
#marshal_dump ⇒ Object
helper for Marshal.dump
488 489 490 491 |
# File 'lib/doodle.rb', line 488 def marshal_dump # note: perhaps should also dump singleton attribute definitions? instance_variables.map{|x| [x, instance_variable_get(x)] } end |
#marshal_load(data) ⇒ Object
helper for Marshal.load
493 494 495 496 497 |
# File 'lib/doodle.rb', line 493 def marshal_load(data) data.each do |name, value| instance_variable_set(name, value) end end |
#must(constraint = 'be valid', &block) ⇒ Object
add a validation
592 593 594 595 596 597 598 599 |
# File 'lib/doodle.rb', line 592 def must(constraint = 'be valid', &block) if block.nil? # is this really useful? do I really want it? __doodle__.local_validations << Validation.new(constraint, &proc { |v| v.instance_eval(constraint) }) else __doodle__.local_validations << Validation.new(constraint, &block) end end |
#validate(owner, *args) ⇒ Object
validate that args meet rules defined with must
fixme: move
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 |
# File 'lib/doodle.rb', line 682 def validate(owner, *args) ##DBG: Doodle::Debug.d { [:validate, self, :owner, owner, :args, args ] } #!p [:validate, 1, args] begin value = convert(owner, *args) rescue Exception => e owner.__doodle__.handle_error name, ConversionError, "#{owner.kind_of?(Class) ? owner : owner.class}.#{ name } - #{e.}", (caller) end #!p [:validate, 2, args, :becomes, value] __doodle__.validations.each do |v| ##DBG: Doodle::Debug.d { [:validate, self, v, args, value] } if !v.block[value] owner.__doodle__.handle_error name, ValidationError, "#{owner.kind_of?(Class) ? owner : owner.class}.#{ name } must #{ v. } - got #{ value.class }(#{ value.inspect })", (caller) end end #!p [:validate, 3, value] value end |
#validate!(all = true) ⇒ Object
validate this object by applying all validations in sequence
-
if all == true, validate all attributes, e.g. when loaded from YAML, else validate at object level only
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 |
# File 'lib/doodle.rb', line 783 def validate!(all = true) ##DBG: Doodle::Debug.d { [:validate!, all, caller] } if all __doodle__.errors.clear end if __doodle__.validation_on if self.class == Class attribs = __doodle__.class_attributes ##DBG: Doodle::Debug.d { [:validate!, "using class_attributes", class_attributes] } else attribs = __doodle__.attributes ##DBG: Doodle::Debug.d { [:validate!, "using instance_attributes", doodle.attributes] } end attribs.each do |name, att| ivar_name = "@#{att.name}" if instance_variable_defined?(ivar_name) # if all == true, reset values so conversions and # validations are applied to raw instance variables # e.g. when loaded from YAML if all ##DBG: Doodle::Debug.d { [:validate!, :sending, att.name, instance_variable_get(ivar_name) ] } __send__("#{att.name}=", instance_variable_get(ivar_name)) end elsif att.optional? # treat default/init as special case ##DBG: Doodle::Debug.d { [:validate!, :optional, name ]} break elsif self.class != Class __doodle__.handle_error name, Doodle::ValidationError, "#{self} missing required attribute '#{name}'", (caller) end end # now apply instance level validations ##DBG: Doodle::Debug.d { [:validate!, "validations", doodle_validations ]} __doodle__.validations.each do |v| ##DBG: Doodle::Debug.d { [:validate!, self, v ] } begin if !instance_eval(&v.block) __doodle__.handle_error self, ValidationError, "#{ self.class } must #{ v. }", (caller) end rescue Exception => e __doodle__.handle_error self, ValidationError, e.to_s, (caller) end end end # if OK, then return self self end |