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"
|
||||
|
||||
gem "rake"
|
||||
|
||||
gem "salama-object-file" , :path => "."
|
||||
|
||||
|
@ -1,12 +1,13 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
salama-object-file (0.1)
|
||||
salama-object-file (0.2.0)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
minitest (5.6.1)
|
||||
rake (10.4.2)
|
||||
rubygems-tasks (0.2.4)
|
||||
|
||||
PLATFORMS
|
||||
@ -14,5 +15,6 @@ PLATFORMS
|
||||
|
||||
DEPENDENCIES
|
||||
minitest
|
||||
rake
|
||||
rubygems-tasks
|
||||
salama-object-file!
|
||||
|
26
README.md
26
README.md
@ -8,10 +8,11 @@ Hence the need for a code/object file format
|
||||
I started with yaml, which is nice in that it has a solid implementation, reads and writes,
|
||||
handles arbitrary objects, handles graphs and is a sort of readable text format.
|
||||
|
||||
But the "sort of" started to get to me, because
|
||||
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.
|
||||
- 3) has no concept of dumping only parts of an object
|
||||
|
||||
To fix this i started on Sof, with an eye to expand it.
|
||||
|
||||
@ -21,11 +22,18 @@ The main starting goal was quite like yaml, but with
|
||||
- also short versions of arrays and hashes
|
||||
- Shorter class names (no ruby/object or even ruby/struct stuff)
|
||||
- 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
|
||||
|
||||
Ok, so we all heard about object files, it's the things compilers create so we don't have to have
|
||||
Ok, so we all heard about object files, it's the things compilers create so we don't have to have
|
||||
huge compiles and can link them later.
|
||||
|
||||
Much fewer know what they include, and that is not because they are not very useful,
|
||||
@ -33,13 +41,13 @@ but rather very complicated.
|
||||
|
||||
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 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
|
||||
compile and run on the same object machine.
|
||||
Not neccessarily my idea, but i haven't seen it pulled off. Not that i will.
|
||||
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.
|
||||
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!!
|
||||
|
||||
|
@ -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/node"
|
||||
require_relative "sof/simple_node"
|
||||
require_relative "sof/object_node"
|
||||
require_relative "sof/members"
|
||||
require_relative "sof/volotile"
|
||||
require_relative "sof/writer"
|
||||
require_relative "sof/array"
|
||||
require_relative "sof/hash"
|
||||
require_relative "sof/array_node"
|
||||
require_relative "sof/hash_node"
|
||||
require_relative "sof/occurence"
|
||||
|
||||
Class.class_eval do
|
||||
def to_sof
|
||||
self.name
|
||||
end
|
||||
end
|
||||
Symbol.class_eval do
|
||||
def to_sof()
|
||||
":#{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
|
||||
|
||||
|
||||
# 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
|
||||
@references = []
|
||||
@objects = {}
|
||||
@referenced = false
|
||||
add(root , 0)
|
||||
add_object( root , 0)
|
||||
collect_level(0 , [root])
|
||||
end
|
||||
attr_reader :objects , :root , :referenced
|
||||
|
||||
def add object , level
|
||||
return if is_value?(object)
|
||||
attr_reader :objects , :root
|
||||
|
||||
private
|
||||
# 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] )
|
||||
#puts "reset level #{level} at #{occurence.level}"
|
||||
#puts "reset level #{level} at #{occurence.level}" if occurence.referenced #== 19
|
||||
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)
|
||||
@counter = @counter + 1
|
||||
# puts "referencing #{@counter} #{occurence.object.name}, at level #{level}/#{occurence.level} " if @counter == 23
|
||||
# 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
|
||||
end
|
||||
else
|
||||
reference = @counter.to_s
|
||||
@counter = @counter + 1
|
||||
end
|
||||
occurence.set_reference(reference)
|
||||
@references << reference
|
||||
end
|
||||
return
|
||||
return nil
|
||||
end
|
||||
o = Occurence.new( object , level )
|
||||
@objects[object.object_id] = o
|
||||
attributes = attributes_for(object)
|
||||
attributes.each do |a|
|
||||
val = get_value( object , a)
|
||||
add(val , level + 1)
|
||||
end
|
||||
if( object.is_a? Array )
|
||||
object.each do |a|
|
||||
add(a , level + 1)
|
||||
# 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.each do |a|
|
||||
val = get_value( object , a)
|
||||
next_level << val
|
||||
end
|
||||
#TODO get all superclsses here, but this covers 99% so . . moving on
|
||||
superclasses = [object.class.superclass.name]
|
||||
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
|
||||
if( object.is_a? Hash )
|
||||
object.each do |a,b|
|
||||
add(a , level + 1)
|
||||
add(b , level + 1)
|
||||
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
|
||||
|
@ -1,58 +1,65 @@
|
||||
# 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
|
||||
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
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
|
||||
# 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
|
||||
|
||||
class SimpleNode < Node
|
||||
def initialize data , ref = nil
|
||||
super(ref)
|
||||
@data = data
|
||||
|
||||
private
|
||||
def short_out io , level
|
||||
raise "abstact function short_out called for #{self}"
|
||||
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
|
||||
|
||||
def long_out io , level
|
||||
raise "abstact function long_out called for #{self}"
|
||||
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
|
||||
|
||||
# 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
|
||||
@level = level
|
||||
@referenced = nil
|
||||
@referenced = nil
|
||||
@written = nil
|
||||
end
|
||||
def set_reference r
|
||||
raise "was set #{@referenced}" if @referenced
|
||||
@referenced = r
|
||||
end
|
||||
attr_reader :object , :referenced
|
||||
attr_accessor :level
|
||||
attr_accessor :level , :written
|
||||
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 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 o == true
|
||||
return true if o == false
|
||||
return true if o == nil
|
||||
return true if o.class == Fixnum
|
||||
return true if o.class == Symbol
|
||||
return true if o.class == String
|
||||
return true if [true , false , nil].include?(o)
|
||||
return true if [Fixnum, Symbol, String, Class].include?(o.class)
|
||||
if o.respond_to? :is_value?
|
||||
return true if o.is_value?
|
||||
end
|
||||
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,49 +31,90 @@ 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 becomes 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())
|
||||
end
|
||||
occurence = @members.objects[object.object_id]
|
||||
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}"
|
||||
return SimpleNode.new("*#{occurence.referenced}")
|
||||
return SimpleNode.new("->#{occurence.referenced}") unless (level == occurence.level )
|
||||
if( occurence.written.nil? )
|
||||
occurence.written = true
|
||||
else
|
||||
return SimpleNode.new("->#{occurence.referenced}")
|
||||
end
|
||||
end
|
||||
|
||||
ref = occurence.referenced
|
||||
if(object.respond_to? :to_sof_node) #mainly meant for arrays and hashes
|
||||
object.to_sof_node(self , level , ref )
|
||||
case object.class.name
|
||||
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_sof_node(object , level , ref )
|
||||
object_to_sof_node(object , level , ref)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def object_sof_node( object , level , ref)
|
||||
if( object.is_a? Class )
|
||||
return SimpleNode.new( object.name , ref )
|
||||
end
|
||||
head = object.class.name + "("
|
||||
atts = {}
|
||||
attributes_for(object).each() do |a|
|
||||
# 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|
|
||||
val = get_value(object , a)
|
||||
next if val.nil?
|
||||
atts[a] = to_sof_node(val , level + 1)
|
||||
node.add( 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)}"}.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 )
|
||||
#TODO get all superclsses here, but this covers 99% so . . moving on
|
||||
superclasses = [object.class.superclass.name]
|
||||
if superclasses.include?( "Array") or superclasses.include?( "Parfait::List")
|
||||
node.add_super( array_to_sof_node(object , level , ref ) )
|
||||
end
|
||||
if superclasses.include?( "Hash") or superclasses.include?( "Parfait::Dictionary")
|
||||
node.add_super( hash_to_sof_node(object , level , ref ) )
|
||||
end
|
||||
node
|
||||
end
|
||||
|
||||
def self.write object
|
||||
writer = Writer.new(Members.new(object) )
|
||||
writer.write
|
||||
# Creates a ArrayNode (see there) for the Array.
|
||||
# This mainly involves creating nodes for the children
|
||||
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
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'salama-object-file'
|
||||
s.version = '0.1'
|
||||
s.version = '0.2.0'
|
||||
|
||||
s.authors = ['Torsten Ruger']
|
||||
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'))
|
||||
|
||||
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