grayswx-dynastruct 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|