From 78f01081662234b46a8d685aa8e78d4d50af97f7 Mon Sep 17 00:00:00 2001 From: Torsten Ruger Date: Wed, 17 Jun 2015 21:16:39 +0300 Subject: [PATCH] push more responsibility down to node expose simple attribute write long or short many test got more compact, good --- lib/sof/array_node.rb | 20 +++++++------------- lib/sof/hash_node.rb | 27 ++++++++++----------------- lib/sof/members.rb | 5 +++-- lib/sof/node.rb | 34 +++++++++++++++++++++++++++++++--- lib/sof/object_node.rb | 39 ++++++++++++++++++++++++++++----------- lib/sof/simple_node.rb | 13 +++++++++---- lib/sof/writer.rb | 12 ++---------- test/test_basic.rb | 8 ++++---- test/test_object.rb | 2 +- test/test_refs.rb | 11 +++++------ 10 files changed, 100 insertions(+), 71 deletions(-) diff --git a/lib/sof/array_node.rb b/lib/sof/array_node.rb index 249546f..e5edb25 100644 --- a/lib/sof/array_node.rb +++ b/lib/sof/array_node.rb @@ -26,35 +26,29 @@ module Sof super(ref) @children = [] end - attr_reader :children def add c @children << c end - # The output of a Array can be a long or a short format - # The short is used for 7 or less SimpleNodes - def out io , level - super + def is_simple? + return false if(@children.length > 7 ) short = true - children.each do |c| - short = false unless c.is_a?(SimpleNode) - end - if(short and children.length < 7 ) - short_out(io , level) - else - long_out(io , level) + @children.each do |c| + short = false unless c.is_simple? end + short end private # This defines the short output which is basically what you would write in ruby # ie [ value1 , value2 , ...] + # The short is used for 7 or less SimpleNodes def short_out(io,level) io.write("[") @children.each_with_index do |child , i| child.out(io , level + 1 ) - io.write ", " unless (i+1) == children.length + io.write ", " unless (i+1) == @children.length end io.write("]") end diff --git a/lib/sof/hash_node.rb b/lib/sof/hash_node.rb index 4b7a1b1..c902802 100644 --- a/lib/sof/hash_node.rb +++ b/lib/sof/hash_node.rb @@ -28,39 +28,32 @@ module Sof super(ref) @children = [] end - attr_reader :children def add key , val @children << [key,val] end - # The output of a Hash can be a long or a short format - # The short is used for 7 or less SimpleNodes - def out io , level - super - short = true - children.each do |k,v| - short = false unless k.is_a?(SimpleNode) - short = false unless v.is_a?(SimpleNode) - end - if(short and children.length < 7 ) - short_out(io,level) - else - long_out(io , level) + def is_simple? + return false if(@children.length > 7 ) + @children.each do |k,v| + return false unless k.is_simple? + return false unless v.is_simple? end + true end private # This defines the short output which is basically what you would write in ruby # ie { key1 => value1 , ... } + # The short is used for 7 or less SimpleNodes def short_out(io,level) io.write("{") - children.each_with_index do |child , i| + @children.each_with_index do |child , i| key , val = child key.out(io , level + 1) io.write " => " val.out(io , level + 1) - io.write ", " unless (i+1) == children.length + io.write ", " unless (i+1) == @children.length end io.write("}") end @@ -70,7 +63,7 @@ module Sof # and each line has the association key => value, same as used for the {} syntax def long_out io , level indent = " " * level - children.each_with_index do |child , i| + @children.each_with_index do |child , i| key , val = child io.write "\n#{indent}" unless i == 0 io.write "- " diff --git a/lib/sof/members.rb b/lib/sof/members.rb index 9e4a5f0..cd8f711 100644 --- a/lib/sof/members.rb +++ b/lib/sof/members.rb @@ -29,14 +29,15 @@ module Sof # see if we we came accross this before if( occurence = @objects[object.object_id] ) - #puts "reset level #{level} at #{occurence.level}" + puts "reset level #{level} at #{occurence.level}" if occurence.referenced == 19 if occurence.level > level #always store the most shallow level occurence.level = level end # and only one Occurence for each object, create a reference for the second occurence unless occurence.referenced - #puts "referencing #{@counter} , at level #{level}/#{occurence.level} " + puts "referencing #{@counter} #{occurence.object.name}, at level #{level}/#{occurence.level} " if @counter == 14 + puts "referencing #{@counter} #{occurence.object.name}, at level #{level}/#{occurence.level} " if @counter == 19 occurence.set_reference(@counter) @counter = @counter + 1 end diff --git a/lib/sof/node.rb b/lib/sof/node.rb index 0f745cf..9d6e28a 100644 --- a/lib/sof/node.rb +++ b/lib/sof/node.rb @@ -7,7 +7,13 @@ module Sof # # There are only two subclasses, SimpleNode and ObejctNode, for simple or not # The base class only holds the referenced flag - # Also nodes must implement the out function + + # Node implements the out function which just checks wether a node is_simple? + # and either calls short_out or long_out + # + # Notice that different instances of the same clas may be simple or not + + # So deriving classes must implement is_simple? and accordingly long/and or short_out class Node include Util @@ -20,10 +26,23 @@ module Sof end attr_reader :referenced - # must be able to output to a stream - # This function must be called as super as it handles possible reference marker "& num" + # This ochastrates the output of derived classes to the stream + # It writes any possible reference and sees if the noe is_simple? (see there) + # and calls long / or short_out respectively def out io ,level io.write "&#{@referenced} " if @referenced + if( is_simple? ) + short_out(io,level) + else + long_out(io,level) + end + end + + # Determine wether node is simple, meaning short, in the 30 char region + # The point of hoisting this property into the public api is that + # other nodes may ask of their children and output accordingly + def is_simple? + raise "abstact function is_simple called for #{self}" end # helper function to return the output as a string @@ -33,5 +52,14 @@ module Sof out(io,level) io.string end + + private + def short_out io , level + raise "abstact function short_out called for #{self}" + end + + def long_out io , level + raise "abstact function long_out called for #{self}" + end end end diff --git a/lib/sof/object_node.rb b/lib/sof/object_node.rb index 29db674..3f711bf 100644 --- a/lib/sof/object_node.rb +++ b/lib/sof/object_node.rb @@ -8,31 +8,48 @@ module Sof # init with a string, much like a simple node # structure is added after construction and kept in a children array - def initialize data , ref + def initialize name , ref super(ref) - @data = data - @children = [] + @name = name + @simple = {} + @complex = {} end - attr_reader :children , :data - # children array hold key value pairs + # attributes hold key value pairs def add k , v - @children << [k,v] + raise "Key should be symbol not #{k}" unless k.is_a? Symbol + if( v.is_simple?) + @simple[k] = v + else + @complex[k] = v + end + end + + def is_simple? + true if( @referenced.nil? and @complex.empty? and head.length < 30 ) end # write out at the given level # level determines the indentation (level * space) # write out the data and then the children (always key value on one line) - def out io , level - super - io.write(@data) + def long_out io , level + io.write(head) indent = " " * (level + 1) - @children.each do |k,v| + @complex.each do |k,v| io.write "\n#{indent}" - k.out(io , level + 1) + io.write ":#{k}" io.write " " v.out(io , level + 1) end end + + def short_out io , level + io.write head + end + + def head + body = @simple.collect {|a,val| ":#{a} => #{val.as_string(1)}"}.join(", ") + "#{@name}(#{body})" + end end end diff --git a/lib/sof/simple_node.rb b/lib/sof/simple_node.rb index b915503..e59d25b 100644 --- a/lib/sof/simple_node.rb +++ b/lib/sof/simple_node.rb @@ -13,12 +13,17 @@ module Sof super(ref) @data = data end - attr_reader :data + # A SimpleNode is always simple (aha). + # accordingly there is no long_out + def is_simple? + true + end + + private # just write the data given in construcor. simple. hence the name. - def out io , level - super(io,level) - io.write(data) + def short_out io , level + io.write(@data) end end diff --git a/lib/sof/writer.rb b/lib/sof/writer.rb index aa39cce..702d25a 100644 --- a/lib/sof/writer.rb +++ b/lib/sof/writer.rb @@ -66,19 +66,11 @@ module Sof # small means only simple attributes and only 30 chars of them # object nodes are basically arrays (see there) def object_sof_node( object , level , ref) - head = object.class.name + "(" - atts = {} + node = ObjectNode.new(object.class.name , ref) attributes_for(object).each() do |a| val = get_value(object , a) next if val.nil? - atts[a] = to_sof_node(val , level + 1) - end - immediate , extended = atts.partition {|a,val| val.is_a?(SimpleNode) } - head += immediate.collect {|a,val| "#{a.to_sof()} => #{val.as_string(level + 1)}"}.join(", ") + ")" - return SimpleNode.new(head) if( ref.nil? and extended.empty? and head.length < 30 ) - node = ObjectNode.new(head , ref) - extended.each do |a , val| - node.add( to_sof_node(a,level + 1) , val ) + node.add( a , to_sof_node( val , level + 1) ) end node end diff --git a/test/test_basic.rb b/test/test_basic.rb index 3010a77..3994b9a 100644 --- a/test/test_basic.rb +++ b/test/test_basic.rb @@ -21,15 +21,15 @@ class BasicSof < MiniTest::Test end def test_array_array @out = [true, 1 , [true , 12 ]] - check "- true\n- 1\n- [true, 12]" + check "[true, 1, [true, 12]]" end def test_array_array_reverse @out = [ [true , 12 ], true, 1] - check "- [true, 12]\n- true\n- 1" + check "[[true, 12], true, 1]" end def test_array_array_array @out = [true, 1 , [true , 12 , [true , 123 ]]] - check "- true\n- 1\n- - true\n - 12\n - [true, 123]" + check "[true, 1, [true, 12, [true, 123]]]" end def test_simple_hash @out = { :one => 1 , :tru => true } @@ -37,7 +37,7 @@ class BasicSof < MiniTest::Test end def test_array_hash @out = [true, 1 , { :one => 1 , :tru => true }] - check "- true\n- 1\n- {:one => 1, :tru => true}" + check "[true, 1, {:one => 1, :tru => true}]" end def test_array_recursive ar = [true, 1 ] diff --git a/test/test_object.rb b/test/test_object.rb index 604da6e..6d79520 100644 --- a/test/test_object.rb +++ b/test/test_object.rb @@ -11,7 +11,7 @@ class ObjectSof < MiniTest::Test object = ObjectWithAttributes.new object.extra = [:sym , 123] @out = object - check "#{OBJECT_STRING}\n :extra [:sym, 123]" + check "ObjectWithAttributes(:name => 'some name', :number => 1234, :extra => [:sym, 123])" end def test_array_object @out = [true, 1234 , ObjectWithAttributes.new] diff --git a/test/test_refs.rb b/test/test_refs.rb index ca13311..50fa0d1 100644 --- a/test/test_refs.rb +++ b/test/test_refs.rb @@ -14,30 +14,29 @@ class TestRefs < MiniTest::Test def test_one_empty @out = @array << @hash - check "- {}" + check "[{}]" end def test_two_empty @out = @array << @hash @out << @hash - check "- &1 {} -- ->1" + check "[&1 {}, ->1]" end def test_bigger @out = [ { :one => @array , :two => [{ :three => @array}] } ] - check "- - :one => &1 []\n - :two => - {:three => ->1}" + check "[{:one => &1 [], :two => [{:three => ->1}]}]" end def test_object_ref object = ObjectWithAttributes.new object.extra = [object] @out = [ {:one => object} , object ] - check "- {:one => ->1}\n- &1 #{OBJECT_STRING}\n :extra [->1]" + check "- {:one => ->1}\n- &1 ObjectWithAttributes(:name => 'some name', :number => 1234, :extra => [->1])" end def test_object_ref2 object = ObjectWithAttributes.new object2 = ObjectWithAttributes.new object.extra = [object2] @out = [ {:one => object} , object2 ] - check "- - :one => #{OBJECT_STRING}\n :extra [->1]\n- &1 #{OBJECT_STRING}" + check "- - :one => ObjectWithAttributes(:name => 'some name', :number => 1234, :extra => [->1])\n- &1 ObjectWithAttributes(:name => 'some name', :number => 1234)" end end