forester 4.0.0 → 4.1.0
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.
- checksums.yaml +4 -4
- data/forester.gemspec +1 -1
- data/lib/forester/node_content/dictionary.rb +1 -0
- data/lib/forester/tree_node.rb +3 -2
- data/lib/forester/tree_node_ext/mutators.rb +15 -7
- data/lib/forester/tree_node_ext/validators.rb +134 -0
- data/lib/forester/tree_node_ext/views.rb +1 -1
- data/lib/forester/version.rb +1 -1
- data/lib/forester.rb +1 -0
- data/test/test_tree_node.rb +14 -1
- data/test/test_validators.rb +245 -0
- data/test/trees/simple_tree.yml +7 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21870a6a2f8976322af18cc53776dc41a6a1dbb9
|
4
|
+
data.tar.gz: 6947433f22479158ef6e08c8d965a2ce0de7a058
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bc863f4b9fdd51749c3bde9da3baea18265d61cf1f7a240571df07192a0d757fc187fcf1412cc94b1ca2304458e0d23f1de71bc951498496e3f25d177d753f0
|
7
|
+
data.tar.gz: dfdf878a13b18c54d14ce852d9cee9ac4d136b342c44f080032c8b71f82ad93c87cebfe90ff97a63ce14287b287d08ae84287471cba16c3a1370abee1495834d
|
data/forester.gemspec
CHANGED
@@ -6,7 +6,7 @@ require 'forester/version'
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = 'forester'
|
8
8
|
s.version = Forester::Version
|
9
|
-
s.date = '2016-08-
|
9
|
+
s.date = '2016-08-28'
|
10
10
|
s.summary = "A gem to represent and interact with tree data structures"
|
11
11
|
s.description = "Based on rubytree, this gem lets you build trees and run queries against them."
|
12
12
|
s.authors = ["Eugenio Bruno"]
|
@@ -59,6 +59,7 @@ module Forester
|
|
59
59
|
symbolize_keys: false
|
60
60
|
}
|
61
61
|
options = default_options.merge(options)
|
62
|
+
options[:fields_to_include] = fields if options[:fields_to_include] == :all
|
62
63
|
|
63
64
|
convert_key = ->(k) { k }
|
64
65
|
convert_key = ->(k) { k.to_s } if options[:stringify_keys]
|
data/lib/forester/tree_node.rb
CHANGED
@@ -5,6 +5,7 @@ module Forester
|
|
5
5
|
def_delegators :@content, :fields, :has?, :put!, :add!, :del!
|
6
6
|
|
7
7
|
include Aggregators
|
8
|
+
include Validators
|
8
9
|
include Mutators
|
9
10
|
include Views
|
10
11
|
|
@@ -38,8 +39,8 @@ module Forester
|
|
38
39
|
content.get(field)
|
39
40
|
elsif block_given?
|
40
41
|
yield self
|
41
|
-
elsif default != :raise
|
42
|
-
default
|
42
|
+
elsif options[:default] != :raise
|
43
|
+
options[:default]
|
43
44
|
else
|
44
45
|
raise ArgumentError.new("the node \"#{name}\" does not have \"#{field}\"")
|
45
46
|
end
|
@@ -2,18 +2,26 @@ module Forester
|
|
2
2
|
module Mutators
|
3
3
|
|
4
4
|
def add_field!(name, definition, options = {})
|
5
|
+
add_fields!([{ name: name, definition: definition }], options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_fields!(fields, options = {})
|
5
9
|
default_options = {
|
6
10
|
subtree: true
|
7
11
|
}
|
8
12
|
options = default_options.merge(options)
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
target_nodes = options[:subtree] ? each_node : [self]
|
15
|
+
|
16
|
+
target_nodes.each { |node| node.add_fields_to_root!(fields) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_fields_to_root!(fields)
|
20
|
+
fields.each do |field|
|
21
|
+
value = field[:definition]
|
22
|
+
value = value.call(self) if value.respond_to?(:call)
|
23
|
+
|
24
|
+
put!(field[:name], value)
|
17
25
|
end
|
18
26
|
end
|
19
27
|
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Forester
|
2
|
+
module Validators
|
3
|
+
|
4
|
+
def validate_uniqueness_of_field(field, options = {})
|
5
|
+
validate_uniqueness_of_fields([field], options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def validate_uniqueness_of_fields(fields, options = {})
|
9
|
+
default_options = {
|
10
|
+
combination: false,
|
11
|
+
first_failure_only: false,
|
12
|
+
within_subtrees_of_level: 0,
|
13
|
+
among_siblings_of_level: :not_siblings,
|
14
|
+
field_for_failures: :name,
|
15
|
+
as_failure: ->(node) { node.get(options[:field_for_failures]) }
|
16
|
+
}
|
17
|
+
options = default_options.merge(options)
|
18
|
+
|
19
|
+
return of_combination_of_fields(fields, options) if options[:combination]
|
20
|
+
|
21
|
+
failures = Hash.new(Hash.new([]))
|
22
|
+
|
23
|
+
nodes_of_level(options[:within_subtrees_of_level]).each do |subtree|
|
24
|
+
visited_nodes = []
|
25
|
+
nodes_to_visit =
|
26
|
+
if options[:among_siblings_of_level] == :not_siblings
|
27
|
+
subtree.each_node
|
28
|
+
else
|
29
|
+
nodes_of_level(options[:among_siblings_of_level])
|
30
|
+
end
|
31
|
+
|
32
|
+
nodes_to_visit.each do |node|
|
33
|
+
visited_nodes.each do |vn|
|
34
|
+
fields.each do |field|
|
35
|
+
next unless all_have?(field, [vn, node])
|
36
|
+
|
37
|
+
if same_values?(field, [vn, node])
|
38
|
+
k = vn.get(field) # repeated value
|
39
|
+
|
40
|
+
prepare_hash(failures, field, k)
|
41
|
+
|
42
|
+
add_failure_if_new(failures, field, k, options[:as_failure].call(vn))
|
43
|
+
add_failure( failures, field, k, options[:as_failure].call(node))
|
44
|
+
|
45
|
+
return result(failures) if options[:first_failure_only]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
visited_nodes << node
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
result(failures)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def of_combination_of_fields(fields, options)
|
59
|
+
failures = Hash.new(Hash.new([]))
|
60
|
+
|
61
|
+
nodes_of_level(options[:within_subtrees_of_level]).each do |subtree|
|
62
|
+
visited_nodes = []
|
63
|
+
nodes_to_visit =
|
64
|
+
if options[:among_siblings_of_level] == :not_siblings
|
65
|
+
subtree.each_node
|
66
|
+
else
|
67
|
+
nodes_of_level(options[:among_siblings_of_level])
|
68
|
+
end
|
69
|
+
|
70
|
+
nodes_to_visit.each do |node|
|
71
|
+
visited_nodes.each do |vn|
|
72
|
+
next unless all_have_all?(fields, [vn, node])
|
73
|
+
|
74
|
+
if same_values_for_all?(fields, [vn, node])
|
75
|
+
k = fields.map { |f| vn.get(f) } # repeated combination of values
|
76
|
+
|
77
|
+
prepare_hash(failures, fields, k)
|
78
|
+
|
79
|
+
add_failure_if_new(failures, fields, k, options[:as_failure].call(vn))
|
80
|
+
add_failure( failures, fields, k, options[:as_failure].call(node))
|
81
|
+
|
82
|
+
return result(failures) if options[:first_failure_only]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
visited_nodes << node
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
result(failures)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def all_have?(field, nodes)
|
95
|
+
all_have_all?([field], nodes)
|
96
|
+
end
|
97
|
+
|
98
|
+
def all_have_all?(fields, nodes)
|
99
|
+
nodes.all? { |n| fields.all? { |f| n.has?(f) } }
|
100
|
+
end
|
101
|
+
|
102
|
+
def same_values?(field, nodes)
|
103
|
+
same_values_for_all?([field], nodes)
|
104
|
+
end
|
105
|
+
|
106
|
+
def same_values_for_all?(fields, nodes)
|
107
|
+
fields
|
108
|
+
.map { |f| nodes.map { |n| n.get(f) } }
|
109
|
+
.all? { |vs| vs.uniq.length <= 1 }
|
110
|
+
end
|
111
|
+
|
112
|
+
def prepare_hash(hash, key, subkey)
|
113
|
+
hash[key] = {} unless hash.has_key?(key)
|
114
|
+
hash[key][subkey] = [] unless hash[key].has_key?(subkey)
|
115
|
+
end
|
116
|
+
|
117
|
+
def add_failure_if_new(failures, key, subkey, value)
|
118
|
+
add_failure(failures, key, subkey, value) unless failures[key][subkey].include?(value)
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_failure(failures, key, subkey, value)
|
122
|
+
failures[key][subkey] << value
|
123
|
+
end
|
124
|
+
|
125
|
+
def result(failures)
|
126
|
+
{
|
127
|
+
is_valid: failures.empty?,
|
128
|
+
repeated: failures.each_with_object({}) { |(k,v), h| h[k] = v.keys },
|
129
|
+
failures: failures
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
data/lib/forester/version.rb
CHANGED
data/lib/forester.rb
CHANGED
data/test/test_tree_node.rb
CHANGED
@@ -12,10 +12,23 @@ class TestTreeNode < Minitest::Test
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def test_values
|
15
|
-
|
15
|
+
values = (0..9).to_a
|
16
|
+
|
17
|
+
expected = values.reduce(:+)
|
16
18
|
actual = @@tree.reduce(0) { |acum, node| acum + node.get('value') }
|
17
19
|
|
18
20
|
assert_equal expected, actual
|
21
|
+
|
22
|
+
assert_equal values, @@tree.get('value', { subtree: true })
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_missing_values
|
26
|
+
assert_equal 0, @@tree.get('value')
|
27
|
+
assert_equal 'no', @@tree.get('whatever', { default: 'no' })
|
28
|
+
assert_equal 'no', @@tree.get('whatever', { default: 'missing' }) { 'no' }
|
29
|
+
assert_equal 'no', @@tree.get('whatever') { 'no' }
|
30
|
+
assert_equal 'no', @@tree.get('whatever') { |n| 'no' }
|
31
|
+
assert_equal 1, @@tree.get('whatever') { |n| n.get('value') + 1 }
|
19
32
|
end
|
20
33
|
|
21
34
|
def test_levels
|
@@ -0,0 +1,245 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'forester'
|
3
|
+
|
4
|
+
require_relative './simple_tree_helper'
|
5
|
+
|
6
|
+
class TestValidators < Minitest::Test
|
7
|
+
|
8
|
+
include SimpleTreeHelper
|
9
|
+
|
10
|
+
def test_validate_uniqueness_of_field_uniques
|
11
|
+
expected = {
|
12
|
+
is_valid: true,
|
13
|
+
repeated: {},
|
14
|
+
failures: {}
|
15
|
+
}
|
16
|
+
|
17
|
+
['name', :name, 'special', 'ghost'].each do |field|
|
18
|
+
actual = @@tree.validate_uniqueness_of_field(field)
|
19
|
+
assert_equal expected, actual
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_validate_uniqueness_of_field_color
|
25
|
+
expected = {
|
26
|
+
is_valid: false,
|
27
|
+
repeated: {
|
28
|
+
:color => ['Green', 'Yellow']
|
29
|
+
},
|
30
|
+
failures: {
|
31
|
+
:color => {
|
32
|
+
'Green' => ['First node of level 1', 'Second node of level 1'],
|
33
|
+
'Yellow' => ['First node of level 4', 'Second node of level 4']
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
actual = @@tree.validate_uniqueness_of_field(:color)
|
39
|
+
assert_equal expected, actual
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_validate_uniqueness_of_field_color_first_failure_only
|
43
|
+
expected = {
|
44
|
+
is_valid: false,
|
45
|
+
repeated: {
|
46
|
+
:color => ['Green']
|
47
|
+
},
|
48
|
+
failures: {
|
49
|
+
:color => {
|
50
|
+
'Green' => ['First node of level 1', 'Second node of level 1']
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
actual = @@tree.validate_uniqueness_of_field(:color, {
|
56
|
+
first_failure_only: true
|
57
|
+
})
|
58
|
+
assert_equal expected, actual
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_validate_uniqueness_of_field_color_among_siblings_of_level_1
|
62
|
+
expected = {
|
63
|
+
is_valid: false,
|
64
|
+
repeated: {
|
65
|
+
:color => ['Green']
|
66
|
+
},
|
67
|
+
failures: {
|
68
|
+
:color => {
|
69
|
+
'Green' => ['First node of level 1', 'Second node of level 1']
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
actual = @@tree.validate_uniqueness_of_field(:color, {
|
75
|
+
among_siblings_of_level: 1
|
76
|
+
})
|
77
|
+
|
78
|
+
assert_equal expected, actual
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_validate_uniqueness_of_field_color_among_siblings_of_level_2
|
82
|
+
expected = {
|
83
|
+
is_valid: true,
|
84
|
+
repeated: {},
|
85
|
+
failures: {}
|
86
|
+
}
|
87
|
+
|
88
|
+
actual = @@tree.validate_uniqueness_of_field(:color, {
|
89
|
+
among_siblings_of_level: 2
|
90
|
+
})
|
91
|
+
|
92
|
+
assert_equal expected, actual
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_validate_uniqueness_of_field_color_within_subtrees_of_level_1
|
96
|
+
expected = {
|
97
|
+
is_valid: false,
|
98
|
+
repeated: {
|
99
|
+
:color => ['Yellow']
|
100
|
+
},
|
101
|
+
failures: {
|
102
|
+
:color => {
|
103
|
+
'Yellow' => ['First node of level 4', 'Second node of level 4']
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
actual = @@tree.validate_uniqueness_of_field(:color, {
|
109
|
+
within_subtrees_of_level: 1,
|
110
|
+
})
|
111
|
+
|
112
|
+
assert_equal expected, actual
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_validate_uniqueness_of_field_color_within_subtrees_of_level_3
|
116
|
+
expected = {
|
117
|
+
is_valid: false,
|
118
|
+
repeated: {
|
119
|
+
:color => ['Yellow']
|
120
|
+
},
|
121
|
+
failures: {
|
122
|
+
:color => {
|
123
|
+
'Yellow' => ['First node of level 4', 'Second node of level 4']
|
124
|
+
}
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
actual = @@tree.validate_uniqueness_of_field(:color, {
|
129
|
+
within_subtrees_of_level: 3,
|
130
|
+
})
|
131
|
+
|
132
|
+
assert_equal expected, actual
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_validate_uniqueness_of_field_color_within_subtrees_of_level_4
|
136
|
+
expected = {
|
137
|
+
is_valid: true,
|
138
|
+
repeated: {},
|
139
|
+
failures: {}
|
140
|
+
}
|
141
|
+
|
142
|
+
actual = @@tree.validate_uniqueness_of_field(:color, {
|
143
|
+
within_subtrees_of_level: 4,
|
144
|
+
})
|
145
|
+
|
146
|
+
assert_equal expected, actual
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_validate_uniqueness_of_fields_name_color
|
150
|
+
expected = {
|
151
|
+
is_valid: false,
|
152
|
+
repeated: {
|
153
|
+
'color' => ['Green', 'Yellow']
|
154
|
+
},
|
155
|
+
failures: {
|
156
|
+
'color' => {
|
157
|
+
'Green' => ['First node of level 1', 'Second node of level 1'],
|
158
|
+
'Yellow' => ['First node of level 4', 'Second node of level 4']
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
actual = @@tree.validate_uniqueness_of_fields(['name', 'color'])
|
164
|
+
|
165
|
+
assert_equal expected, actual
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_validate_uniqueness_of_combination_of_fields_name_color
|
169
|
+
expected = {
|
170
|
+
is_valid: true,
|
171
|
+
repeated: {},
|
172
|
+
failures: {}
|
173
|
+
}
|
174
|
+
|
175
|
+
actual = @@tree.validate_uniqueness_of_fields(['name', 'color'], {
|
176
|
+
combination: true
|
177
|
+
})
|
178
|
+
|
179
|
+
assert_equal expected, actual
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_validate_uniqueness_of_fields_color_tone_first_failure_only
|
183
|
+
expected = {
|
184
|
+
is_valid: false,
|
185
|
+
repeated: {
|
186
|
+
'color' => ['Green']
|
187
|
+
},
|
188
|
+
failures: {
|
189
|
+
'color' => {
|
190
|
+
'Green' => ['First node of level 1', 'Second node of level 1']
|
191
|
+
}
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
actual = @@tree.validate_uniqueness_of_fields(['color', 'tone'], {
|
196
|
+
first_failure_only: true
|
197
|
+
})
|
198
|
+
|
199
|
+
assert_equal expected, actual
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_validate_uniqueness_of_fields_color_tone
|
203
|
+
expected = {
|
204
|
+
is_valid: false,
|
205
|
+
repeated: {
|
206
|
+
'color' => ['Green', 'Yellow'],
|
207
|
+
'tone' => ['Dark']
|
208
|
+
},
|
209
|
+
failures: {
|
210
|
+
'color' => {
|
211
|
+
'Green' => ['First node of level 1', 'Second node of level 1'],
|
212
|
+
'Yellow' => ['First node of level 4', 'Second node of level 4']
|
213
|
+
},
|
214
|
+
'tone' => {
|
215
|
+
'Dark' => ['First node of level 1', 'Second node of level 1']
|
216
|
+
}
|
217
|
+
}
|
218
|
+
}
|
219
|
+
|
220
|
+
actual = @@tree.validate_uniqueness_of_fields(['color', 'tone'])
|
221
|
+
|
222
|
+
assert_equal expected, actual
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_validate_uniqueness_of_combination_of_fields_color_tone
|
226
|
+
expected = {
|
227
|
+
is_valid: false,
|
228
|
+
repeated: {
|
229
|
+
['color', 'tone'] => [['Green', 'Dark']]
|
230
|
+
},
|
231
|
+
failures: {
|
232
|
+
['color', 'tone'] => {
|
233
|
+
['Green', 'Dark'] => ['First node of level 1', 'Second node of level 1']
|
234
|
+
}
|
235
|
+
}
|
236
|
+
}
|
237
|
+
|
238
|
+
actual = @@tree.validate_uniqueness_of_fields(['color', 'tone'], {
|
239
|
+
combination: true
|
240
|
+
})
|
241
|
+
|
242
|
+
assert_equal expected, actual
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
data/test/trees/simple_tree.yml
CHANGED
@@ -7,6 +7,8 @@ root:
|
|
7
7
|
children:
|
8
8
|
- name: First node of level 1
|
9
9
|
value: 1
|
10
|
+
color: Green
|
11
|
+
tone: Dark
|
10
12
|
children:
|
11
13
|
- name: First node of level 2
|
12
14
|
value: 2
|
@@ -15,12 +17,15 @@ root:
|
|
15
17
|
- I want to be the very best
|
16
18
|
- like no one ever was
|
17
19
|
- name: Second node of level 2
|
20
|
+
special: No other node has this field
|
18
21
|
value: 3
|
19
22
|
strings:
|
20
23
|
- I have a sibling to my left
|
21
24
|
- She wants to catch them all
|
22
25
|
- name: Second node of level 1
|
23
26
|
value: 4
|
27
|
+
color: Green
|
28
|
+
tone: Dark
|
24
29
|
children:
|
25
30
|
- name: Third node of level 2
|
26
31
|
value: 5
|
@@ -38,8 +43,10 @@ root:
|
|
38
43
|
children:
|
39
44
|
- name: First node of level 4
|
40
45
|
value: 8
|
46
|
+
color: Yellow
|
41
47
|
- name: Second node of level 4
|
42
48
|
value: 9
|
49
|
+
color: Yellow
|
43
50
|
strings:
|
44
51
|
- Could forester handle trees with hundreds of levels?
|
45
52
|
- Maybe.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: forester
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eugenio Bruno
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubytree
|
@@ -87,6 +87,7 @@ files:
|
|
87
87
|
- lib/forester/tree_node.rb
|
88
88
|
- lib/forester/tree_node_ext/aggregators.rb
|
89
89
|
- lib/forester/tree_node_ext/mutators.rb
|
90
|
+
- lib/forester/tree_node_ext/validators.rb
|
90
91
|
- lib/forester/tree_node_ext/views.rb
|
91
92
|
- lib/forester/version.rb
|
92
93
|
- test/simple_tree_helper.rb
|
@@ -94,6 +95,7 @@ files:
|
|
94
95
|
- test/test_mutators.rb
|
95
96
|
- test/test_tree_factory.rb
|
96
97
|
- test/test_tree_node.rb
|
98
|
+
- test/test_validators.rb
|
97
99
|
- test/test_views.rb
|
98
100
|
- test/trees/simple_tree.yml
|
99
101
|
homepage: http://rubygems.org/gems/forester
|
@@ -126,5 +128,6 @@ test_files:
|
|
126
128
|
- test/test_mutators.rb
|
127
129
|
- test/test_tree_factory.rb
|
128
130
|
- test/test_tree_node.rb
|
131
|
+
- test/test_validators.rb
|
129
132
|
- test/test_views.rb
|
130
133
|
- test/trees/simple_tree.yml
|