diff --git a/lib/dolly/document.rb b/lib/dolly/document.rb index 6ad2641..d0bdc99 100644 --- a/lib/dolly/document.rb +++ b/lib/dolly/document.rb @@ -138,14 +138,19 @@ def self.create options = {} obj end - def self.property *ary + def self.property *ary, &block self.properties ||= {} options = ary.pop if ary.last.kind_of? Hash options ||= {} - ary.each do |name| - self.properties[name] = Property.new options.merge(name: name) - self.write_methods name + if options[:class_name] == Hash && block_given? + ary.each do |name| + init_property name, options + property = self.properties[name] + property.instance_exec(property, &block) + end + else + ary.each { |name| init_property name, options } end end @@ -185,9 +190,14 @@ def init_properties options = {} init_doc options end + def self.init_property name, opts={} + self.properties[name] = Property.new opts.merge(name: name) + self.write_methods name + end + def initialize_default_properties options _properties.reject { |property| options.keys.include? property.name }.each do |property| - property_value = property.default.clone unless Dolly::Property::CANT_CLONE.any? { |klass| property.default.is_a? klass } + property_value = property.value.clone unless Dolly::Property::CANT_CLONE.any? { |klass| property.default.is_a? klass } property_value ||= property.default self.doc[property.name] ||= property_value end diff --git a/lib/dolly/property.rb b/lib/dolly/property.rb index 27c89a5..7def60c 100644 --- a/lib/dolly/property.rb +++ b/lib/dolly/property.rb @@ -2,7 +2,7 @@ module Dolly class Property attr_writer :value attr_accessor :name - attr_reader :class_name, :default + attr_reader :class_name, :default, :subproperties CANT_CLONE = [NilClass, TrueClass, FalseClass, Fixnum].freeze @@ -11,20 +11,29 @@ def initialize opts = {} @name = opts.delete(:name).to_s @default = opts.delete(:default) @default = @default.clone if @default && CANT_CLONE.none? { |klass| @default.is_a? klass } - @value = @default if @default + @subproperties = {} warn 'There are some unprocessed options!' if opts.present? end def value - #TODO: tets if this actually sets `doc[ "name" ]` - return @default if @value.nil? - return @value unless self_klass - - klass_sym = :"#{self_klass.name.underscore}_#{__method__}" - - return self_klass.new @value unless self.respond_to?(klass_sym) + return value_if_subproperties if subproperties.any? + standard_value + end - self.send klass_sym + def subproperty *ary, &block + options = ary.pop if ary.last.kind_of? Hash + options ||= {} + + if options[:class_name] == Hash && block_given? + ary.each do |name| + @subproperties[name] = SubProperty.new options.merge(name: name) + self.subproperties[name].instance_exec self.subproperties[name], &block + end + else + ary.each do |name| + @subproperties[name] = SubProperty.new options.merge(name: name) + end + end end def array_value @@ -81,5 +90,20 @@ def self_klass @class_name.is_a?(Class)? @class_name : @class_name.constantize end + def value_if_subproperties + @value ||= self_klass.new.tap do |v| + subproperties.each { |name, subproperty| v[name.to_s] = subproperty.value } + end + end + + def standard_value + return @default if @value.nil? + klass_sym = :"#{self_klass.name.underscore}_#{__method__}" + return self_klass.new @value unless self.respond_to?(klass_sym) + self.send klass_sym + end + end + + class SubProperty < Property; end end diff --git a/test/document_test.rb b/test/document_test.rb index f1c9cfe..9869185 100644 --- a/test/document_test.rb +++ b/test/document_test.rb @@ -21,7 +21,12 @@ class FooBar < BaseDolly class Baz < Dolly::Document; end class FooBaz < Dolly::Document - property :foo, class_name: Hash, default: {} + property :foo, class_name: Hash, default: {} do + subproperty :bar, class_name: Array, default: [] + subproperty :baz, class_name: Hash, default: Hash.new do + subproperty :far, class_name: Hash, default: Hash.new + end + end def add_to_foo key, value foo[key] ||= value @@ -53,6 +58,12 @@ class Bar < FooBar property :a, :b end +class SubPropertyDocument < Dolly::Document + property :foo, :bar, class_name: Hash, default: {} do + subproperty :baz, class_name: Hash, default: Hash.new + end +end + class DocumentTest < ActiveSupport::TestCase DB_BASE_PATH = "http://localhost:5984/test".freeze @@ -498,6 +509,19 @@ def setup assert_equal 1, bar.a end + test 'subproperties and children subprops are populated on initialize' do + instance = FooBaz.new + expected = {"bar"=>[], "baz"=>{"far"=>{}}} + assert_equal expected, instance.foo + end + + test 'subproperties may be defined for multiple properties at once' do + instance = SubPropertyDocument.new + expected = {"baz"=>{}} + assert_equal expected, instance.foo + assert_equal expected, instance.bar + end + private def generic_response rows, count = 1 {total_rows: count, offset:0, rows: rows}