grayswx-dynastruct 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +0 -0
- data/Rakefile +33 -0
- data/dynastruct.rb +3 -0
- data/lib/d_exceptions.rb +5 -0
- data/lib/d_field.rb +78 -0
- data/lib/d_key.rb +165 -0
- data/lib/d_struct.rb +36 -0
- data/test/d_field_test.rb +78 -0
- data/test/d_key_test.rb +167 -0
- data/test/d_struct_test.rb +88 -0
- data/test/test_helper.rb +2 -0
- metadata +65 -0
data/README
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
%w'dynastruct rubygems rake/clean rake/testtask
|
2
|
+
rake/gempackagetask'.each {|w| require w }
|
3
|
+
|
4
|
+
task :default => [:test]
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.test_files = FileList['test/**/*_test.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
spec = Gem::Specification.new do |s|
|
11
|
+
s.name = 'dynastruct'
|
12
|
+
s.version = '0.0.1'
|
13
|
+
s.summary = 'Structs that can change their shape on the fly.'
|
14
|
+
s.author = 'grayswx'
|
15
|
+
s.homepage = 'http://github.com/grayswx/dynastruct/tree/master'
|
16
|
+
s.rubyforge_project = 'n/a'
|
17
|
+
s.email = 'grayswx@gmail.com'
|
18
|
+
s.files = FileList['[A-Z]*', '*.rb', '{lib,test}/**/*']
|
19
|
+
s.require_paths = ['.', 'lib']
|
20
|
+
s.has_rdoc = true
|
21
|
+
end
|
22
|
+
|
23
|
+
Rake::GemPackageTask.new spec do |pkg|
|
24
|
+
pkg.need_tar = true
|
25
|
+
pkg.need_zip = true
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Generate a gemspec file for GitHub"
|
29
|
+
task :gemspec do
|
30
|
+
File.open("#{spec.name}.gemspec", 'w') do |f|
|
31
|
+
f.write spec.to_ruby
|
32
|
+
end
|
33
|
+
end
|
data/dynastruct.rb
ADDED
data/lib/d_exceptions.rb
ADDED
data/lib/d_field.rb
ADDED
@@ -0,0 +1,78 @@
|
|
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
ADDED
@@ -0,0 +1,165 @@
|
|
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
ADDED
@@ -0,0 +1,36 @@
|
|
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
|
@@ -0,0 +1,78 @@
|
|
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
ADDED
@@ -0,0 +1,167 @@
|
|
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
|
@@ -0,0 +1,88 @@
|
|
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
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: grayswx-dynastruct
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- grayswx
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-12-23 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: grayswx@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README
|
26
|
+
- Rakefile
|
27
|
+
- te.rb
|
28
|
+
- dynastruct.rb
|
29
|
+
- lib/d_key.rb
|
30
|
+
- lib/d_exceptions.rb
|
31
|
+
- lib/d_struct.rb
|
32
|
+
- lib/d_field.rb
|
33
|
+
- test/d_struct_test.rb
|
34
|
+
- test/d_key_test.rb
|
35
|
+
- test/test_helper.rb
|
36
|
+
- test/d_field_test.rb
|
37
|
+
has_rdoc: true
|
38
|
+
homepage: http://github.com/grayswx/dynastruct/tree/master
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
require_paths:
|
43
|
+
- .
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
rubyforge_project: n/a
|
60
|
+
rubygems_version: 1.2.0
|
61
|
+
signing_key:
|
62
|
+
specification_version: 2
|
63
|
+
summary: Structs that can change their shape on the fly.
|
64
|
+
test_files: []
|
65
|
+
|