Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
e71b8d8010 | |||
1a037f616e | |||
921bb76f83 | |||
9adf0b7e00 | |||
0868ef5770 | |||
3eb6870bff | |||
ca803225c6 | |||
4754ba60be | |||
f00364cd18 | |||
78f0108166 | |||
72fa26b00e | |||
6f9d4af3cd | |||
293ddb3b69 | |||
389b5f9e1a | |||
7d01c1193c | |||
1ee0f7c006 | |||
224bd84b01 | |||
4853f944ef | |||
037f8d80d2 | |||
941f0a4886 | |||
5ca9eed89c | |||
bdd67b3213 | |||
aebbe17252 | |||
db13ccbe41 | |||
9492f29e66 | |||
b00f49ade6 | |||
217eda2436 | |||
b5d44bf2f3 | |||
778d751c55 | |||
c48b7a3403 |
1
Gemfile
1
Gemfile
@ -1,5 +1,6 @@
|
|||||||
source "http://rubygems.org"
|
source "http://rubygems.org"
|
||||||
|
|
||||||
|
gem "rake"
|
||||||
|
|
||||||
gem "salama-object-file" , :path => "."
|
gem "salama-object-file" , :path => "."
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
salama-object-file (0.1)
|
salama-object-file (0.2.0)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: http://rubygems.org/
|
remote: http://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
minitest (5.6.1)
|
minitest (5.6.1)
|
||||||
|
rake (10.4.2)
|
||||||
rubygems-tasks (0.2.4)
|
rubygems-tasks (0.2.4)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
@ -14,5 +15,6 @@ PLATFORMS
|
|||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
minitest
|
minitest
|
||||||
|
rake
|
||||||
rubygems-tasks
|
rubygems-tasks
|
||||||
salama-object-file!
|
salama-object-file!
|
||||||
|
18
README.md
18
README.md
@ -12,6 +12,7 @@ But the "sort of" started to get to me, because
|
|||||||
|
|
||||||
- 1) it's way to verbose (long files, object groups over many pages) and
|
- 1) it's way to verbose (long files, object groups over many pages) and
|
||||||
- 2) does not allow for (easy) ordering.
|
- 2) does not allow for (easy) ordering.
|
||||||
|
- 3) has no concept of dumping only parts of an object
|
||||||
|
|
||||||
To fix this i started on Sof, with an eye to expand it.
|
To fix this i started on Sof, with an eye to expand it.
|
||||||
|
|
||||||
@ -21,7 +22,14 @@ The main starting goal was quite like yaml, but with
|
|||||||
- also short versions of arrays and hashes
|
- also short versions of arrays and hashes
|
||||||
- Shorter class names (no ruby/object or even ruby/struct stuff)
|
- Shorter class names (no ruby/object or even ruby/struct stuff)
|
||||||
- references at the most shallow level
|
- references at the most shallow level
|
||||||
- an easy way to order attributes and specify attributes that should not be serialized
|
- a way specify attributes that should not be serialized
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
The module's main useful api is
|
||||||
|
|
||||||
|
Sof::Writer.write(object_to_derialize)
|
||||||
|
|
||||||
|
|
||||||
### Salama Object File
|
### Salama Object File
|
||||||
|
|
||||||
@ -33,13 +41,13 @@ but rather very complicated.
|
|||||||
|
|
||||||
An object machine must off course have it's own object files, because:
|
An object machine must off course have it's own object files, because:
|
||||||
|
|
||||||
- otherwise we'd have to express the object machine in c (nischt gut)
|
- otherwise we'd have to express the object machine in c formats (nischt gut)
|
||||||
- we would be forced to read the source every time (slow)
|
- we would be forced to read the source every time (slow)
|
||||||
- we would have no language independant format
|
- we would have no language independent format
|
||||||
|
|
||||||
And i was going to get there, juust not now. I mean i think it's a great idea to have many languages
|
And i was going to get there, just not now. I mean i think it's a great idea to have many languages
|
||||||
compile and run on the same object machine.
|
compile and run on the same object machine.
|
||||||
Not neccessarily my idea, but i haven't seen it pulled off. Not that i will.
|
Not necessarily my idea, but i haven't seen it pulled off. Not that i will.
|
||||||
|
|
||||||
I just want to be able to read my compiled code!!
|
I just want to be able to read my compiled code!!
|
||||||
|
|
||||||
|
@ -1,12 +1,23 @@
|
|||||||
|
# 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/util"
|
||||||
require_relative "sof/node"
|
require_relative "sof/node"
|
||||||
|
require_relative "sof/simple_node"
|
||||||
|
require_relative "sof/object_node"
|
||||||
require_relative "sof/members"
|
require_relative "sof/members"
|
||||||
require_relative "sof/volotile"
|
require_relative "sof/volotile"
|
||||||
require_relative "sof/writer"
|
require_relative "sof/writer"
|
||||||
require_relative "sof/array"
|
require_relative "sof/array_node"
|
||||||
require_relative "sof/hash"
|
require_relative "sof/hash_node"
|
||||||
require_relative "sof/occurence"
|
require_relative "sof/occurence"
|
||||||
|
|
||||||
|
Class.class_eval do
|
||||||
|
def to_sof
|
||||||
|
self.name
|
||||||
|
end
|
||||||
|
end
|
||||||
Symbol.class_eval do
|
Symbol.class_eval do
|
||||||
def to_sof()
|
def to_sof()
|
||||||
":#{to_s}"
|
":#{to_s}"
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
module Sof
|
|
||||||
class ArrayNode < Node
|
|
||||||
def initialize ref
|
|
||||||
super(ref)
|
|
||||||
@children = []
|
|
||||||
end
|
|
||||||
attr_reader :children
|
|
||||||
def add c
|
|
||||||
@children << c
|
|
||||||
end
|
|
||||||
def out io , level = 0
|
|
||||||
super
|
|
||||||
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)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
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
|
|
||||||
end
|
|
||||||
io.write("]")
|
|
||||||
end
|
|
||||||
def long_out io , level
|
|
||||||
indent = " " * level
|
|
||||||
@children.each_with_index do |child , i|
|
|
||||||
io.write "\n#{indent}" unless i == 0
|
|
||||||
io.write "-"
|
|
||||||
child.out(io , level + 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Array.class_eval do
|
|
||||||
def to_sof_node(writer , level , ref )
|
|
||||||
node = Sof::ArrayNode.new(ref)
|
|
||||||
each do |object|
|
|
||||||
node.add writer.to_sof_node( object , level + 1)
|
|
||||||
end
|
|
||||||
node
|
|
||||||
end
|
|
||||||
end
|
|
51
lib/sof/array_node.rb
Normal file
51
lib/sof/array_node.rb
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
|
||||||
|
module Sof
|
||||||
|
|
||||||
|
# A ArrayNode is a Node for an 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
|
||||||
|
|
||||||
|
def add c
|
||||||
|
@children << c
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_simple?
|
||||||
|
return false if(@children.length > 7 )
|
||||||
|
short = true
|
||||||
|
@children.each do |c|
|
||||||
|
short = false unless c.is_simple?
|
||||||
|
end
|
||||||
|
short
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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
|
||||||
|
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|
|
||||||
|
io.write "\n#{indent}" unless i == 0
|
||||||
|
io.write "- "
|
||||||
|
child.out(io , level + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,60 +0,0 @@
|
|||||||
module Sof
|
|
||||||
class HashNode < Node
|
|
||||||
def initialize ref
|
|
||||||
super(ref)
|
|
||||||
@children = []
|
|
||||||
end
|
|
||||||
attr_reader :children
|
|
||||||
def add key , val
|
|
||||||
@children << [key,val]
|
|
||||||
end
|
|
||||||
def out io , level = 0
|
|
||||||
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)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def short_out(io,level)
|
|
||||||
io.write("{")
|
|
||||||
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
|
|
||||||
end
|
|
||||||
io.write("}")
|
|
||||||
end
|
|
||||||
def long_out io , level
|
|
||||||
indent = " " * level
|
|
||||||
children.each_with_index do |child , i|
|
|
||||||
key , val = child
|
|
||||||
io.write "\n#{indent}" unless i == 0
|
|
||||||
io.write "-"
|
|
||||||
key.out(io , level + 1)
|
|
||||||
io.write " => "
|
|
||||||
val.out(io , level + 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Hash.class_eval do
|
|
||||||
def to_sof_node(writer , level , ref)
|
|
||||||
node = Sof::HashNode.new(ref)
|
|
||||||
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
|
|
||||||
|
|
55
lib/sof/hash_node.rb
Normal file
55
lib/sof/hash_node.rb
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
module Sof
|
||||||
|
# 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
|
||||||
|
|
||||||
|
def add key , val
|
||||||
|
@children << [key,val]
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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|
|
||||||
|
key , val = child
|
||||||
|
key.out(io , level + 1)
|
||||||
|
io.write " => "
|
||||||
|
val.out(io , level + 1)
|
||||||
|
io.write ", " unless (i+1) == @children.length
|
||||||
|
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|
|
||||||
|
key , val = child
|
||||||
|
io.write "\n#{indent}" unless i == 0
|
||||||
|
io.write "- "
|
||||||
|
key.out(io , level + 1)
|
||||||
|
io.write " => "
|
||||||
|
val.out(io , level + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,48 +1,113 @@
|
|||||||
module Sof
|
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
|
class Members
|
||||||
include Util
|
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
|
def initialize root
|
||||||
@root = root
|
@root = root
|
||||||
@counter = 1
|
@counter = 1
|
||||||
|
@references = []
|
||||||
@objects = {}
|
@objects = {}
|
||||||
@referenced = false
|
add_object( root , 0)
|
||||||
add(root , 0)
|
collect_level(0 , [root])
|
||||||
end
|
end
|
||||||
attr_reader :objects , :root , :referenced
|
attr_reader :objects , :root
|
||||||
|
|
||||||
def add object , level
|
private
|
||||||
return if is_value?(object)
|
# add object (as occurence) if it doesn't exist
|
||||||
|
# return object or nil
|
||||||
|
def add_object object , level
|
||||||
|
# see if we we came accross this before
|
||||||
if( occurence = @objects[object.object_id] )
|
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
|
if occurence.level > level
|
||||||
|
#always store the most shallow level
|
||||||
occurence.level = level
|
occurence.level = level
|
||||||
end
|
end
|
||||||
|
# and only one Occurence for each object, create a reference for the second occurence
|
||||||
unless occurence.referenced
|
unless occurence.referenced
|
||||||
#puts "referencing #{@counter} , at level #{level}/#{occurence.level} "
|
# puts "referencing #{@counter} #{occurence.object.name}, at level #{level}/#{occurence.level} " if @counter == 23
|
||||||
occurence.set_reference(@counter)
|
# puts "referencing #{@counter} #{occurence.object.name}, at level #{level}/#{occurence.level} " if @counter == 19
|
||||||
|
if object.respond_to? :sof_reference_name
|
||||||
|
reference = object.sof_reference_name
|
||||||
|
reference = reference.to_s.gsub(/\s|\W/ , "") #remove space and stuff
|
||||||
|
if( @references.include?(reference) or reference.empty?)
|
||||||
|
reference = "#{reference}-#{@counter}"
|
||||||
@counter = @counter + 1
|
@counter = @counter + 1
|
||||||
end
|
end
|
||||||
return
|
else
|
||||||
|
reference = @counter.to_s
|
||||||
|
@counter = @counter + 1
|
||||||
end
|
end
|
||||||
o = Occurence.new( object , level )
|
occurence.set_reference(reference)
|
||||||
@objects[object.object_id] = o
|
@references << reference
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
# if first time see, create and store Occurence
|
||||||
|
@objects[object.object_id] = Occurence.new( object , level )
|
||||||
|
return object
|
||||||
|
end
|
||||||
|
|
||||||
|
# recursively find reachable objects from this level of objects
|
||||||
|
# this is called from the initialize and is private
|
||||||
|
# we go through the tree in breadth first (which is a little more effort) to catch lowest
|
||||||
|
# references.
|
||||||
|
def collect_level level , objects
|
||||||
|
next_level = Array.new
|
||||||
|
#puts "collect level #{level} #{objects.length}"
|
||||||
|
objects.each do |object|
|
||||||
|
#puts "collect level #{level} #{object.object_id}"
|
||||||
|
# not storing simple (value) objects
|
||||||
|
next if is_value?(object)
|
||||||
|
case object.class.name
|
||||||
|
when "Array" , "Parfait::List"
|
||||||
|
collect_array object , next_level
|
||||||
|
when "Hash" , "Parfait::Dictionary"
|
||||||
|
collect_hash object, next_level
|
||||||
|
else
|
||||||
|
# and recursively add attributes
|
||||||
attributes = attributes_for(object)
|
attributes = attributes_for(object)
|
||||||
attributes.each do |a|
|
attributes.each do |a|
|
||||||
val = get_value( object , a)
|
val = get_value( object , a)
|
||||||
add(val , level + 1)
|
next_level << val
|
||||||
end
|
end
|
||||||
if( object.is_a? Array )
|
#TODO get all superclsses here, but this covers 99% so . . moving on
|
||||||
object.each do |a|
|
superclasses = [object.class.superclass.name]
|
||||||
add(a , level + 1)
|
if superclasses.include?( "Array") or superclasses.include?( "Parfait::List")
|
||||||
|
collect_array object, next_level
|
||||||
|
end
|
||||||
|
if superclasses.include?( "Hash") or superclasses.include?( "Parfait::Dictionary")
|
||||||
|
collect_hash object, next_level
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if( object.is_a? Hash )
|
|
||||||
object.each do |a,b|
|
|
||||||
add(a , level + 1)
|
|
||||||
add(b , level + 1)
|
|
||||||
end
|
end
|
||||||
|
new_objects = next_level.collect { |o| add_object(o , level + 1) }
|
||||||
|
new_objects.compact!
|
||||||
|
# recurse , but break off if hit bottom
|
||||||
|
collect_level( level + 1 , new_objects) unless new_objects.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
# and hash keys/values
|
||||||
|
def collect_hash hash , next_level
|
||||||
|
hash.each do |a,b|
|
||||||
|
next_level << a
|
||||||
|
next_level << b
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# and array values
|
||||||
|
def collect_array array , next_level
|
||||||
|
array.each do |a|
|
||||||
|
next_level << a
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,58 +1,65 @@
|
|||||||
# We transform objects into a tree of nodes
|
# We transform objects into a tree of nodes
|
||||||
|
|
||||||
module Sof
|
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
|
||||||
|
|
||||||
|
# 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
|
class Node
|
||||||
include Util
|
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
|
def initialize ref
|
||||||
|
#puts "node has ref #{self.class}:#{ref}" if ref
|
||||||
@referenced = ref
|
@referenced = ref
|
||||||
end
|
end
|
||||||
# must be able to output to a stream
|
attr_reader :referenced
|
||||||
|
|
||||||
|
# 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
|
def out io ,level
|
||||||
io.write "&#{@referenced} " if @referenced
|
io.write "&#{@referenced} " if @referenced
|
||||||
|
if( is_simple? )
|
||||||
|
short_out(io,level)
|
||||||
|
else
|
||||||
|
long_out(io,level)
|
||||||
end
|
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
|
||||||
|
# ie creates stringio, calls out and returns the string
|
||||||
def as_string(level)
|
def as_string(level)
|
||||||
io = StringIO.new
|
io = StringIO.new
|
||||||
out(io,level)
|
out(io,level)
|
||||||
io.string
|
io.string
|
||||||
end
|
end
|
||||||
attr_reader :referenced
|
|
||||||
|
private
|
||||||
|
def short_out io , level
|
||||||
|
raise "abstact function short_out called for #{self}"
|
||||||
end
|
end
|
||||||
|
|
||||||
class SimpleNode < Node
|
def long_out io , level
|
||||||
def initialize data , ref = nil
|
raise "abstact function long_out called for #{self}"
|
||||||
super(ref)
|
|
||||||
@data = data
|
|
||||||
end
|
|
||||||
attr_reader :data
|
|
||||||
def out io , level
|
|
||||||
super(io,level)
|
|
||||||
io.write(data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class ObjectNode < Node
|
|
||||||
def initialize data , ref
|
|
||||||
super(ref)
|
|
||||||
@data = data
|
|
||||||
@children = []
|
|
||||||
end
|
|
||||||
attr_reader :children , :data
|
|
||||||
def add k , v
|
|
||||||
@children << [k,v]
|
|
||||||
end
|
|
||||||
def out io , level = 0
|
|
||||||
super
|
|
||||||
io.write(@data)
|
|
||||||
indent = " " * (level + 1)
|
|
||||||
@children.each_with_index do |child , i|
|
|
||||||
k , v = child
|
|
||||||
io.write "\n#{indent}"
|
|
||||||
k.out(io , level + 2)
|
|
||||||
io.write " "
|
|
||||||
v.out(io , level + 2)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
78
lib/sof/object_node.rb
Normal file
78
lib/sof/object_node.rb
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
module Sof
|
||||||
|
|
||||||
|
# ObjectNode means node with structure
|
||||||
|
# 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 name , ref
|
||||||
|
super(ref)
|
||||||
|
@name = name
|
||||||
|
@simple = {}
|
||||||
|
@complex = {}
|
||||||
|
@super = nil # if derived from array or hash
|
||||||
|
end
|
||||||
|
|
||||||
|
# super is a hash or array, if the class of object derives from Hash/Array
|
||||||
|
def add_super s
|
||||||
|
@super = s
|
||||||
|
end
|
||||||
|
# attributes hold key value pairs
|
||||||
|
def add 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
|
||||||
|
|
||||||
|
# simple when no complex attributes and any
|
||||||
|
# possible super is also simple
|
||||||
|
def is_simple?
|
||||||
|
if( @referenced.nil? and @complex.empty? and head.length < 30 )
|
||||||
|
unless(@super and !@super.is_simple?)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
false
|
||||||
|
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 long_out io , level
|
||||||
|
io.write(head)
|
||||||
|
indent = " " * (level + 1)
|
||||||
|
@complex.each do |k,v|
|
||||||
|
io.write "\n#{indent}"
|
||||||
|
io.write ":#{k}"
|
||||||
|
io.write " "
|
||||||
|
v.out(io , level + 1)
|
||||||
|
end
|
||||||
|
if(@super)
|
||||||
|
io.write " "
|
||||||
|
@super.long_out(io,level)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def short_out io , level
|
||||||
|
io.write head
|
||||||
|
if(@super)
|
||||||
|
if( @super.is_simple? )
|
||||||
|
@super.short_out(io,level)
|
||||||
|
else
|
||||||
|
@super.long_out(io,level)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def head
|
||||||
|
body = @simple.collect {|a,val| ":#{a} => #{val.as_string(1)}"}.join(", ")
|
||||||
|
"#{@name}(#{body})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,16 +1,22 @@
|
|||||||
module Sof
|
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
|
class Occurence
|
||||||
def initialize object , level
|
def initialize object , level
|
||||||
@object = object
|
@object = object
|
||||||
@level = level
|
@level = level
|
||||||
@referenced = nil
|
@referenced = nil
|
||||||
|
@written = nil
|
||||||
end
|
end
|
||||||
def set_reference r
|
def set_reference r
|
||||||
|
raise "was set #{@referenced}" if @referenced
|
||||||
@referenced = r
|
@referenced = r
|
||||||
end
|
end
|
||||||
attr_reader :object , :referenced
|
attr_reader :object , :referenced
|
||||||
attr_accessor :level
|
attr_accessor :level , :written
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
30
lib/sof/simple_node.rb
Normal file
30
lib/sof/simple_node.rb
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
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
|
||||||
|
super(nil) # simple nodes can not be referenced, always value
|
||||||
|
@data = data
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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 short_out io , level
|
||||||
|
io.write(@data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@ -1,22 +1,35 @@
|
|||||||
module Sof
|
module Sof
|
||||||
|
|
||||||
|
# module for a couple of helpers that are needed in Members and Writer
|
||||||
|
|
||||||
module Util
|
module Util
|
||||||
|
# "value" is a property meaning simple/ not further structure
|
||||||
|
# hence int/bool/string etc are values
|
||||||
def is_value? o
|
def is_value? o
|
||||||
return true if o == true
|
return true if [true , false , nil].include?(o)
|
||||||
return true if o == false
|
return true if [Fixnum, Symbol, String, Class].include?(o.class)
|
||||||
return true if o == nil
|
if o.respond_to? :is_value?
|
||||||
return true if o.class == Fixnum
|
return true if o.is_value?
|
||||||
return true if o.class == Symbol
|
end
|
||||||
return true if o.class == String
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# extract an attribute by the given name from the object
|
||||||
|
# done with instance_variable_get
|
||||||
def get_value(object,name)
|
def get_value(object,name)
|
||||||
object.instance_variable_get "@#{name}".to_sym
|
object.instance_variable_get "@#{name}".to_sym
|
||||||
end
|
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
|
def attributes_for object
|
||||||
Sof::Util.attributes(object)
|
Sof::Util.attributes(object)
|
||||||
end
|
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 )
|
def self.attributes( object )
|
||||||
atts = object.instance_variables.collect{|i| i.to_s[1..-1].to_sym } # chop of @
|
atts = object.instance_variables.collect{|i| i.to_s[1..-1].to_sym } # chop of @
|
||||||
atts - Volotile.attributes(object.class)
|
atts - Volotile.attributes(object.class)
|
||||||
|
@ -1,11 +1,27 @@
|
|||||||
module Sof
|
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 = { }
|
@@mapping = { }
|
||||||
def self.attributes clazz
|
|
||||||
@@mapping[clazz] || []
|
# Add attributes that are then ommited from the sof writing process
|
||||||
end
|
# 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
|
def self.add clazz , attributes
|
||||||
@@mapping[clazz] = attributes
|
@@mapping[clazz] = attributes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# return the volotile attributes as an array (or empty array)
|
||||||
|
def self.attributes clazz
|
||||||
|
@@mapping[clazz] || []
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,10 +1,29 @@
|
|||||||
module Sof
|
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
|
class Writer
|
||||||
include Util
|
include Util
|
||||||
|
|
||||||
|
# Initialized with the Members (hash of occurences, see there)
|
||||||
def initialize members
|
def initialize members
|
||||||
@members = members
|
@members = members
|
||||||
end
|
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
|
def write
|
||||||
node = to_sof_node(@members.root , 0)
|
node = to_sof_node(@members.root , 0)
|
||||||
io = StringIO.new
|
io = StringIO.new
|
||||||
@ -12,48 +31,89 @@ module Sof
|
|||||||
io.string
|
io.string
|
||||||
end
|
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 becomes a ObjectNode
|
||||||
|
# Hash and Array create their own nodes via to_sof_node functions on the classes
|
||||||
def to_sof_node(object , level)
|
def to_sof_node(object , level)
|
||||||
if is_value?(object)
|
if is_value?(object)
|
||||||
return SimpleNode.new(object.to_sof())
|
return SimpleNode.new(object.to_sof())
|
||||||
end
|
end
|
||||||
occurence = @members.objects[object.object_id]
|
occurence = @members.objects[object.object_id]
|
||||||
raise "no object #{object}" unless occurence
|
raise "no object #{object}" unless occurence
|
||||||
if(level > occurence.level )
|
#puts "#{level} ? #{occurence.level} : ref #{occurence.referenced}"
|
||||||
|
if( occurence.referenced )
|
||||||
#puts "ref #{occurence.referenced} level #{level} at #{occurence.level}"
|
#puts "ref #{occurence.referenced} level #{level} at #{occurence.level}"
|
||||||
return SimpleNode.new("*#{occurence.referenced}")
|
return SimpleNode.new("->#{occurence.referenced}") unless (level == occurence.level )
|
||||||
end
|
if( occurence.written.nil? )
|
||||||
ref = occurence.referenced
|
occurence.written = true
|
||||||
if(object.respond_to? :to_sof_node) #mainly meant for arrays and hashes
|
|
||||||
object.to_sof_node(self , level , ref )
|
|
||||||
else
|
else
|
||||||
object_sof_node(object , level , ref )
|
return SimpleNode.new("->#{occurence.referenced}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def object_sof_node( object , level , ref)
|
ref = occurence.referenced
|
||||||
if( object.is_a? Class )
|
case object.class.name
|
||||||
return SimpleNode.new( object.name , ref )
|
when "Array" , "Parfait::List"
|
||||||
|
# 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_to_sof_node(object , level , ref )
|
||||||
|
when "Hash" , "Parfait::Dictionary"
|
||||||
|
# and hash keys/values
|
||||||
|
hash_to_sof_node( object , level , ref)
|
||||||
|
else
|
||||||
|
object_to_sof_node(object , level , ref)
|
||||||
end
|
end
|
||||||
head = object.class.name + "("
|
|
||||||
atts = {}
|
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)
|
||||||
|
#
|
||||||
|
# objects may be derived from array/hash. In that case the ObjectNode gets a super
|
||||||
|
# (either ArrayNode or HashNode)
|
||||||
|
def object_to_sof_node( object , level , ref)
|
||||||
|
node = ObjectNode.new(object.class.name , ref)
|
||||||
attributes_for(object).each() do |a|
|
attributes_for(object).each() do |a|
|
||||||
val = get_value(object , a)
|
val = get_value(object , a)
|
||||||
next if val.nil?
|
next if val.nil?
|
||||||
atts[a] = to_sof_node(val , level + 1)
|
node.add( a , to_sof_node( val , level + 1) )
|
||||||
end
|
end
|
||||||
immediate , extended = atts.partition {|a,val| val.is_a?(SimpleNode) }
|
#TODO get all superclsses here, but this covers 99% so . . moving on
|
||||||
head += immediate.collect {|a,val| "#{a.to_sof()} => #{val.as_string(level)}"}.join(", ") + ")"
|
superclasses = [object.class.superclass.name]
|
||||||
return SimpleNode.new(head) if( ref.nil? and extended.empty? and head.length < 30 )
|
if superclasses.include?( "Array") or superclasses.include?( "Parfait::List")
|
||||||
node = ObjectNode.new(head , ref)
|
node.add_super( array_to_sof_node(object , level , ref ) )
|
||||||
extended.each do |a , val|
|
end
|
||||||
node.add( to_sof_node(a,level + 1) , val )
|
if superclasses.include?( "Hash") or superclasses.include?( "Parfait::Dictionary")
|
||||||
|
node.add_super( hash_to_sof_node(object , level , ref ) )
|
||||||
end
|
end
|
||||||
node
|
node
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.write object
|
# Creates a ArrayNode (see there) for the Array.
|
||||||
writer = Writer.new(Members.new(object) )
|
# This mainly involves creating nodes for the children
|
||||||
writer.write
|
def array_to_sof_node(array , level , ref )
|
||||||
|
node = Sof::ArrayNode.new(ref)
|
||||||
|
array.each do |object|
|
||||||
|
node.add to_sof_node( object , level + 1)
|
||||||
|
end
|
||||||
|
node
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates a HashNode (see there) for the Hash.
|
||||||
|
# This mainly involves creating nodes for key value pairs
|
||||||
|
def hash_to_sof_node(hash , level , ref)
|
||||||
|
node = Sof::HashNode.new(ref)
|
||||||
|
hash.each do |key , object|
|
||||||
|
k = to_sof_node( key ,level + 1)
|
||||||
|
v = to_sof_node( object ,level + 1)
|
||||||
|
node.add(k , v)
|
||||||
|
end
|
||||||
|
node
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = 'salama-object-file'
|
s.name = 'salama-object-file'
|
||||||
s.version = '0.1'
|
s.version = '0.2.0'
|
||||||
|
|
||||||
s.authors = ['Torsten Ruger']
|
s.authors = ['Torsten Ruger']
|
||||||
s.email = 'torsten@villataika.fi'
|
s.email = 'torsten@villataika.fi'
|
||||||
|
@ -14,3 +14,22 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|||||||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'test'))
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'test'))
|
||||||
|
|
||||||
require 'salama-object-file'
|
require 'salama-object-file'
|
||||||
|
|
||||||
|
module Checker
|
||||||
|
def check should
|
||||||
|
out = Sof.write(@out)
|
||||||
|
same = (should == out)
|
||||||
|
puts "Shouldda\n#{out}" unless same
|
||||||
|
assert_equal should , out
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ObjectWithAttributes
|
||||||
|
def initialize
|
||||||
|
@name = "some name"
|
||||||
|
@number = 1234
|
||||||
|
end
|
||||||
|
attr_accessor :extra , :volotile
|
||||||
|
end
|
||||||
|
OBJECT_STRING = "ObjectWithAttributes(:name => 'some name', :number => 1234)"
|
||||||
|
Sof::Volotile.add(ObjectWithAttributes , [:volotile])
|
||||||
|
106
test/sof.rb
106
test/sof.rb
@ -1,106 +0,0 @@
|
|||||||
require_relative "helper"
|
|
||||||
require "yaml"
|
|
||||||
|
|
||||||
class ObjectWithAttributes
|
|
||||||
def initialize
|
|
||||||
@name = "some name"
|
|
||||||
@number = 1234
|
|
||||||
end
|
|
||||||
attr_accessor :extra
|
|
||||||
end
|
|
||||||
OBJECT_STRING = "ObjectWithAttributes(:name => 'some name', :number => 1234)"
|
|
||||||
|
|
||||||
class BasicSof < MiniTest::Test
|
|
||||||
def check should
|
|
||||||
same = (should == @out)
|
|
||||||
puts "Shouldda\n#{@out}" unless same
|
|
||||||
assert_equal should , @out
|
|
||||||
end
|
|
||||||
def test_true
|
|
||||||
@out = Sof::Writer.write(true)
|
|
||||||
check "true"
|
|
||||||
end
|
|
||||||
def test_num
|
|
||||||
@out = Sof::Writer.write(124)
|
|
||||||
check "124"
|
|
||||||
end
|
|
||||||
def test_simple_object
|
|
||||||
@out = Sof::Writer.write(ObjectWithAttributes.new)
|
|
||||||
check "#{OBJECT_STRING}"
|
|
||||||
end
|
|
||||||
def test_object_extra_array
|
|
||||||
object = ObjectWithAttributes.new
|
|
||||||
object.extra = [:sym , 123]
|
|
||||||
@out = Sof::Writer.write(object)
|
|
||||||
check "#{OBJECT_STRING}\n :extra [:sym, 123]"
|
|
||||||
end
|
|
||||||
def test_simple_array
|
|
||||||
@out = Sof::Writer.write([true, 1234])
|
|
||||||
check "[true, 1234]"
|
|
||||||
end
|
|
||||||
def test_array_object
|
|
||||||
@out = Sof::Writer.write([true, 1234 , ObjectWithAttributes.new])
|
|
||||||
check "-true\n-1234\n-#{OBJECT_STRING}"
|
|
||||||
end
|
|
||||||
def test_array_array
|
|
||||||
@out = Sof::Writer.write([true, 1 , [true , 12 ]])
|
|
||||||
check "-true\n-1\n-[true, 12]"
|
|
||||||
end
|
|
||||||
def test_array_array_reverse
|
|
||||||
@out = Sof::Writer.write([ [true , 12 ], true, 1])
|
|
||||||
check "-[true, 12]\n-true\n-1"
|
|
||||||
end
|
|
||||||
def test_array_array_array
|
|
||||||
@out = Sof::Writer.write([true, 1 , [true , 12 , [true , 123 ]]])
|
|
||||||
check "-true\n-1\n--true\n -12\n -[true, 123]"
|
|
||||||
end
|
|
||||||
def test_array_array_object
|
|
||||||
@out = Sof::Writer.write([true, 1 , [true , 12 , ObjectWithAttributes.new]])
|
|
||||||
check "-true\n-1\n--true\n -12\n -#{OBJECT_STRING}"
|
|
||||||
end
|
|
||||||
def test_simple_hash
|
|
||||||
@out = Sof::Writer.write({ one: 1 , tru: true })
|
|
||||||
check "{:one => 1, :tru => true}"
|
|
||||||
end
|
|
||||||
def test_hash_object
|
|
||||||
@out = Sof::Writer.write({ one: 1 , two: ObjectWithAttributes.new })
|
|
||||||
check "-:one => 1\n-:two => #{OBJECT_STRING}"
|
|
||||||
end
|
|
||||||
def test_array_hash
|
|
||||||
@out = Sof::Writer.write([true, 1 , { one: 1 , tru: true }])
|
|
||||||
check "-true\n-1\n-{:one => 1, :tru => true}"
|
|
||||||
end
|
|
||||||
def test_hash_array
|
|
||||||
@out = Sof::Writer.write({ one: [1 , ObjectWithAttributes.new] , two: true })
|
|
||||||
check "-:one => -1\n -#{OBJECT_STRING}\n-:two => true"
|
|
||||||
end
|
|
||||||
def test_array_recursive
|
|
||||||
ar = [true, 1 ]
|
|
||||||
ar << ar
|
|
||||||
@out = Sof::Writer.write(ar)
|
|
||||||
check "&1 [true, 1, *1]"
|
|
||||||
end
|
|
||||||
def test_object_recursive
|
|
||||||
object = ObjectWithAttributes.new
|
|
||||||
object.extra = object
|
|
||||||
@out = Sof::Writer.write(object)
|
|
||||||
check "&1 ObjectWithAttributes(:name => 'some name', :number => 1234, :extra => *1)"
|
|
||||||
end
|
|
||||||
def test_object_inline
|
|
||||||
object = ObjectWithAttributes.new
|
|
||||||
object.extra = Object.new
|
|
||||||
@out = Sof::Writer.write(object)
|
|
||||||
check "ObjectWithAttributes(:name => 'some name', :number => 1234, :extra => Object())"
|
|
||||||
end
|
|
||||||
def test_class
|
|
||||||
@out = Sof::Writer.write(ObjectWithAttributes)
|
|
||||||
check "ObjectWithAttributes"
|
|
||||||
end
|
|
||||||
def test_class_ref
|
|
||||||
object = ObjectWithAttributes.new
|
|
||||||
object.extra = ObjectWithAttributes
|
|
||||||
ar = [object , ObjectWithAttributes]
|
|
||||||
@out = Sof::Writer.write(ar)
|
|
||||||
check "-ObjectWithAttributes(:name => 'some name', :number => 1234, :extra => *1)\n-&1 ObjectWithAttributes"
|
|
||||||
end
|
|
||||||
end
|
|
6
test/test_all.rb
Normal file
6
test/test_all.rb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
require_relative "test_basic"
|
||||||
|
require_relative "test_object"
|
||||||
|
require_relative "test_ext"
|
||||||
|
require_relative "test_refs"
|
||||||
|
require_relative "test_super"
|
||||||
|
require_relative "test_names"
|
48
test/test_basic.rb
Normal file
48
test/test_basic.rb
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
require_relative "helper"
|
||||||
|
|
||||||
|
class BasicSof < MiniTest::Test
|
||||||
|
include Checker
|
||||||
|
|
||||||
|
def test_true
|
||||||
|
@out = true
|
||||||
|
check "true"
|
||||||
|
end
|
||||||
|
def test_string
|
||||||
|
@out = "true"
|
||||||
|
check "'true'"
|
||||||
|
end
|
||||||
|
def test_num
|
||||||
|
@out = 124
|
||||||
|
check "124"
|
||||||
|
end
|
||||||
|
def test_simple_array
|
||||||
|
@out = [true, 1234]
|
||||||
|
check "[true, 1234]"
|
||||||
|
end
|
||||||
|
def test_array_array
|
||||||
|
@out = [true, 1 , [true , 12 ]]
|
||||||
|
check "[true, 1, [true, 12]]"
|
||||||
|
end
|
||||||
|
def test_array_array_reverse
|
||||||
|
@out = [ [true , 12 ], true, 1]
|
||||||
|
check "[[true, 12], true, 1]"
|
||||||
|
end
|
||||||
|
def test_array_array_array
|
||||||
|
@out = [true, 1 , [true , 12 , [true , 123 ]]]
|
||||||
|
check "[true, 1, [true, 12, [true, 123]]]"
|
||||||
|
end
|
||||||
|
def test_simple_hash
|
||||||
|
@out = { :one => 1 , :tru => true }
|
||||||
|
check "{:one => 1, :tru => true}"
|
||||||
|
end
|
||||||
|
def test_array_hash
|
||||||
|
@out = [true, 1 , { :one => 1 , :tru => true }]
|
||||||
|
check "[true, 1, {:one => 1, :tru => true}]"
|
||||||
|
end
|
||||||
|
def test_array_recursive
|
||||||
|
ar = [true, 1 ]
|
||||||
|
ar << ar
|
||||||
|
@out = ar
|
||||||
|
check "&1 [true, 1, ->1]"
|
||||||
|
end
|
||||||
|
end
|
31
test/test_ext.rb
Normal file
31
test/test_ext.rb
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
require_relative "helper"
|
||||||
|
|
||||||
|
class FailValue
|
||||||
|
def initialize str
|
||||||
|
@name = str
|
||||||
|
end
|
||||||
|
def is_value?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
class BasicValue < FailValue
|
||||||
|
def to_sof
|
||||||
|
"'#{@name}'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ObjectSof < MiniTest::Test
|
||||||
|
include Checker
|
||||||
|
|
||||||
|
def test_to_sof
|
||||||
|
assert_raises NoMethodError do
|
||||||
|
Sof::Writer.write(FailValue.new("name"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_basic
|
||||||
|
@out = BasicValue.new("name")
|
||||||
|
check "'name'"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
29
test/test_names.rb
Normal file
29
test/test_names.rb
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
require_relative "helper"
|
||||||
|
class NamedRef < ObjectWithAttributes
|
||||||
|
def initialize name
|
||||||
|
super()
|
||||||
|
@name = name
|
||||||
|
end
|
||||||
|
def sof_reference_name
|
||||||
|
@name.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class NamedTest < MiniTest::Test
|
||||||
|
include Checker
|
||||||
|
|
||||||
|
def test_object_one
|
||||||
|
object = NamedRef.new("one")
|
||||||
|
object.extra = [object]
|
||||||
|
@out = [ {:one => object} , object ]
|
||||||
|
check "- {:one => ->one}\n- &one NamedRef(:name => 'one', :number => 1234, :extra => [->one])"
|
||||||
|
end
|
||||||
|
def test_object_two
|
||||||
|
object = NamedRef.new("one")
|
||||||
|
object2 = NamedRef.new("two")
|
||||||
|
object.extra = [object2]
|
||||||
|
@out = [ {:one => object} , object2 ]
|
||||||
|
check "- - :one => NamedRef(:name => 'one', :number => 1234, :extra => [->two])\n- &two NamedRef(:name => 'two', :number => 1234)"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
60
test/test_object.rb
Normal file
60
test/test_object.rb
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
require_relative "helper"
|
||||||
|
|
||||||
|
class ObjectSof < MiniTest::Test
|
||||||
|
include Checker
|
||||||
|
|
||||||
|
def test_simple_object
|
||||||
|
@out = ObjectWithAttributes.new
|
||||||
|
check "#{OBJECT_STRING}"
|
||||||
|
end
|
||||||
|
def test_object_extra_array
|
||||||
|
object = ObjectWithAttributes.new
|
||||||
|
object.extra = [:sym , 123]
|
||||||
|
@out = object
|
||||||
|
check "ObjectWithAttributes(:name => 'some name', :number => 1234, :extra => [:sym, 123])"
|
||||||
|
end
|
||||||
|
def test_array_object
|
||||||
|
@out = [true, 1234 , ObjectWithAttributes.new]
|
||||||
|
check "- true\n- 1234\n- #{OBJECT_STRING}"
|
||||||
|
end
|
||||||
|
def test_array_array_object
|
||||||
|
@out = [true, 1 , [true , 12 , ObjectWithAttributes.new]]
|
||||||
|
check "- true\n- 1\n- - true\n - 12\n - #{OBJECT_STRING}"
|
||||||
|
end
|
||||||
|
def test_hash_object
|
||||||
|
@out = { :one => 1 , :two => ObjectWithAttributes.new }
|
||||||
|
check "- :one => 1\n- :two => #{OBJECT_STRING}"
|
||||||
|
end
|
||||||
|
def test_hash_array
|
||||||
|
@out = { :one => [1 , ObjectWithAttributes.new] , :two => true }
|
||||||
|
check "- :one => - 1\n - #{OBJECT_STRING}\n- :two => true"
|
||||||
|
end
|
||||||
|
def test_object_recursive
|
||||||
|
object = ObjectWithAttributes.new
|
||||||
|
object.extra = object
|
||||||
|
@out = object
|
||||||
|
check "&1 ObjectWithAttributes(:name => 'some name', :number => 1234, :extra => ->1)"
|
||||||
|
end
|
||||||
|
def test_object_inline
|
||||||
|
object = ObjectWithAttributes.new
|
||||||
|
object.extra = Object.new
|
||||||
|
@out = object
|
||||||
|
check "ObjectWithAttributes(:name => 'some name', :number => 1234, :extra => Object())"
|
||||||
|
end
|
||||||
|
def test_volotile
|
||||||
|
@out = ObjectWithAttributes.new
|
||||||
|
@out.volotile = 42
|
||||||
|
check "#{OBJECT_STRING}"
|
||||||
|
end
|
||||||
|
def test_class
|
||||||
|
@out = ObjectWithAttributes
|
||||||
|
check "ObjectWithAttributes"
|
||||||
|
end
|
||||||
|
def test_class_ref
|
||||||
|
object = ObjectWithAttributes.new
|
||||||
|
object.extra = ObjectWithAttributes
|
||||||
|
ar = [object , ObjectWithAttributes]
|
||||||
|
@out = ar
|
||||||
|
check "- ObjectWithAttributes(:name => 'some name', :number => 1234, :extra => ObjectWithAttributes)\n- ObjectWithAttributes"
|
||||||
|
end
|
||||||
|
end
|
42
test/test_refs.rb
Normal file
42
test/test_refs.rb
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
require_relative "helper"
|
||||||
|
|
||||||
|
class TestRefs < MiniTest::Test
|
||||||
|
include Checker
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@hash = {}
|
||||||
|
@array =[]
|
||||||
|
end
|
||||||
|
def fill_some
|
||||||
|
@hash[:some] = @array
|
||||||
|
@array[1] = :one
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_one_empty
|
||||||
|
@out = @array << @hash
|
||||||
|
check "[{}]"
|
||||||
|
end
|
||||||
|
def test_two_empty
|
||||||
|
@out = @array << @hash
|
||||||
|
@out << @hash
|
||||||
|
check "[&1 {}, ->1]"
|
||||||
|
end
|
||||||
|
def test_bigger
|
||||||
|
@out = [ { :one => @array , :two => [{ :three => @array}] } ]
|
||||||
|
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 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 => ObjectWithAttributes(:name => 'some name', :number => 1234, :extra => [->2])\n- &2 ObjectWithAttributes(:name => 'some name', :number => 1234)"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
54
test/test_super.rb
Normal file
54
test/test_super.rb
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
require_relative "helper"
|
||||||
|
|
||||||
|
class ASuper < Array
|
||||||
|
def initialize object
|
||||||
|
@object = object
|
||||||
|
end
|
||||||
|
attr_accessor :object
|
||||||
|
end
|
||||||
|
class HSuper < Hash
|
||||||
|
def initialize object
|
||||||
|
@object = object
|
||||||
|
end
|
||||||
|
attr_accessor :object
|
||||||
|
end
|
||||||
|
class TestSuper < MiniTest::Test
|
||||||
|
include Checker
|
||||||
|
|
||||||
|
def test_asuper_empty
|
||||||
|
@out = ASuper.new( [] )
|
||||||
|
check "ASuper(:object => [])[]"
|
||||||
|
end
|
||||||
|
def test_asuper_with_array
|
||||||
|
@out = ASuper.new( [1,2,3] )
|
||||||
|
check "ASuper(:object => [1, 2, 3])[]"
|
||||||
|
end
|
||||||
|
def test_asuper_as_array
|
||||||
|
@out = ASuper.new( nil )
|
||||||
|
@out << 1 << 2 << 3
|
||||||
|
check "ASuper()[1, 2, 3]"
|
||||||
|
end
|
||||||
|
def test_asuper_as_big_array
|
||||||
|
@out = ASuper.new( nil )
|
||||||
|
@out << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8
|
||||||
|
check "ASuper() - 1\n- 2\n- 3\n- 4\n- 5\n- 6\n- 7\n- 8"
|
||||||
|
end
|
||||||
|
def test_asuper_self_ref
|
||||||
|
@out = ASuper.new( self )
|
||||||
|
@out.object = @out
|
||||||
|
check "&1 ASuper(:object => ->1) "
|
||||||
|
end
|
||||||
|
def test_asuper_indirect_ref
|
||||||
|
object = ObjectWithAttributes.new
|
||||||
|
@out = ASuper.new( object )
|
||||||
|
object.extra = @out
|
||||||
|
@out << 1 << 2
|
||||||
|
check "&1 ASuper()\n :object ObjectWithAttributes(:name => 'some name', :number => 1234, :extra => ->1) - 1\n- 2"
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_hsuper_empty
|
||||||
|
@out = HSuper.new( {} )
|
||||||
|
check "HSuper(:object => {}){}"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Reference in New Issue
Block a user