Add missing comments
This commit is contained in:
parent
941f0a4886
commit
037f8d80d2
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user