grayswx-dynastruct 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +12 -8
- data/lib/dynastruct.rb +16 -0
- data/lib/dynastruct/field.rb +78 -0
- data/lib/dynastruct/key.rb +164 -0
- data/lib/dynastruct/struct.rb +32 -0
- data/test/dynastruct/field_test.rb +80 -0
- data/test/dynastruct/key_test.rb +167 -0
- data/test/dynastruct/struct_test.rb +90 -0
- data/test/test_helper.rb +2 -2
- metadata +11 -12
- data/dynastruct.rb +0 -3
- data/lib/d_exceptions.rb +0 -5
- data/lib/d_field.rb +0 -78
- data/lib/d_key.rb +0 -165
- data/lib/d_struct.rb +0 -36
- data/test/d_field_test.rb +0 -78
- data/test/d_key_test.rb +0 -167
- data/test/d_struct_test.rb +0 -88
data/Rakefile
CHANGED
@@ -1,5 +1,10 @@
|
|
1
|
-
%w'
|
2
|
-
|
1
|
+
%w'
|
2
|
+
lib/dynastruct
|
3
|
+
rubygems
|
4
|
+
rake/clean
|
5
|
+
rake/testtask
|
6
|
+
rake/gempackagetask
|
7
|
+
'.each {|w| require w }
|
3
8
|
|
4
9
|
task :default => [:test]
|
5
10
|
|
@@ -9,20 +14,19 @@ end
|
|
9
14
|
|
10
15
|
spec = Gem::Specification.new do |s|
|
11
16
|
s.name = 'dynastruct'
|
12
|
-
s.version = '0.0.
|
13
|
-
s.summary = 'Structs that
|
17
|
+
s.version = '0.0.4'
|
18
|
+
s.summary = 'Structs that change based upon their contents.'
|
14
19
|
s.author = 'grayswx'
|
15
20
|
s.homepage = 'http://github.com/grayswx/dynastruct/tree/master'
|
16
21
|
s.rubyforge_project = 'n/a'
|
17
22
|
s.email = 'grayswx@gmail.com'
|
18
|
-
s.files = FileList['[A-Z]*', '
|
19
|
-
s.require_paths = ['.', 'lib']
|
23
|
+
s.files = FileList['[A-Z]*', '{lib,test}/**/*']
|
20
24
|
s.has_rdoc = true
|
21
25
|
end
|
22
26
|
|
23
27
|
Rake::GemPackageTask.new spec do |pkg|
|
24
|
-
|
25
|
-
|
28
|
+
pkg.need_tar = true
|
29
|
+
pkg.need_zip = true
|
26
30
|
end
|
27
31
|
|
28
32
|
desc "Generate a gemspec file for GitHub"
|
data/lib/dynastruct.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$:.unshift $_ unless $:.include?($_ = File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module Dynastruct
|
4
|
+
|
5
|
+
class NoProcError < StandardError; end
|
6
|
+
class ValidationError < StandardError; end
|
7
|
+
class KeyError < StandardError; end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
%w'
|
12
|
+
dynastruct/field.rb
|
13
|
+
dynastruct/key.rb
|
14
|
+
dynastruct/struct.rb
|
15
|
+
'.each {|l| require l }
|
16
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Dynastruct
|
2
|
+
class Field
|
3
|
+
|
4
|
+
# Creates a new kind of Field with the given validators.
|
5
|
+
def initialize( *validators )
|
6
|
+
@validators = validators or []
|
7
|
+
end
|
8
|
+
|
9
|
+
##### Usage Methods
|
10
|
+
|
11
|
+
# Tests whether or not the given object is a valid object for this field
|
12
|
+
def valid?( target )
|
13
|
+
@validators.all? {|v| v[target]}
|
14
|
+
end
|
15
|
+
|
16
|
+
# Reads an object of this field type from an IO stream.
|
17
|
+
def read( ios )
|
18
|
+
raise NoProcError, "Field type cannot read." unless @reader
|
19
|
+
@reader[ios]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Writes an object to an IO stream.
|
23
|
+
def write( object, ios )
|
24
|
+
raise NoProcError, "Field type cannot write." unless @writer
|
25
|
+
@writer[object, ios]
|
26
|
+
end
|
27
|
+
|
28
|
+
##### Creation Methods
|
29
|
+
|
30
|
+
# Adds a validator to the object.
|
31
|
+
def add_validator( &validator )
|
32
|
+
@validators << validator
|
33
|
+
end
|
34
|
+
|
35
|
+
# Sets the reader for this field.
|
36
|
+
def reader( &proc )
|
37
|
+
@reader = proc
|
38
|
+
end
|
39
|
+
|
40
|
+
# Sets the writer for this field.
|
41
|
+
def writer( &proc )
|
42
|
+
@writer = proc
|
43
|
+
end
|
44
|
+
|
45
|
+
##### Shortcut creation methods.
|
46
|
+
|
47
|
+
# Makes a shortcut field creator of the given type.
|
48
|
+
def self.make_shortcut( name, test, validator )
|
49
|
+
temp, $bypass = $bypass, [name, test, validator]
|
50
|
+
class << self
|
51
|
+
make_shortcut(*$bypass)
|
52
|
+
end
|
53
|
+
$bypass = temp
|
54
|
+
end
|
55
|
+
class << self
|
56
|
+
def self.make_shortcut( name, test, validator )
|
57
|
+
define_method "of_#{name}".to_sym do |type|
|
58
|
+
fieldvar = "@fields_of_#{name}".to_sym
|
59
|
+
intern_hash = instance_variable_get fieldvar
|
60
|
+
unless intern_hash
|
61
|
+
intern_hash = {}
|
62
|
+
instance_variable_set fieldvar, intern_hash
|
63
|
+
end
|
64
|
+
raise ArgumentError, 'Wrong type provided.' unless test[type]
|
65
|
+
intern_hash[type] ||= self.new(lambda {|o| validator[type, o]})
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
make_shortcut('class',
|
71
|
+
lambda {|t| t.kind_of? Class },
|
72
|
+
lambda {|t,o| o.kind_of? t })
|
73
|
+
make_shortcut('method',
|
74
|
+
lambda {|t| t.kind_of? Symbol },
|
75
|
+
lambda {|t,o| o.respond_to? t })
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module Dynastruct
|
2
|
+
class Key < Field
|
3
|
+
|
4
|
+
Tuple = ::Struct.new :index, :hooks, :type
|
5
|
+
|
6
|
+
# Creates a new Key.
|
7
|
+
# Fields is a list of fields which trigger the test.
|
8
|
+
def initialize( parent = nil, *fields, &test )
|
9
|
+
|
10
|
+
super(lambda do |struct|
|
11
|
+
struct.key.ancestor? self
|
12
|
+
end)
|
13
|
+
reader do |ios|
|
14
|
+
struct = Struct.new self
|
15
|
+
# Must do a manual loop here because size can change on the fly.
|
16
|
+
index = 0
|
17
|
+
while index < struct.key.size do
|
18
|
+
struct[index] = struct.key[index].read ios
|
19
|
+
index += 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
writer do |struct, ios|
|
23
|
+
struct.each_index do |i|
|
24
|
+
struct.key[i].write struct[i], ios
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
@fields = {}
|
29
|
+
@fields_i = []
|
30
|
+
@children = []
|
31
|
+
if parent then
|
32
|
+
@size = parent.size
|
33
|
+
@parent = parent
|
34
|
+
@test = test
|
35
|
+
parent.add_child self
|
36
|
+
parent.add_hooks fields, self
|
37
|
+
else
|
38
|
+
raise ArgumentError, "Cannot transform with no parent." if test or not fields.empty?
|
39
|
+
@size = 0
|
40
|
+
@parent = nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
##### Usage Methods
|
45
|
+
|
46
|
+
attr_reader :size, :parent
|
47
|
+
|
48
|
+
# Returns the index of a field name or index.
|
49
|
+
# Also makes sure that the field exists.
|
50
|
+
def index( name )
|
51
|
+
# If an index.
|
52
|
+
if name.kind_of? Integer then
|
53
|
+
raise KeyError, 'Key out of bounds.' unless name >= 0 and name < size
|
54
|
+
return name
|
55
|
+
end
|
56
|
+
# If a String or Symbol.
|
57
|
+
name = name.to_sym
|
58
|
+
field = @fields[name]
|
59
|
+
return field.index if field
|
60
|
+
return @parent.index(name) if @parent
|
61
|
+
raise KeyError, 'Key does not exist.'
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns a hash of names to field types.
|
65
|
+
def fields()
|
66
|
+
base = @parent ? @parent.fields : {}
|
67
|
+
@fields.each do |key, val|
|
68
|
+
base[key] = val.type
|
69
|
+
end
|
70
|
+
base
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the field with the given index or name.
|
74
|
+
def field( field )
|
75
|
+
field = index_to_field field if field.kind_of? Integer
|
76
|
+
@fields[field.to_sym].type
|
77
|
+
end
|
78
|
+
alias :[] :field
|
79
|
+
|
80
|
+
# Returns the children of this key.
|
81
|
+
def children()
|
82
|
+
@children.dup
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns whether or not the given key is an ancestor.
|
86
|
+
def ancestor?( key )
|
87
|
+
return true if self == key
|
88
|
+
return @parent.ancestor? key if @parent
|
89
|
+
false
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns the new key that results from assigning
|
93
|
+
# a given value to a given field.
|
94
|
+
# A ValidationError occurs if the assignment is not valid.
|
95
|
+
def update( struct, field, value )
|
96
|
+
field = index_to_field field if field.kind_of? Integer
|
97
|
+
field = field.to_sym
|
98
|
+
raise KeyError, 'Key not present' unless @fields.key? field
|
99
|
+
field = @fields[field]
|
100
|
+
if field.type.valid? value then
|
101
|
+
yield field.index
|
102
|
+
return catch(:hook_match) do
|
103
|
+
field.hooks.each do |key|
|
104
|
+
throw :hook_match, key if key.test_hook struct
|
105
|
+
end
|
106
|
+
self
|
107
|
+
end
|
108
|
+
end
|
109
|
+
raise ValidationError, 'Value not valid.'
|
110
|
+
end
|
111
|
+
|
112
|
+
##### Helper Methods
|
113
|
+
|
114
|
+
# Converts an index to a field name.
|
115
|
+
def index_to_field( index )
|
116
|
+
raise KeyError, 'Key out of bounds' if index < 0 or index >= size
|
117
|
+
psize = @parent ? @parent.size : 0
|
118
|
+
return @parent.index_to_field index if index < psize
|
119
|
+
@fields_i[index - psize]
|
120
|
+
end
|
121
|
+
|
122
|
+
# Increments this and child keys' indices by 1.
|
123
|
+
def increment_indices()
|
124
|
+
@size += 1
|
125
|
+
@fields.each do |key, tuple|
|
126
|
+
tuple.index += 1
|
127
|
+
end
|
128
|
+
@children.each do |child|
|
129
|
+
child.increment_indices
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Tests if the struct meets the specified criteria for
|
134
|
+
# transforming to this key.
|
135
|
+
def test_hook( struct )
|
136
|
+
@test[struct]
|
137
|
+
end
|
138
|
+
|
139
|
+
##### Creation Methods
|
140
|
+
|
141
|
+
# Registers a field with a name.
|
142
|
+
def register( name, field )
|
143
|
+
name = name.to_sym
|
144
|
+
@fields[name] = Tuple.new @size, [], field
|
145
|
+
@fields_i[@size] = name
|
146
|
+
@size += 1
|
147
|
+
@children.each {|c| c.increment_indices }
|
148
|
+
end
|
149
|
+
|
150
|
+
# Adds a child to this key.
|
151
|
+
def add_child( child )
|
152
|
+
@children << child
|
153
|
+
end
|
154
|
+
|
155
|
+
# Adds hooks to this key.
|
156
|
+
def add_hooks( hooks, key )
|
157
|
+
hooks.each do |hook|
|
158
|
+
@fields[hook.to_sym].hooks << key
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Dynastruct
|
2
|
+
class Struct
|
3
|
+
|
4
|
+
def initialize( key )
|
5
|
+
@key = key
|
6
|
+
@data = []
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :key
|
10
|
+
|
11
|
+
# Gets the element of the struct.
|
12
|
+
def []( field )
|
13
|
+
@data[@key.index field]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Assigns the element of the struct.
|
17
|
+
def []=( field, value )
|
18
|
+
@key = @key.update self, field, value do |i|
|
19
|
+
@data[i] = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def each( &proc )
|
24
|
+
@data.each &proc
|
25
|
+
end
|
26
|
+
|
27
|
+
def each_index( &proc )
|
28
|
+
@data.each_index &proc
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require File.join(File.dirname(__FILE__), '../test_helper')
|
3
|
+
module Dynastruct
|
4
|
+
class FieldTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
context 'A new Field' do
|
7
|
+
|
8
|
+
setup do
|
9
|
+
@field = Field.new
|
10
|
+
end
|
11
|
+
should 'validate anything' do
|
12
|
+
@field.valid?(nil).should be true
|
13
|
+
@field.valid?(42).should be true
|
14
|
+
end
|
15
|
+
should 'not be able to read' do
|
16
|
+
lambda{@field.read(StringIO.new)}.should raise_error NoProcError
|
17
|
+
end
|
18
|
+
should 'not be able to write' do
|
19
|
+
lambda{@field.write('', StringIO.new)}.should raise_error NoProcError
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with a direct writer' do
|
23
|
+
setup do
|
24
|
+
@field.writer {|v,o| o.write v }
|
25
|
+
end
|
26
|
+
should 'write out "abc"' do
|
27
|
+
ios = StringIO.new
|
28
|
+
@field.write 'abc', ios
|
29
|
+
ios.string.should be 'abc'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with a direct word reader' do
|
34
|
+
setup do
|
35
|
+
@field.reader {|i| i.gets(' ').strip }
|
36
|
+
end
|
37
|
+
should 'read in "abc"' do
|
38
|
+
ios = StringIO.new 'abc'
|
39
|
+
@field.read(ios).should be 'abc'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'A new Field of class Integer' do
|
46
|
+
setup do
|
47
|
+
@field = Field.of_class Integer
|
48
|
+
end
|
49
|
+
should "validate Integers." do
|
50
|
+
@field.valid?(42).should be true
|
51
|
+
@field.valid?(0).should be true
|
52
|
+
end
|
53
|
+
should "not validate non Integers." do
|
54
|
+
@field.valid?('hello').should be false
|
55
|
+
@field.valid?(45.5).should be false
|
56
|
+
end
|
57
|
+
should 'intern' do
|
58
|
+
@field.should be Field.of_class Integer
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'A new Field of method :to_str' do
|
63
|
+
setup do
|
64
|
+
@field = Field.of_method :to_str
|
65
|
+
end
|
66
|
+
should 'validate Strings.' do
|
67
|
+
@field.valid?('hello').should be true
|
68
|
+
@field.valid?('').should be true
|
69
|
+
end
|
70
|
+
should 'not validate Numerics.' do
|
71
|
+
@field.valid?(42).should be false
|
72
|
+
@field.valid?(1.5).should be false
|
73
|
+
end
|
74
|
+
should 'intern' do
|
75
|
+
@field.should be Field.of_method :to_str
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
### XXX Test the read / write actions. Maybe in Struct?
|
3
|
+
require File.join(File.dirname(__FILE__), '../test_helper')
|
4
|
+
module Dynastruct
|
5
|
+
class DKeyTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
context 'A new Key' do
|
8
|
+
|
9
|
+
setup { @key = Key.new }
|
10
|
+
|
11
|
+
should 'have no children' do
|
12
|
+
@key.children.empty?.should be true
|
13
|
+
end
|
14
|
+
should 'have no parent' do
|
15
|
+
@key.parent.should be nil
|
16
|
+
end
|
17
|
+
should 'have 0 elements' do
|
18
|
+
@key.size.should be 0
|
19
|
+
end
|
20
|
+
should 'have no fields' do
|
21
|
+
@key.fields.empty?.should be true
|
22
|
+
end
|
23
|
+
should 'have itself as an ancestor' do
|
24
|
+
@key.ancestor?(@key).should be true
|
25
|
+
end
|
26
|
+
should 'raise a KeyError when trying to update' do
|
27
|
+
lambda{@key.update(nil,:aoe,42)}.should raise_error KeyError
|
28
|
+
end
|
29
|
+
should 'raise a KeyError when trying to find a field' do
|
30
|
+
lambda{@key.field(0)}.should raise_error KeyError
|
31
|
+
end
|
32
|
+
should 'raise a KeyError when accessing a non-existant index' do
|
33
|
+
lambda{@key.index(:c)}.should raise_error KeyError
|
34
|
+
lambda{@key.index(15)}.should raise_error KeyError
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with a child' do
|
38
|
+
|
39
|
+
setup { @child = Key.new @key }
|
40
|
+
|
41
|
+
should 'have 1 child' do
|
42
|
+
@key.children.size.should be 1
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'and a grandchild' do
|
46
|
+
|
47
|
+
setup { @grand = Key.new @child }
|
48
|
+
|
49
|
+
should 'have the correct ancestors' do
|
50
|
+
@grand.ancestor?(@key).should be true
|
51
|
+
@grand.ancestor?(@child).should be true
|
52
|
+
@child.ancestor?(@key).should be true
|
53
|
+
@key.ancestor?(@child).should be false
|
54
|
+
@key.ancestor?(@grand).should be false
|
55
|
+
end
|
56
|
+
|
57
|
+
should 'correctly validate Structs' do
|
58
|
+
@k_s = Struct.new @key
|
59
|
+
@c_s = Struct.new @child
|
60
|
+
@g_s = Struct.new @grand
|
61
|
+
@key.valid?(@k_s).should be true
|
62
|
+
@key.valid?(@c_s).should be true
|
63
|
+
@key.valid?(@g_s).should be true
|
64
|
+
@child.valid?(@k_s).should be false
|
65
|
+
@child.valid?(@g_s).should be true
|
66
|
+
@grand.valid?(@c_s).should be false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'A Key with two strict fields' do
|
74
|
+
|
75
|
+
setup do
|
76
|
+
@key = Key.new
|
77
|
+
@str_field = Field.of_class String
|
78
|
+
@int_field = Field.of_class Integer
|
79
|
+
@key.register 'str', @str_field
|
80
|
+
@key.register :int, @int_field
|
81
|
+
end
|
82
|
+
|
83
|
+
should 'have the correct fields' do
|
84
|
+
@key.fields.should be(
|
85
|
+
{:str => @str_field, :int => @int_field})
|
86
|
+
end
|
87
|
+
|
88
|
+
should 'return the correct fields' do
|
89
|
+
@key.field(0).should be @str_field
|
90
|
+
@key.field(1).should be @int_field
|
91
|
+
@key.field(:str).should be @str_field
|
92
|
+
@key.field('int').should be @int_field
|
93
|
+
end
|
94
|
+
|
95
|
+
should 'have the correct indices' do
|
96
|
+
@key.index(:str).should be 0
|
97
|
+
@key.index('int').should be 1
|
98
|
+
@key.index(1).should be 1
|
99
|
+
end
|
100
|
+
|
101
|
+
should 'return itself when updated correctly' do
|
102
|
+
@key.update(nil, 'str', 'abc'){}.should be @key
|
103
|
+
@key.update(nil, 'int', 12345){}.should be @key
|
104
|
+
end
|
105
|
+
|
106
|
+
should 'raise a ValidationError when updated incorrectly' do
|
107
|
+
lambda{@key.update(nil, :str, 123){}}.should raise_error ValidationError
|
108
|
+
lambda{@key.update(nil, :int, 'p'){}}.should raise_error ValidationError
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'and a child with 1 field.' do
|
112
|
+
|
113
|
+
setup {
|
114
|
+
@child = Key.new @key
|
115
|
+
@child.register :i2, @int_field
|
116
|
+
}
|
117
|
+
|
118
|
+
context 'The child' do
|
119
|
+
should 'have the correct size' do
|
120
|
+
@child.size.should be 3
|
121
|
+
end
|
122
|
+
should 'have the correct indices' do
|
123
|
+
@child.index(:int).should be 1
|
124
|
+
@child.index('i2').should be 2
|
125
|
+
end
|
126
|
+
should 'have the correct fields' do
|
127
|
+
@child.fields.should be(
|
128
|
+
{:str => @str_field, :int => @int_field, :i2 => @int_field})
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'The parent' do
|
133
|
+
should 'have the correct fields' do
|
134
|
+
@key.fields.should be(
|
135
|
+
{:str => @str_field, :int => @int_field})
|
136
|
+
end
|
137
|
+
|
138
|
+
should 'have the correct indices' do
|
139
|
+
@key.index('str').should be 0
|
140
|
+
@key.index(:int).should be 1
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'When the parent is updated. The child' do
|
145
|
+
|
146
|
+
setup { @key.register :s2, @str_field }
|
147
|
+
|
148
|
+
should 'update its fields' do
|
149
|
+
@child.fields.should be(
|
150
|
+
{:str => @str_field, :int => @int_field,
|
151
|
+
:s2 => @str_field, :i2 => @int_field})
|
152
|
+
end
|
153
|
+
|
154
|
+
should 'update its indices' do
|
155
|
+
@child.index(:s2).should be 2
|
156
|
+
@child.index(:i2).should be 3
|
157
|
+
end
|
158
|
+
|
159
|
+
should 'update its size' do
|
160
|
+
@child.size.should be 4
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require File.join(File.dirname(__FILE__), '../test_helper')
|
3
|
+
module Dynastruct
|
4
|
+
class StructTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
context 'A new Struct of a 1 element key' do
|
7
|
+
|
8
|
+
setup do
|
9
|
+
@field = Field.of_class Integer
|
10
|
+
@key = Key.new
|
11
|
+
@key.register :a, @field
|
12
|
+
@struct = Struct.new @key
|
13
|
+
end
|
14
|
+
|
15
|
+
should 'raise a KeyError when a non-existant field is accessed or assigned' do
|
16
|
+
lambda{@struct['b']}.should raise_error KeyError
|
17
|
+
lambda{@struct[:x] = 42}.should raise_error KeyError
|
18
|
+
end
|
19
|
+
|
20
|
+
should 'correctly store data' do
|
21
|
+
@struct[:a].should be nil
|
22
|
+
@struct['a'] = 42
|
23
|
+
@struct['a'].should be 42
|
24
|
+
end
|
25
|
+
|
26
|
+
should 'raise a ValidationError when incorrectly assigned' do
|
27
|
+
lambda{@struct['a'] = 'abc'}.should raise_error ValidationError
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'A transforming struct' do
|
33
|
+
|
34
|
+
setup do
|
35
|
+
@f_str = Field.of_class String
|
36
|
+
@f_sym = Field.of_class Symbol
|
37
|
+
@f_int = Field.of_class Integer
|
38
|
+
@k_base = Key.new
|
39
|
+
@k_base.register :type, @f_sym
|
40
|
+
@k_base.register :typ2, @f_sym
|
41
|
+
|
42
|
+
@k_date = Key.new(@k_base, :type) do |s|
|
43
|
+
s[:type] == :date
|
44
|
+
end
|
45
|
+
@k_date.register :date, @f_str
|
46
|
+
|
47
|
+
@k_nums = Key.new(@k_base, :type, :typ2) do |s|
|
48
|
+
s[:type] == :num and s[:typ2] == :s
|
49
|
+
end
|
50
|
+
@k_nums.register :num1, @f_int
|
51
|
+
@k_nums.register :num2, @f_int
|
52
|
+
@k_nums.register :num3, @f_int
|
53
|
+
|
54
|
+
@k_same = Key.new(@k_base, :typ2) do |s|
|
55
|
+
s[:type] == s[:typ2]
|
56
|
+
end
|
57
|
+
|
58
|
+
@struct = Struct.new @k_base
|
59
|
+
end
|
60
|
+
|
61
|
+
should 'be able to start as a child' do
|
62
|
+
a = Struct.new @k_date
|
63
|
+
a[:date] = 'today'
|
64
|
+
end
|
65
|
+
|
66
|
+
should 'transform into the date structure' do
|
67
|
+
@struct[:type] = :date
|
68
|
+
@struct.key.should be @k_date
|
69
|
+
end
|
70
|
+
|
71
|
+
should 'transform into the nums structure' do
|
72
|
+
@struct[:type] = :num
|
73
|
+
@struct[:typ2] = :a
|
74
|
+
@struct.key.should be @k_base
|
75
|
+
@struct[:typ2] = :s
|
76
|
+
@struct.key.should be @k_nums
|
77
|
+
end
|
78
|
+
|
79
|
+
should 'only transform when the selected key is changed' do
|
80
|
+
@struct[:typ2] = :a
|
81
|
+
@struct[:type] = :a
|
82
|
+
@struct.key.should be @k_base
|
83
|
+
@struct[:typ2] = :a
|
84
|
+
@struct.key.should be @k_same
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), '../dynastruct')
|
2
|
-
%w'rubygems shoulda matchy
|
1
|
+
require File.join(File.dirname(__FILE__), '../lib/dynastruct')
|
2
|
+
%w'rubygems shoulda matchy'.each {|w| require w }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grayswx-dynastruct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- grayswx
|
@@ -24,23 +24,22 @@ extra_rdoc_files: []
|
|
24
24
|
files:
|
25
25
|
- README
|
26
26
|
- Rakefile
|
27
|
-
-
|
28
|
-
- dynastruct.rb
|
29
|
-
- lib/
|
30
|
-
- lib/
|
31
|
-
- lib/
|
32
|
-
-
|
33
|
-
- test/
|
34
|
-
- test/
|
27
|
+
- lib/dynastruct
|
28
|
+
- lib/dynastruct/struct.rb
|
29
|
+
- lib/dynastruct/key.rb
|
30
|
+
- lib/dynastruct/field.rb
|
31
|
+
- lib/dynastruct.rb
|
32
|
+
- test/dynastruct
|
33
|
+
- test/dynastruct/key_test.rb
|
34
|
+
- test/dynastruct/field_test.rb
|
35
|
+
- test/dynastruct/struct_test.rb
|
35
36
|
- test/test_helper.rb
|
36
|
-
- test/d_field_test.rb
|
37
37
|
has_rdoc: true
|
38
38
|
homepage: http://github.com/grayswx/dynastruct/tree/master
|
39
39
|
post_install_message:
|
40
40
|
rdoc_options: []
|
41
41
|
|
42
42
|
require_paths:
|
43
|
-
- .
|
44
43
|
- lib
|
45
44
|
required_ruby_version: !ruby/object:Gem::Requirement
|
46
45
|
requirements:
|
@@ -60,6 +59,6 @@ rubyforge_project: n/a
|
|
60
59
|
rubygems_version: 1.2.0
|
61
60
|
signing_key:
|
62
61
|
specification_version: 2
|
63
|
-
summary: Structs that
|
62
|
+
summary: Structs that change based upon their contents.
|
64
63
|
test_files: []
|
65
64
|
|
data/dynastruct.rb
DELETED
data/lib/d_exceptions.rb
DELETED
data/lib/d_field.rb
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
require 'd_exceptions'
|
2
|
-
|
3
|
-
class DField
|
4
|
-
|
5
|
-
# Creates a new kind of DField.
|
6
|
-
def initialize( *validators )
|
7
|
-
@validators = validators or []
|
8
|
-
end
|
9
|
-
|
10
|
-
##### Usage Methods
|
11
|
-
|
12
|
-
# Tests whether or not the given object is a valid object for this field
|
13
|
-
def valid?( target )
|
14
|
-
@validators.all? {|v| v[target]}
|
15
|
-
end
|
16
|
-
|
17
|
-
# Reads an object of this field type from an IO stream.
|
18
|
-
def read( ios )
|
19
|
-
raise NoProcError, "Field type cannot read." unless @reader
|
20
|
-
@reader[ios]
|
21
|
-
end
|
22
|
-
|
23
|
-
# Writes an object to an IO stream.
|
24
|
-
def write( object, ios )
|
25
|
-
raise NoProcError, "Field type cannot write." unless @writer
|
26
|
-
@writer[object, ios]
|
27
|
-
end
|
28
|
-
|
29
|
-
##### Creation Methods
|
30
|
-
|
31
|
-
# Adds a validator to the object.
|
32
|
-
def add_validator( &validator )
|
33
|
-
@validators << validator
|
34
|
-
end
|
35
|
-
|
36
|
-
# Sets the reader for this field.
|
37
|
-
def reader( &proc )
|
38
|
-
@reader = proc
|
39
|
-
end
|
40
|
-
|
41
|
-
# Sets the writer for this field.
|
42
|
-
def writer( &proc )
|
43
|
-
@writer = proc
|
44
|
-
end
|
45
|
-
|
46
|
-
##### Shortcut creation methods.
|
47
|
-
|
48
|
-
# Makes a shortcut field creator of the given type.
|
49
|
-
def self.make_shortcut( name, test, validator )
|
50
|
-
temp, $bypass = $bypass, [name, test, validator]
|
51
|
-
class << self
|
52
|
-
make_shortcut(*$bypass)
|
53
|
-
end
|
54
|
-
$bypass = temp
|
55
|
-
end
|
56
|
-
class << self
|
57
|
-
def self.make_shortcut( name, test, validator )
|
58
|
-
define_method "of_#{name}".to_sym do |type|
|
59
|
-
fieldvar = "@fields_of_#{name}".to_sym
|
60
|
-
intern_hash = instance_variable_get fieldvar
|
61
|
-
unless intern_hash
|
62
|
-
intern_hash = {}
|
63
|
-
instance_variable_set fieldvar, intern_hash
|
64
|
-
end
|
65
|
-
raise ArgumentError, 'Wrong type provided.' unless test[type]
|
66
|
-
intern_hash[type] ||= self.new(lambda {|o| validator[type, o]})
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
make_shortcut('class',
|
72
|
-
lambda {|t| t.kind_of? Class },
|
73
|
-
lambda {|t,o| o.kind_of? t })
|
74
|
-
make_shortcut('method',
|
75
|
-
lambda {|t| t.kind_of? Symbol },
|
76
|
-
lambda {|t,o| o.respond_to? t })
|
77
|
-
|
78
|
-
end
|
data/lib/d_key.rb
DELETED
@@ -1,165 +0,0 @@
|
|
1
|
-
require 'd_field'
|
2
|
-
|
3
|
-
class DKey < DField
|
4
|
-
|
5
|
-
Tuple = Struct.new :index, :hooks, :type
|
6
|
-
|
7
|
-
# Creates a new DKey.
|
8
|
-
# Fields is a list of fields which trigger the test.
|
9
|
-
def initialize( parent = nil, *fields, &test )
|
10
|
-
|
11
|
-
super(lambda do |struct|
|
12
|
-
struct.key.ancestor? self
|
13
|
-
end)
|
14
|
-
reader do |ios|
|
15
|
-
struct = DStruct.new self
|
16
|
-
# Must do a manual loop here because size can change on the fly.
|
17
|
-
index = 0
|
18
|
-
while index < struct.key.size do
|
19
|
-
struct[index] = struct.key[index].read ios
|
20
|
-
index += 1
|
21
|
-
end
|
22
|
-
end
|
23
|
-
writer do |struct, ios|
|
24
|
-
struct.each_index do |i|
|
25
|
-
struct.key[i].write struct[i], ios
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
@fields = {}
|
30
|
-
@fields_i = []
|
31
|
-
@children = []
|
32
|
-
if parent then
|
33
|
-
@size = parent.size
|
34
|
-
@parent = parent
|
35
|
-
@test = test
|
36
|
-
parent.add_child self
|
37
|
-
parent.add_hooks fields, self
|
38
|
-
else
|
39
|
-
raise ArgumentError, "Cannot transform with no parent." \
|
40
|
-
if test or not fields.empty?
|
41
|
-
@size = 0
|
42
|
-
@parent = nil
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
##### Usage Methods
|
48
|
-
|
49
|
-
attr_reader :size, :parent
|
50
|
-
|
51
|
-
# Returns the index of a field name or index.
|
52
|
-
# Also makes sure that the field exists.
|
53
|
-
def index( name )
|
54
|
-
# If an index.
|
55
|
-
if name.kind_of? Integer then
|
56
|
-
raise KeyError, 'Key out of bounds.' unless name >= 0 and name < size
|
57
|
-
return name
|
58
|
-
end
|
59
|
-
# If a String or Symbol.
|
60
|
-
name = name.to_sym
|
61
|
-
field = @fields[name]
|
62
|
-
return field.index if field
|
63
|
-
return @parent.index(name) if @parent
|
64
|
-
raise KeyError, 'Key does not exist.'
|
65
|
-
end
|
66
|
-
|
67
|
-
# Returns a hash of names to field types.
|
68
|
-
def fields()
|
69
|
-
base = @parent ? @parent.fields : {}
|
70
|
-
@fields.each do |key, val|
|
71
|
-
base[key] = val.type
|
72
|
-
end
|
73
|
-
base
|
74
|
-
end
|
75
|
-
|
76
|
-
# Returns the field with the given index or name.
|
77
|
-
def field( field )
|
78
|
-
field = index_to_field field if field.kind_of? Integer
|
79
|
-
@fields[field.to_sym].type
|
80
|
-
end
|
81
|
-
alias :[] :field
|
82
|
-
|
83
|
-
# Returns the children of this key.
|
84
|
-
def children()
|
85
|
-
@children.dup
|
86
|
-
end
|
87
|
-
|
88
|
-
# Returns whether or not the given key is an ancestor.
|
89
|
-
def ancestor?( key )
|
90
|
-
return true if self == key
|
91
|
-
return @parent.ancestor? key if @parent
|
92
|
-
false
|
93
|
-
end
|
94
|
-
|
95
|
-
# Returns the new key that results from assigning
|
96
|
-
# a given value to a given field.
|
97
|
-
# A ValidationError occurs if the assignment is not valid.
|
98
|
-
def update( struct, field, value )
|
99
|
-
field = index_to_field field if field.kind_of? Integer
|
100
|
-
field = field.to_sym
|
101
|
-
raise KeyError, 'Key not present' unless @fields.key? field
|
102
|
-
field = @fields[field]
|
103
|
-
if field.type.valid? value then
|
104
|
-
yield field.index
|
105
|
-
return catch(:hook_match) do
|
106
|
-
field.hooks.each do |key|
|
107
|
-
throw :hook_match, key if key.test_hook struct
|
108
|
-
end
|
109
|
-
self
|
110
|
-
end
|
111
|
-
end
|
112
|
-
raise ValidationError, 'Value not valid.'
|
113
|
-
end
|
114
|
-
|
115
|
-
##### Helper Methods
|
116
|
-
|
117
|
-
# Converts an index to a field name.
|
118
|
-
def index_to_field( index )
|
119
|
-
raise KeyError, 'Key out of bounds' if index < 0 or index >= size
|
120
|
-
psize = @parent ? @parent.size : 0
|
121
|
-
return @parent.index_to_field index if index < psize
|
122
|
-
@fields_i[index - psize]
|
123
|
-
end
|
124
|
-
|
125
|
-
# Increments this and child keys' indices by 1.
|
126
|
-
def increment_indices()
|
127
|
-
@size += 1
|
128
|
-
@fields.each do |key, tuple|
|
129
|
-
tuple.index += 1
|
130
|
-
end
|
131
|
-
@children.each do |child|
|
132
|
-
child.increment_indices
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
# Tests if the struct meets the specified criteria for
|
137
|
-
# transforming to this key.
|
138
|
-
def test_hook( struct )
|
139
|
-
@test[struct]
|
140
|
-
end
|
141
|
-
|
142
|
-
##### Creation Methods
|
143
|
-
|
144
|
-
# Registers a field with a name.
|
145
|
-
def register( name, field )
|
146
|
-
name = name.to_sym
|
147
|
-
@fields[name] = Tuple.new @size, [], field
|
148
|
-
@fields_i[@size] = name
|
149
|
-
@size += 1
|
150
|
-
@children.each {|c| c.increment_indices }
|
151
|
-
end
|
152
|
-
|
153
|
-
# Adds a child to this key.
|
154
|
-
def add_child( child )
|
155
|
-
@children << child
|
156
|
-
end
|
157
|
-
|
158
|
-
# Adds hooks to this key.
|
159
|
-
def add_hooks( hooks, key )
|
160
|
-
hooks.each do |hook|
|
161
|
-
@fields[hook.to_sym].hooks << key
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
end
|
data/lib/d_struct.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'd_key'
|
2
|
-
|
3
|
-
class DStruct
|
4
|
-
|
5
|
-
def initialize( key )
|
6
|
-
@key = key
|
7
|
-
@data = []
|
8
|
-
end
|
9
|
-
|
10
|
-
attr_reader :key
|
11
|
-
|
12
|
-
##### Usage Methods.
|
13
|
-
|
14
|
-
# Gets the element of the struct.
|
15
|
-
def []( field )
|
16
|
-
@data[@key.index field]
|
17
|
-
end
|
18
|
-
|
19
|
-
# Assigns the element of the struct.
|
20
|
-
def []=( field, value )
|
21
|
-
@key = @key.update self, field, value do |i|
|
22
|
-
@data[i] = value
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def each( &proc )
|
27
|
-
@data.each &proc
|
28
|
-
end
|
29
|
-
|
30
|
-
def each_index( &proc )
|
31
|
-
@data.each_index &proc
|
32
|
-
end
|
33
|
-
|
34
|
-
##### Helper Methods.
|
35
|
-
|
36
|
-
end
|
data/test/d_field_test.rb
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
#! /usr/bin/ruby
|
2
|
-
require File.join(File.dirname(__FILE__), 'test_helper')
|
3
|
-
class DFieldTest < Test::Unit::TestCase
|
4
|
-
|
5
|
-
context 'A new DField' do
|
6
|
-
|
7
|
-
setup do
|
8
|
-
@field = DField.new
|
9
|
-
end
|
10
|
-
should 'validate anything' do
|
11
|
-
@field.valid?(nil).should be true
|
12
|
-
@field.valid?(42).should be true
|
13
|
-
end
|
14
|
-
should 'not be able to read' do
|
15
|
-
lambda{@field.read(StringIO.new)}.should raise_error NoProcError
|
16
|
-
end
|
17
|
-
should 'not be able to write' do
|
18
|
-
lambda{@field.write('', StringIO.new)}.should raise_error NoProcError
|
19
|
-
end
|
20
|
-
|
21
|
-
context 'with a direct writer' do
|
22
|
-
setup do
|
23
|
-
@field.writer {|v,o| o.write v }
|
24
|
-
end
|
25
|
-
should 'write out "abc"' do
|
26
|
-
ios = StringIO.new
|
27
|
-
@field.write 'abc', ios
|
28
|
-
ios.string.should be 'abc'
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
context 'with a direct word reader' do
|
33
|
-
setup do
|
34
|
-
@field.reader {|i| i.gets(' ').strip }
|
35
|
-
end
|
36
|
-
should 'read in "abc"' do
|
37
|
-
ios = StringIO.new 'abc'
|
38
|
-
@field.read(ios).should be 'abc'
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
context 'A new DField of class Integer' do
|
45
|
-
setup do
|
46
|
-
@field = DField.of_class Integer
|
47
|
-
end
|
48
|
-
should "validate Integers." do
|
49
|
-
@field.valid?(42).should be true
|
50
|
-
@field.valid?(0).should be true
|
51
|
-
end
|
52
|
-
should "not validate non Integers." do
|
53
|
-
@field.valid?('hello').should be false
|
54
|
-
@field.valid?(45.5).should be false
|
55
|
-
end
|
56
|
-
should 'intern' do
|
57
|
-
@field.should be DField.of_class Integer
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
context 'A new DField of method :to_str' do
|
62
|
-
setup do
|
63
|
-
@field = DField.of_method :to_str
|
64
|
-
end
|
65
|
-
should 'validate Strings.' do
|
66
|
-
@field.valid?('hello').should be true
|
67
|
-
@field.valid?('').should be true
|
68
|
-
end
|
69
|
-
should 'not validate Numerics.' do
|
70
|
-
@field.valid?(42).should be false
|
71
|
-
@field.valid?(1.5).should be false
|
72
|
-
end
|
73
|
-
should 'intern' do
|
74
|
-
@field.should be DField.of_method :to_str
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
data/test/d_key_test.rb
DELETED
@@ -1,167 +0,0 @@
|
|
1
|
-
#! /usr/bin/ruby
|
2
|
-
### XXX Test the read / write actions. Maybe in Struct?
|
3
|
-
|
4
|
-
|
5
|
-
require File.join(File.dirname(__FILE__), 'test_helper')
|
6
|
-
class DKeyTest < Test::Unit::TestCase
|
7
|
-
|
8
|
-
context 'A new DKey' do
|
9
|
-
|
10
|
-
setup { @key = DKey.new }
|
11
|
-
|
12
|
-
should 'have no children' do
|
13
|
-
@key.children.empty?.should be true
|
14
|
-
end
|
15
|
-
should 'have no parent' do
|
16
|
-
@key.parent.should be nil
|
17
|
-
end
|
18
|
-
should 'have 0 elements' do
|
19
|
-
@key.size.should be 0
|
20
|
-
end
|
21
|
-
should 'have no fields' do
|
22
|
-
@key.fields.empty?.should be true
|
23
|
-
end
|
24
|
-
should 'have itself as an ancestor' do
|
25
|
-
@key.ancestor?(@key).should be true
|
26
|
-
end
|
27
|
-
should 'raise a KeyError when trying to update' do
|
28
|
-
lambda{@key.update(nil,:aoe,42)}.should raise_error KeyError
|
29
|
-
end
|
30
|
-
should 'raise a KeyError when trying to find a field' do
|
31
|
-
lambda{@key.field(0)}.should raise_error KeyError
|
32
|
-
end
|
33
|
-
should 'raise a KeyError when accessing a non-existant index' do
|
34
|
-
lambda{@key.index(:c)}.should raise_error KeyError
|
35
|
-
lambda{@key.index(15)}.should raise_error KeyError
|
36
|
-
end
|
37
|
-
|
38
|
-
context 'with a child' do
|
39
|
-
|
40
|
-
setup { @child = DKey.new @key }
|
41
|
-
|
42
|
-
should 'have 1 child' do
|
43
|
-
@key.children.size.should be 1
|
44
|
-
end
|
45
|
-
|
46
|
-
context 'and a grandchild' do
|
47
|
-
|
48
|
-
setup { @grand = DKey.new @child }
|
49
|
-
|
50
|
-
should 'have the correct ancestors' do
|
51
|
-
@grand.ancestor?(@key).should be true
|
52
|
-
@grand.ancestor?(@child).should be true
|
53
|
-
@child.ancestor?(@key).should be true
|
54
|
-
@key.ancestor?(@child).should be false
|
55
|
-
@key.ancestor?(@grand).should be false
|
56
|
-
end
|
57
|
-
|
58
|
-
should 'correctly validate DStructs' do
|
59
|
-
@k_s = DStruct.new @key
|
60
|
-
@c_s = DStruct.new @child
|
61
|
-
@g_s = DStruct.new @grand
|
62
|
-
@key.valid?(@k_s).should be true
|
63
|
-
@key.valid?(@c_s).should be true
|
64
|
-
@key.valid?(@g_s).should be true
|
65
|
-
@child.valid?(@k_s).should be false
|
66
|
-
@child.valid?(@g_s).should be true
|
67
|
-
@grand.valid?(@c_s).should be false
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
end
|
73
|
-
|
74
|
-
context 'A DKey with two strict fields' do
|
75
|
-
|
76
|
-
setup do
|
77
|
-
@key = DKey.new
|
78
|
-
@str_field = DField.of_class String
|
79
|
-
@int_field = DField.of_class Integer
|
80
|
-
@key.register 'str', @str_field
|
81
|
-
@key.register :int, @int_field
|
82
|
-
end
|
83
|
-
|
84
|
-
should 'have the correct fields' do
|
85
|
-
@key.fields.should be(
|
86
|
-
{:str => @str_field, :int => @int_field})
|
87
|
-
end
|
88
|
-
|
89
|
-
should 'return the correct fields' do
|
90
|
-
@key.field(0).should be @str_field
|
91
|
-
@key.field(1).should be @int_field
|
92
|
-
@key.field(:str).should be @str_field
|
93
|
-
@key.field('int').should be @int_field
|
94
|
-
end
|
95
|
-
|
96
|
-
should 'have the correct indices' do
|
97
|
-
@key.index(:str).should be 0
|
98
|
-
@key.index('int').should be 1
|
99
|
-
@key.index(1).should be 1
|
100
|
-
end
|
101
|
-
|
102
|
-
should 'return itself when updated correctly' do
|
103
|
-
@key.update(nil, 'str', 'abc'){}.should be @key
|
104
|
-
@key.update(nil, 'int', 12345){}.should be @key
|
105
|
-
end
|
106
|
-
|
107
|
-
should 'raise a ValidationError when updated incorrectly' do
|
108
|
-
lambda{@key.update(nil, :str, 123){}}.should raise_error ValidationError
|
109
|
-
lambda{@key.update(nil, :int, 'p'){}}.should raise_error ValidationError
|
110
|
-
end
|
111
|
-
|
112
|
-
context 'and a child with 1 field.' do
|
113
|
-
|
114
|
-
setup {
|
115
|
-
@child = DKey.new @key
|
116
|
-
@child.register :i2, @int_field
|
117
|
-
}
|
118
|
-
|
119
|
-
context 'The child' do
|
120
|
-
should 'have the correct size' do
|
121
|
-
@child.size.should be 3
|
122
|
-
end
|
123
|
-
should 'have the correct indices' do
|
124
|
-
@child.index(:int).should be 1
|
125
|
-
@child.index('i2').should be 2
|
126
|
-
end
|
127
|
-
should 'have the correct fields' do
|
128
|
-
@child.fields.should be(
|
129
|
-
{:str => @str_field, :int => @int_field, :i2 => @int_field})
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
context 'The parent' do
|
134
|
-
should 'have the correct fields' do
|
135
|
-
@key.fields.should be(
|
136
|
-
{:str => @str_field, :int => @int_field})
|
137
|
-
end
|
138
|
-
|
139
|
-
should 'have the correct indices' do
|
140
|
-
@key.index('str').should be 0
|
141
|
-
@key.index(:int).should be 1
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
context 'When the parent is updated. The child' do
|
146
|
-
|
147
|
-
setup { @key.register :s2, @str_field }
|
148
|
-
|
149
|
-
should 'update its fields' do
|
150
|
-
@child.fields.should be(
|
151
|
-
{:str => @str_field, :int => @int_field,
|
152
|
-
:s2 => @str_field, :i2 => @int_field})
|
153
|
-
end
|
154
|
-
|
155
|
-
should 'update its indices' do
|
156
|
-
@child.index(:s2).should be 2
|
157
|
-
@child.index(:i2).should be 3
|
158
|
-
end
|
159
|
-
|
160
|
-
should 'update its size' do
|
161
|
-
@child.size.should be 4
|
162
|
-
end
|
163
|
-
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
data/test/d_struct_test.rb
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
#! /usr/bin/ruby
|
2
|
-
require File.join(File.dirname(__FILE__), 'test_helper')
|
3
|
-
class DStructTest < Test::Unit::TestCase
|
4
|
-
|
5
|
-
context 'A new DStruct of a 1 element key' do
|
6
|
-
|
7
|
-
setup do
|
8
|
-
@field = DField.of_class Integer
|
9
|
-
@key = DKey.new
|
10
|
-
@key.register :a, @field
|
11
|
-
@struct = DStruct.new @key
|
12
|
-
end
|
13
|
-
|
14
|
-
should 'raise a KeyError when a non-existant field is accessed or assigned' do
|
15
|
-
lambda{@struct['b']}.should raise_error KeyError
|
16
|
-
lambda{@struct[:x] = 42}.should raise_error KeyError
|
17
|
-
end
|
18
|
-
|
19
|
-
should 'correctly store data' do
|
20
|
-
@struct[:a].should be nil
|
21
|
-
@struct['a'] = 42
|
22
|
-
@struct['a'].should be 42
|
23
|
-
end
|
24
|
-
|
25
|
-
should 'raise a ValidationError when incorrectly assigned' do
|
26
|
-
lambda{@struct['a'] = 'abc'}.should raise_error ValidationError
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'A transforming struct' do
|
32
|
-
|
33
|
-
setup do
|
34
|
-
@f_str = DField.of_class String
|
35
|
-
@f_sym = DField.of_class Symbol
|
36
|
-
@f_int = DField.of_class Integer
|
37
|
-
@k_base = DKey.new
|
38
|
-
@k_base.register :type, @f_sym
|
39
|
-
@k_base.register :typ2, @f_sym
|
40
|
-
|
41
|
-
@k_date = DKey.new(@k_base, :type) do |s|
|
42
|
-
s[:type] == :date
|
43
|
-
end
|
44
|
-
@k_date.register :date, @f_str
|
45
|
-
|
46
|
-
@k_nums = DKey.new(@k_base, :type, :typ2) do |s|
|
47
|
-
s[:type] == :num and s[:typ2] == :s
|
48
|
-
end
|
49
|
-
@k_nums.register :num1, @f_int
|
50
|
-
@k_nums.register :num2, @f_int
|
51
|
-
@k_nums.register :num3, @f_int
|
52
|
-
|
53
|
-
@k_same = DKey.new(@k_base, :typ2) do |s|
|
54
|
-
s[:type] == s[:typ2]
|
55
|
-
end
|
56
|
-
|
57
|
-
@struct = DStruct.new @k_base
|
58
|
-
end
|
59
|
-
|
60
|
-
should 'be able to start as a child' do
|
61
|
-
a = DStruct.new @k_date
|
62
|
-
a[:date] = 'today'
|
63
|
-
end
|
64
|
-
|
65
|
-
should 'transform into the date structure' do
|
66
|
-
@struct[:type] = :date
|
67
|
-
@struct.key.should be @k_date
|
68
|
-
end
|
69
|
-
|
70
|
-
should 'transform into the nums structure' do
|
71
|
-
@struct[:type] = :num
|
72
|
-
@struct[:typ2] = :a
|
73
|
-
@struct.key.should be @k_base
|
74
|
-
@struct[:typ2] = :s
|
75
|
-
@struct.key.should be @k_nums
|
76
|
-
end
|
77
|
-
|
78
|
-
should 'only transform when the selected key is changed' do
|
79
|
-
@struct[:typ2] = :a
|
80
|
-
@struct[:type] = :a
|
81
|
-
@struct.key.should be @k_base
|
82
|
-
@struct[:typ2] = :a
|
83
|
-
@struct.key.should be @k_same
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|