Add missing comments

This commit is contained in:
Torsten Ruger 2015-06-15 08:21:15 +03:00
parent 941f0a4886
commit 037f8d80d2
12 changed files with 206 additions and 54 deletions

View File

@ -1,3 +1,7 @@
# Most of the external functionality is in the writer
# if you want some attributes not written also check volotile
require_relative "sof/util"
require_relative "sof/node"
require_relative "sof/simple_node"

View File

@ -1,13 +1,39 @@
# If a class defines to_sof_node it tells the write that it will generate Nodes itself
# this delegates to array_to_sof_node
Array.class_eval do
def to_sof_node(writer , level , ref )
Sof.array_to_sof_node(self , writer , level , ref )
end
end
module Sof
# Creates a ArrayNode (see there) for the Array.
# This mainly involves creating nodes for the children
def self.array_to_sof_node(array , writer , level , ref )
node = Sof::ArrayNode.new(ref)
array.each do |object|
node.add writer.to_sof_node( object , level + 1)
end
node
end
# A ArrayNode is a Node for a Array. See Node for when and how nodes are used in Sof.
# A ArrayNode has a list of children that hold the value node representations for
# the arrays values.
#
class ArrayNode < Node
def initialize ref
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 = 0
super
short = true
@ -22,6 +48,8 @@ module Sof
end
private
# This defines the short output which is basically what you would write in ruby
# ie [ value1 , value2 , ...]
def short_out(io,level)
io.write("[")
@children.each_with_index do |child , i|
@ -30,6 +58,9 @@ module Sof
end
io.write("]")
end
# Arrays start with the minus on each line "-"
# and each line has the value
def long_out io , level
indent = " " * level
@children.each_with_index do |child , i|
@ -39,18 +70,4 @@ module Sof
end
end
end
def self.array_to_sof_node(array , writer , level , ref )
node = Sof::ArrayNode.new(ref)
array.each do |object|
node.add writer.to_sof_node( object , level + 1)
end
node
end
end
Array.class_eval do
def to_sof_node(writer , level , ref )
Sof.array_to_sof_node(self , writer , level , ref )
end
end

View File

@ -1,13 +1,41 @@
Hash.class_eval do
# If a class defines to_sof_node it tells the write that it will generate Nodes itself
# this delegates to hash_to_sof_node
def to_sof_node(writer , level , ref)
Sof.hash_to_sof_node( self , writer , level , ref)
end
end
module Sof
# Creates a HashNode (see there) for the Hash.
# This mainly involves creating nodes for key value pairs
def self.hash_to_sof_node(hash,writer , level , ref)
node = Sof::HashNode.new(ref)
hash.each do |key , object|
k = writer.to_sof_node( key ,level + 1)
v = writer.to_sof_node( object ,level + 1)
node.add(k , v)
end
node
end
# A HashNode is a Node for a Hash. See Node for when and how nodes are used in Sof.
# A HashNode has a list of children that hold the key/value node representations for
# the hashes keys and values.
class HashNode < Node
def initialize ref
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 = 0
super
short = true
@ -21,6 +49,10 @@ module Sof
long_out(io , level)
end
end
private
# This defines the short output which is basically what you would write in ruby
# ie { key1 => value1 , ... }
def short_out(io,level)
io.write("{")
children.each_with_index do |child , i|
@ -32,6 +64,10 @@ module Sof
end
io.write("}")
end
# The long output is like an array of associations.
# Arrays start with the minus on each line "-"
# 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|
@ -44,20 +80,4 @@ module Sof
end
end
end
def self.hash_to_sof_node(hash,writer , level , ref)
node = Sof::HashNode.new(ref)
hash.each do |key , object|
k = writer.to_sof_node( key ,level + 1)
v = writer.to_sof_node( object ,level + 1)
node.add(k , v)
end
node
end
end
Hash.class_eval do
def to_sof_node(writer , level , ref)
Sof.hash_to_sof_node( self , writer , level , ref)
end
end

View File

@ -1,24 +1,40 @@
module Sof
# Members are members of the graph to be written
# The class collects all reachable objects into a hash for further transformation
class Members
include Util
# initialize with the "root" object
# any object really that then becomes the root.
# the result is easier to read if it really is a root
# All reachable objects are collected into "objects" hash
# The class keeps a counter for references encountered and creates unique
# occurences for each object based on object_id (not == or ===)
def initialize root
@root = root
@counter = 1
@objects = {}
@referenced = false
add(root , 0)
end
attr_reader :objects , :root , :referenced
attr_reader :objects , :root
private
# recursively add reachable objects from this object
# this is called from the initialize and is private
def add object , level
# not storing simple (value) objects
return if is_value?(object)
# see if we we came accross this before
if( occurence = @objects[object.object_id] )
#puts "reset level #{level} at #{occurence.level}"
if occurence.level > level
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} "
occurence.set_reference(@counter)
@ -26,18 +42,24 @@ module Sof
end
return
end
# if first time see, create and store Occurence
o = Occurence.new( object , level )
@objects[object.object_id] = o
# and recursively add attributes
attributes = attributes_for(object)
attributes.each do |a|
val = get_value( object , a)
add(val , level + 1)
end
# and array values
if( object.is_a? Array )
object.each do |a|
add(a , level + 1)
end
end
# and hash keys/values
if( object.is_a? Hash )
object.each do |a,b|
add(a , level + 1)
@ -46,4 +68,6 @@ module Sof
end
end
end
# TODO, since this class only has one function, and one instance
# it could be merged as class functions to Occurence
end

View File

@ -1,22 +1,37 @@
# We transform objects into a tree of nodes
module Sof
# abstract base class for nodes in the tree
# may be referenced (should be a simple name or number)
# Before writing the objects are transformed into a tree of nodes.
# as the Members (all objects) are a graph (not tree) this is achieved by referencing
#
# 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
class Node
include Util
# only has one attribute, the referenced flag
# This could be anything, but we use a simple number counter which is handled in the Members
# class during construction of the occurrence hash
def initialize ref
#puts "node has ref #{self.class}:#{ref}" if ref
@referenced = ref
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"
def out io ,level
io.write "&#{@referenced} " if @referenced
end
# helper function to return the output as a string
# ie creates stringio, calls out and returns the string
def as_string(level)
io = StringIO.new
out(io,level)
io.string
end
attr_reader :referenced
end
end

View File

@ -2,18 +2,27 @@
module Sof
# ObjectNode means node with structure
# ie arrays and hashes get transformed into these too
# ie arrays and hashes get transformed into these too, as well as objects with attributes
class ObjectNode < Node
# init with a string, much like a simple node
# structure is added after construction and kept in a children array
def initialize data , ref
super(ref)
@data = data
@children = []
end
attr_reader :children , :data
# children array hold key value pairs
def add k , v
@children << [k,v]
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 = 0
super
io.write(@data)

View File

@ -1,5 +1,9 @@
module Sof
# simple struct like class to wrap an object and hold additionally
# - the shallowest level at which it was seen
# - A possible reference
# - the fact if it has been written (for referenced objects)
class Occurence
def initialize object , level
@object = object

View File

@ -1,19 +1,25 @@
# We transform objects into a tree of nodes
module Sof
# What makes a node simple is that it has no further structure
#
# This may mean number/string/symbol, but also tiny arrays or objects with
# very little attributes. In other words simple/object is not the same distinction
# as value/not value
class SimpleNode < Node
# data is a string that is written out in "out" function
def initialize data , ref = nil
super(ref)
@data = data
end
attr_reader :data
# just write the data given in construcor. simple. hence the name.
def out io , level
super(io,level)
io.write(data)
end
end
end

View File

@ -1,5 +1,10 @@
module Sof
# module for a couple of helpers that are needed in Members and Writer
module Util
# "value" is a property meaning simple/ not further structure
# hence int/bool/string etc are values
def is_value? o
return true if [true , false , nil].include?(o)
return true if [Fixnum, Symbol, String].include?(o.class)
@ -9,13 +14,22 @@ module Sof
return false
end
# extract an attribute by the given name from the object
# done with instance_variable_get
def get_value(object,name)
object.instance_variable_get "@#{name}".to_sym
end
# return a list of attributes for a given object
# attributes may be supressed with Volotile
# TODO should be able to specify order too
def attributes_for object
Sof::Util.attributes(object)
end
# return a list of attributes for a given object
# attributes may be supressed with Volotile
# TODO should be able to specify order too
def self.attributes( object )
atts = object.instance_variables.collect{|i| i.to_s[1..-1].to_sym } # chop of @
atts - Volotile.attributes(object.class)

View File

@ -1,11 +1,27 @@
module Sof
class Volotile
# Volotile module keeps track of attributes that are not menat to be written
# The idea being similar to private methods. So not every little detail is relevant
# for the object. Some attribuets may be calculated, cached etc,
#
# There is only one useful call for the user, "add" attributes for a given class
#
module Volotile
@@mapping = { }
def self.attributes clazz
@@mapping[clazz] || []
end
# Add attributes that are then ommited from the sof writing process
# The clazz is the real class object (eg String), and thus the
# adding must happen after the class definition, often at the end of the file
# attributes are an array of Symbols
def self.add clazz , attributes
@@mapping[clazz] = attributes
end
private
# return the volotile attributes as an array (or empty array)
def self.attributes clazz
@@mapping[clazz] || []
end
end
end

View File

@ -1,10 +1,29 @@
module Sof
# this function writes the object (and all reachable objects) out as sof
# and returns a string
# For trees or graphs this works best by handing roots
# Internally this is done in three steps:
# - All reachable objects are collected, these are called Occurences and the Members class does
# the collecting. Members holds a hash of occurences
# - A tree of nodes is created from the occurences. Different node classes for different classes
# - The nodes are witten to a steam
def self.write object
writer = Writer.new(Members.new(object) )
writer.write
end
# The writer does the coordinating work of the stages (see write function)
class Writer
include Util
# Initialized with the Members (hash of occurences, see there)
def initialize members
@members = members
end
# main function, creates nodes from the occurences and writes the nodes to a string
# returns the sof formatted string for all objects
def write
node = to_sof_node(@members.root , 0)
io = StringIO.new
@ -12,6 +31,12 @@ module Sof
io.string
end
# create a Node (subclass) for an object at a given level.
# Level is mainly needed for the indenting
# from the object we get the Occurence and decide wether a reference node is needed
# simple objects (with more inner structure) become SimpleNodes
# Any structured object bocomes a ObjectNode
# Hash and Array create their own nodes via to_sof_node functions on the classes
def to_sof_node(object , level)
if is_value?(object)
return SimpleNode.new(object.to_sof())
@ -35,6 +60,10 @@ module Sof
end
end
# create an object node from the object
# simple nodes are returned for small objects
# 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)
if( object.is_a? Class )
return SimpleNode.new( object.name , ref )
@ -55,11 +84,5 @@ module Sof
end
node
end
def self.write object
writer = Writer.new(Members.new(object) )
writer.write
end
end
end

View File

@ -17,7 +17,7 @@ require 'salama-object-file'
module Checker
def check should
out = Sof::Writer.write(@out)
out = Sof.write(@out)
same = (should == out)
puts "Shouldda\n#{out}" unless same
assert_equal should , out