forester 3.2.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a57950cecbc75dfde48ae961c03f72437f1e3d7
4
- data.tar.gz: 31e075d6b51118542e5a8e0dbca61aee3735e6c4
3
+ metadata.gz: c4bc0ddc9aa53d1bdab802726bbdc14871584d67
4
+ data.tar.gz: d43910fab0a17df6bee538e7822a68faba3616b0
5
5
  SHA512:
6
- metadata.gz: 5f2453667e82901bfd5ce7ba63222262319ec772d3b9e9092d806dbf2eafdb5c57de7d2b737316aed1c8ce647e1b6aecfaf4c0292f571595dc7aabda673d828b
7
- data.tar.gz: 3e02c63d56490f50801b65a085b38407d53d31e725a9e52330cb4c42d0813ac9d02a673172b4ca1c2eb5c4c2eaf8371471688e3b67aa9ef33692310a7a446388
6
+ metadata.gz: 58c9d3199133b793ffc19f7a40dd85b37a6e93e4595b06c974447f091f274c77433d9be1df9291116cf2ba240b49178ab6813766fac65ddabf40f5176341ebd3
7
+ data.tar.gz: 05ca996da70214bfb0e5ba33db69ae8d7782b7955fbff7d72893df50ab99a70e9a13a01f92adfe3a94839bc53ff47cb96691b010b12c5625f224083814bb4f0d
data/Rakefile CHANGED
@@ -5,4 +5,4 @@ Rake::TestTask.new do |t|
5
5
  end
6
6
 
7
7
  desc "Run tests"
8
- task :default => :test
8
+ task :default => :test
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-22'
9
+ s.date = '2016-08-27'
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"]
@@ -27,4 +27,4 @@ Gem::Specification.new do |s|
27
27
  s.add_development_dependency 'minitest', ['~> 5.9']
28
28
  s.add_development_dependency 'pry-byebug', ['~> 3.4']
29
29
 
30
- end
30
+ end
@@ -6,4 +6,4 @@ module Forester
6
6
 
7
7
  end
8
8
  end
9
- end
9
+ end
@@ -87,4 +87,4 @@ module Forester
87
87
 
88
88
  end
89
89
  end
90
- end
90
+ end
@@ -24,4 +24,4 @@ module Forester
24
24
 
25
25
  end
26
26
  end
27
- end
27
+ end
@@ -28,4 +28,4 @@ module Forester
28
28
 
29
29
  end
30
30
  end
31
- end
31
+ end
@@ -3,45 +3,72 @@ module Forester
3
3
 
4
4
  class << self
5
5
 
6
- def from_yaml_file(file)
6
+ def from_yaml_file(file, options = {})
7
+ default_options = {
8
+ max_level: :last
9
+ }
10
+ options = default_options.merge(options)
11
+
7
12
  from_hash_with_root_key(YAML.load_file(file))
8
13
  end
9
14
 
10
- def from_root_hash(hash, children_key = :children)
11
- from_hash_with_root_key({ root: hash }, children_key)
15
+ def from_root_hash(hash, options = {})
16
+ default_options = {
17
+ max_level: :last,
18
+ children_key: :children
19
+ }
20
+ options = default_options.merge(options)
21
+
22
+ from_hash_with_root_key({ root: hash }, options)
12
23
  end
13
24
 
14
- def from_hash_with_root_key(hash, children_key = :children, root_key = :root)
25
+ def from_hash_with_root_key(hash, options = {})
26
+ default_options = {
27
+ max_level: :last,
28
+ children_key: :children,
29
+ root_key: :root
30
+ }
31
+ options = default_options.merge(options)
32
+
15
33
  dummy_root = TreeNode.new('<TEMP>')
16
- real_root = fetch_indifferently(hash, root_key)
34
+ real_root = fetch_indifferently(hash, options[:root_key])
17
35
 
18
- tree = with_children(dummy_root, [real_root], children_key).first_child
19
- tree.detached_subtree_copy
20
- end
36
+ max_level = options[:max_level]
37
+ max_level = -2 if max_level == :last
21
38
 
22
- def from_hash(hash, children_key, uid = SecureRandom.uuid)
23
- name = uid
24
- content = NodeContent::Factory.from_hash(hash, children_key)
25
- TreeNode.new(name, content)
39
+ tree = with_children(dummy_root, [real_root], options[:children_key], max_level + 1).first_child
40
+ tree.detached_subtree_copy
26
41
  end
27
42
 
28
- private
43
+ protected
29
44
 
30
45
  def fetch_indifferently(hash, key, default = nil)
31
46
  [key, key.to_s, key.to_s.to_sym].uniq.map { |k| hash[k] }.compact.first || default || hash.fetch(root_key)
32
47
  end
33
48
 
34
- def with_children(tree_node, children, children_key)
49
+ def with_children(tree_node, children, children_key, levels_remaining)
50
+ return tree_node if levels_remaining == 0
35
51
  children.each do |child|
36
- child_node = from_hash(child, children_key)
37
- child_children = fetch_indifferently(child, children_key, []) # nth level
52
+ child_node = node_from_hash(child, children_key)
53
+ child_children = fetch_indifferently(child, children_key, [])
38
54
 
39
- tree_node << with_children(child_node, child_children, children_key)
55
+ tree_node << with_children(child_node, child_children, children_key, levels_remaining - 1)
40
56
  end
41
57
  tree_node
42
58
  end
43
59
 
60
+ def node_from_hash(hash, children_key, options = {})
61
+ default_options = {
62
+ uid: SecureRandom.uuid
63
+ }
64
+ options = default_options.merge(options)
65
+
66
+ name = options[:uid]
67
+ content = NodeContent::Factory.from_hash(hash, children_key)
68
+ TreeNode.new(name, content)
69
+ end
70
+
44
71
  end
45
72
 
46
73
  end
47
- end
74
+ end
@@ -8,30 +8,37 @@ module Forester
8
8
  include Mutators
9
9
  include Views
10
10
 
11
+ alias_method :max_level, :node_height
12
+ alias_method :each_node, :breadth_each
13
+
11
14
  def nodes_of_level(l)
12
- if l.between?(0, max_level) then each_level.take(l + 1).last else [] end
15
+ l.between?(0, max_level) ? each_level.take(l + 1).last : []
13
16
  end
14
17
 
15
- alias_method :max_level, :node_height
16
-
17
18
  def each_level
18
19
  Enumerator.new do |yielder|
19
20
  level = [self]
20
- begin
21
+ until level.empty?
21
22
  yielder << level
22
23
  level = level.flat_map(&:children)
23
- end until level.empty?
24
+ end
24
25
  end
25
26
  end
26
27
 
27
- alias_method :each_node, :breadth_each
28
+ def get(field, options = {}, &if_missing)
29
+ default_options = {
30
+ default: :raise,
31
+ subtree: false
32
+ }
33
+ options = default_options.merge(options)
34
+
35
+ return own_and_descendants(field, &if_missing) if options[:subtree]
28
36
 
29
- def get(field, default = :raise_error, &block)
30
37
  if has?(field)
31
38
  content.get(field)
32
39
  elsif block_given?
33
40
  yield self
34
- elsif default != :raise_error
41
+ elsif default != :raise
35
42
  default
36
43
  else
37
44
  raise ArgumentError.new("the node \"#{name}\" does not have \"#{field}\"")
@@ -46,5 +53,16 @@ module Forester
46
53
  each_node.map(&:content)
47
54
  end
48
55
 
56
+ def same_as?(other)
57
+ return false unless content == other.content
58
+ return false unless size == other.size
59
+ nodes_of_other = other.each_node.to_a
60
+ each_node.with_index do |n, i|
61
+ next if i == 0
62
+ return false unless n.same_as?(nodes_of_other[i])
63
+ end
64
+ true
65
+ end
66
+
49
67
  end
50
68
  end
@@ -1,82 +1,91 @@
1
1
  module Forester
2
2
  module Aggregators
3
3
 
4
- def nodes_with(content_key, content_values)
5
- each_node.select { |node| not ( Array(node.get(content_key) { :no_match }) & Array(content_values) ).empty? }
4
+ def own_and_descendants(field, &if_missing)
5
+ if_missing = -> (node) { [] } unless block_given?
6
+
7
+ flat_map { |node| Array(node.get(field, &if_missing)) }
8
+ end
9
+
10
+ def nodes_with(field, values, options = {})
11
+ default_options = {
12
+ single: false
13
+ }
14
+ options = default_options.merge(options)
15
+
16
+ method = options[:single] ? :find : :select
17
+ found_nodes = each_node.public_send(method) do |node|
18
+ not ( Array(node.get(field) { :no_match }) & Array(values) ).empty?
19
+ end
20
+
21
+ Array(found_nodes)
6
22
  end
7
23
 
8
24
  def with_ancestry(options = {})
9
25
  default_options = {
10
26
  include_root: true,
11
27
  include_self: true,
12
- descending: true
28
+ descending: true
13
29
  }
14
30
  options = default_options.merge(options)
15
31
 
16
32
  ancestors = self.parentage || []
17
33
  ancestors = ancestors[0...-1] unless options[:include_root]
18
34
  ancestors = ancestors.unshift(self) if options[:include_self]
19
- if options[:descending] then ancestors.reverse else ancestors end
20
- end
21
35
 
22
- def own_and_descendants(field_name, &if_field_missing)
23
- if_field_missing = -> (node) { [] } unless block_given?
24
- flat_map do |node|
25
- Array(node.get(field_name, &if_field_missing))
26
- end
36
+ options[:descending] ? ancestors.reverse : ancestors
27
37
  end
28
38
 
29
39
  def search(options)
30
40
  default_options = {
31
- single_node: true,
41
+ single_node: false,
32
42
  by_field: :name,
33
43
  keywords: :missing_search_keywords,
34
- then_get: :nodes,
35
- of_subtree: true,
44
+ then_get: :nodes, # if :nodes, subtree is ignored
45
+ subtree: true
36
46
  }
37
47
  options = default_options.merge(options)
38
48
 
39
- found_nodes = nodes_with(options[:by_field], options[:keywords])
40
-
41
- found_nodes = found_nodes.slice(0, 1) if options[:single_node]
49
+ found_nodes = nodes_with(options[:by_field], options[:keywords], { single: options[:single_node] } )
42
50
 
43
51
  return found_nodes if options[:then_get] == :nodes
44
52
 
45
53
  found_nodes.flat_map do |node|
46
- if options[:of_subtree]
47
- node.own_and_descendants(options[:then_get])
48
- else
49
- node.get(options[:then_get])
50
- end
54
+ node.get(options[:then_get], { subtree: options[:subtree] })
51
55
  end
52
-
53
56
  end
54
57
 
55
- def values_by_subtree_of_level(options = {})
58
+ def group_by_sibling_subtrees(options = {})
56
59
  default_options = {
57
- level: 1,
58
- group_field: 'name',
59
- aggregation_field: 'value',
60
- if_field_missing: -> (node) { [] },
61
- include_ancestry_in_keys: false, # if false, with_root is ignored
62
- with_root: false,
60
+ level: 1,
61
+ group_field: 'name',
62
+ aggregation_field: 'value',
63
+ if_field_missing: -> (node) { [] },
64
+ ancestry_in_keys: false, # if false, with_root is ignored
65
+ with_root: false
63
66
  }
64
67
  options = default_options.merge(options)
65
68
 
66
69
  nodes_of_level(options[:level]).each_with_object({}) do |node, hash|
70
+ key =
71
+ if options[:ancestry_in_keys]
72
+ nodes_for_key = node.with_ancestry({ include_root: options[:with_root] })
73
+ nodes_for_key.map { |n| get_or_id(n, options[:group_field]) }
74
+ else
75
+ get_or_id(node, options[:group_field])
76
+ end
67
77
 
68
- key_nodes = if options[:include_ancestry_in_keys]
69
- node.with_ancestry({ include_root: options[:with_root] })
70
- else
71
- node
72
- end
73
-
74
- key = key_nodes.map { |kn| kn.get(options[:group_field]) { |n| n.object_id } }
75
78
  value = node.own_and_descendants(options[:aggregation_field], &options[:if_field_missing])
76
79
 
77
80
  hash[key] = value
78
81
  end
79
82
  end
80
83
 
84
+ private
85
+
86
+ def get_or_id(node, field)
87
+ node.get(field) { |n| n.object_id }
88
+ end
89
+
81
90
  end
82
- end
91
+ end
@@ -1,13 +1,20 @@
1
1
  module Forester
2
2
  module Mutators
3
3
 
4
- def add_field!(name, definition)
5
- value = if definition.respond_to? :call then definition.call(self) else definition end
6
- put!(name, value)
7
- end
4
+ def add_field!(name, definition, options = {})
5
+ default_options = {
6
+ subtree: true
7
+ }
8
+ options = default_options.merge(options)
8
9
 
9
- def add_field_to_subtree!(name, definition)
10
- each_node { |node| node.add_field!(name, definition) }
10
+ if options[:subtree]
11
+ each_node do |node|
12
+ node.add_field!(name, definition, options.merge({ subtree: false }))
13
+ end
14
+ else
15
+ value = definition.respond_to?(:call) ? definition.call(self) : definition
16
+ put!(name, value)
17
+ end
11
18
  end
12
19
 
13
20
  def remove_levels_past!(last_level_to_keep)
@@ -16,4 +23,4 @@ module Forester
16
23
  end
17
24
 
18
25
  end
19
- end
26
+ end
@@ -1,9 +1,11 @@
1
1
  module Forester
2
2
  module Views
3
3
 
4
- def as_nested_hash(options = {})
4
+ def as_root_hash(options = {})
5
5
  default_options = {
6
6
  fields_to_include: fields, # all of them
7
+ max_level: :last,
8
+ children_key: :children,
7
9
  stringify_keys: false,
8
10
  symbolize_keys: false
9
11
  }
@@ -11,15 +13,22 @@ module Forester
11
13
 
12
14
  hash = content.to_hash(options)
13
15
 
14
- children_key = :children
16
+ children_key = options[:children_key]
15
17
  children_key = children_key.to_s if options[:stringify_keys]
16
18
 
17
- hash.merge(
18
- {
19
- children_key => children.map { |node| node.as_nested_hash(options) }
20
- }
21
- )
19
+ max_level = options[:max_level]
20
+ max_level = -1 if max_level == :last
21
+
22
+ next_children =
23
+ if max_level == 0
24
+ []
25
+ else
26
+ next_options = options.merge({ max_level: max_level - 1 })
27
+ children.map { |node| node.as_root_hash(next_options) }
28
+ end
29
+
30
+ hash.merge({ children_key => next_children })
22
31
  end
23
32
 
24
33
  end
25
- end
34
+ end
@@ -1,7 +1,7 @@
1
1
  module Forester
2
2
  class Version
3
- MAJOR = 3
4
- MINOR = 2
3
+ MAJOR = 4
4
+ MINOR = 0
5
5
  PATCH = 0
6
6
  PRE = nil
7
7
 
@@ -0,0 +1,5 @@
1
+ module SimpleTreeHelper
2
+ PATH_TO_TREES = "#{File.dirname(__FILE__)}/trees"
3
+ PATH_TO_SIMPLE_TREE = "#{PATH_TO_TREES}/simple_tree.yml"
4
+ @@tree = Forester::TreeFactory.from_yaml_file(PATH_TO_SIMPLE_TREE)
5
+ end
@@ -0,0 +1,94 @@
1
+ require 'minitest/autorun'
2
+ require 'forester'
3
+
4
+ require_relative './simple_tree_helper'
5
+
6
+ class TestAggregators < Minitest::Test
7
+
8
+ include SimpleTreeHelper
9
+
10
+ def test_group_by_sibling_subtrees
11
+
12
+ expected = {
13
+ "First node of level 2" => ["Already in level 2", "I want to be the very best", "like no one ever was"],
14
+ "Second node of level 2" => ["I have a sibling to my left", "She wants to catch them all"],
15
+ "Third node of level 2" => ["Reached level 3", "It's dark", "A hidden secret lies in the deepest leaves...", "Just kidding.", "Could forester handle trees with hundreds of levels?", "Maybe."]
16
+ }
17
+
18
+ actual = @@tree.group_by_sibling_subtrees(
19
+ level: 2,
20
+ aggregation_field: 'strings'
21
+ )
22
+
23
+ assert_equal expected, actual
24
+ end
25
+
26
+ def test_group_by_sibling_subtrees_with_ancestry
27
+
28
+ expected = {
29
+ ["First node of level 1", "First node of level 2"] => ["Already in level 2", "I want to be the very best", "like no one ever was"],
30
+ ["First node of level 1", "Second node of level 2"] => ["I have a sibling to my left", "She wants to catch them all"],
31
+ ["Second node of level 1", "Third node of level 2"] => ["Reached level 3", "It's dark", "A hidden secret lies in the deepest leaves...", "Just kidding.", "Could forester handle trees with hundreds of levels?", "Maybe."]
32
+ }
33
+
34
+ actual = @@tree.group_by_sibling_subtrees(
35
+ level: 2,
36
+ aggregation_field: 'strings',
37
+ ancestry_in_keys: true
38
+ )
39
+
40
+ assert_equal expected, actual
41
+ end
42
+
43
+ def test_nodes_with
44
+ expected_names = ["A hidden secret lies in the deepest leaves...", "Just kidding.", "Could forester handle trees with hundreds of levels?", "Maybe."]
45
+
46
+ found_nodes = @@tree.nodes_with('name', 'Second node of level 3')
47
+ assert_equal 1, found_nodes.length
48
+
49
+ actual_names = found_nodes.flat_map do |node|
50
+ node.own_and_descendants('strings')
51
+ end
52
+
53
+ assert_equal expected_names, actual_names
54
+ end
55
+
56
+ def test_search
57
+
58
+ expected = [7]
59
+
60
+ actual_1 = @@tree.search({
61
+ by_field: 'name',
62
+ keywords: 'Second node of level 3',
63
+ then_get: 'value',
64
+ subtree: false
65
+ })
66
+
67
+ actual_2 = @@tree.search({
68
+ by_field: 'name',
69
+ keywords: ['Second node of level 3', 'Not present name'],
70
+ then_get: 'value',
71
+ subtree: false
72
+ })
73
+
74
+ actual_3 = @@tree.search({
75
+ by_field: 'strings',
76
+ keywords: 'A hidden secret lies in the deepest leaves...',
77
+ then_get: 'value',
78
+ subtree: false
79
+ })
80
+ assert_equal expected, actual_1
81
+ assert_equal expected, actual_2
82
+ assert_equal expected, actual_3
83
+
84
+ expected_values = [7, 8, 9]
85
+ actual_values = @@tree.search({
86
+ by_field: 'name',
87
+ keywords: 'Second node of level 3',
88
+ then_get: 'value',
89
+ subtree: true
90
+ })
91
+ assert_equal expected_values, actual_values
92
+ end
93
+
94
+ end
@@ -1,29 +1,34 @@
1
1
  require 'minitest/autorun'
2
2
  require 'forester'
3
3
 
4
+ require_relative './simple_tree_helper'
5
+
4
6
  class TestMutators < Minitest::Test
5
7
 
6
- def test_mutators
8
+ def setup
9
+ path_to_trees = "#{File.dirname(__FILE__)}/trees"
10
+ path_to_simple_tree = "#{path_to_trees}/simple_tree.yml"
11
+ @tree = Forester::TreeFactory.from_yaml_file(path_to_simple_tree)
12
+ end
7
13
 
8
- path_to_trees = "#{File.dirname(__FILE__)}/trees"
9
- tree = Forester::TreeFactory.from_yaml_file("#{path_to_trees}/simple_tree.yml")
14
+ def test_add_field
10
15
 
11
- tree.add_field_to_subtree!('number_four', 4)
16
+ @tree.add_field!('number_four', 4)
12
17
 
13
- assert_equal 4, tree.get(:number_four)
14
- assert_equal 4, tree.get('number_four')
18
+ assert_equal 4, @tree.get(:number_four)
19
+ assert_equal 4, @tree.get('number_four')
15
20
 
16
- tree.add_field_to_subtree!(:number_five, 5)
21
+ @tree.add_field!(:number_five, 5)
17
22
 
18
- assert_equal 5, tree.get(:number_five)
19
- assert_equal 5, tree.get('number_five')
23
+ assert_equal 5, @tree.get(:number_five)
24
+ assert_equal 5, @tree.get('number_five')
20
25
 
21
26
  number_one = 1
22
27
 
23
- tree.add_field_to_subtree!(:number_six, -> (node) { node.get(:number_five) + number_one })
28
+ @tree.add_field!(:number_six, -> (node) { node.get(:number_five) + number_one })
24
29
 
25
- assert_equal 6, tree.get(:number_six)
30
+ assert_equal 6, @tree.get(:number_six)
26
31
 
27
32
  end
28
33
 
29
- end
34
+ end
@@ -0,0 +1,33 @@
1
+ require 'minitest/autorun'
2
+ require 'forester'
3
+
4
+ require_relative './simple_tree_helper'
5
+
6
+ class TestTreeFactory < Minitest::Test
7
+
8
+ include SimpleTreeHelper
9
+
10
+ def test_from_root_hash
11
+ hash = YAML.load_file(PATH_TO_SIMPLE_TREE)
12
+
13
+ whole_trees = [:last, 29, 4].map { |ml| new_with_max_level(hash, ml) }
14
+
15
+ assert(whole_trees.product(whole_trees).all? { |t1, t2| t1.same_as?(t2) })
16
+
17
+ pruned_trees = (0..2).map { |ml| new_with_max_level(hash, ml) }
18
+
19
+ pruned_trees.each_with_index do |t, i|
20
+ assert_equal(i, t.max_level)
21
+
22
+ pruned = whole_trees[i].remove_levels_past!(i)
23
+ assert(t.same_as?(pruned))
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ def new_with_max_level(hash, max_level)
30
+ Forester::TreeFactory.from_root_hash(hash['root'], { max_level: max_level })
31
+ end
32
+
33
+ end
@@ -0,0 +1,34 @@
1
+ require 'minitest/autorun'
2
+ require 'forester'
3
+
4
+ require_relative './simple_tree_helper'
5
+
6
+ class TestTreeNode < Minitest::Test
7
+
8
+ include SimpleTreeHelper
9
+
10
+ def test_size
11
+ assert_equal 10, @@tree.size
12
+ end
13
+
14
+ def test_values
15
+ expected = (0..9).reduce(:+)
16
+ actual = @@tree.reduce(0) { |acum, node| acum + node.get('value') }
17
+
18
+ assert_equal expected, actual
19
+ end
20
+
21
+ def test_levels
22
+ expected = [
23
+ ["root"],
24
+ ["First node of level 1", "Second node of level 1"],
25
+ ["First node of level 2", "Second node of level 2", "Third node of level 2"],
26
+ ["First node of level 3", "Second node of level 3"],
27
+ ["First node of level 4", "Second node of level 4"]
28
+ ]
29
+ actual = @@tree.each_level.map { |l| l.map { |n| n.get('name') } }.to_a
30
+
31
+ assert_equal expected, actual
32
+ end
33
+
34
+ end
data/test/test_views.rb CHANGED
@@ -1,18 +1,20 @@
1
1
  require 'minitest/autorun'
2
2
  require 'forester'
3
3
 
4
- class TestViews < Minitest::Test
4
+ require_relative './simple_tree_helper'
5
5
 
6
- def test_views
6
+ class TestViews < Minitest::Test
7
7
 
8
- path_to_trees = "#{File.dirname(__FILE__)}/trees"
9
- tree = Forester::TreeFactory.from_yaml_file("#{path_to_trees}/simple_tree.yml")
8
+ include SimpleTreeHelper
10
9
 
11
- hash = (YAML.load(File.read("#{path_to_trees}/simple_tree.yml")))
10
+ def test_as_root_hash
11
+ hash = (YAML.load(File.read(PATH_TO_SIMPLE_TREE)))
12
12
  add_empty_children_keys(hash['root'])
13
13
 
14
- assert_equal hash['root'], tree.as_nested_hash({ stringify_keys: true })
14
+ expected = hash['root']
15
+ actual = @@tree.as_root_hash({ stringify_keys: true })
15
16
 
17
+ assert_equal expected, actual
16
18
  end
17
19
 
18
20
  private
@@ -22,4 +24,4 @@ class TestViews < Minitest::Test
22
24
  hash['children'].each { |child| add_empty_children_keys(child) }
23
25
  end
24
26
 
25
- end
27
+ end
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: 3.2.0
4
+ version: 4.0.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-22 00:00:00.000000000 Z
11
+ date: 2016-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubytree
@@ -89,8 +89,11 @@ files:
89
89
  - lib/forester/tree_node_ext/mutators.rb
90
90
  - lib/forester/tree_node_ext/views.rb
91
91
  - lib/forester/version.rb
92
+ - test/simple_tree_helper.rb
93
+ - test/test_aggregators.rb
92
94
  - test/test_mutators.rb
93
- - test/test_treenode.rb
95
+ - test/test_tree_factory.rb
96
+ - test/test_tree_node.rb
94
97
  - test/test_views.rb
95
98
  - test/trees/simple_tree.yml
96
99
  homepage: http://rubygems.org/gems/forester
@@ -118,7 +121,10 @@ signing_key:
118
121
  specification_version: 4
119
122
  summary: A gem to represent and interact with tree data structures
120
123
  test_files:
124
+ - test/simple_tree_helper.rb
125
+ - test/test_aggregators.rb
121
126
  - test/test_mutators.rb
122
- - test/test_treenode.rb
127
+ - test/test_tree_factory.rb
128
+ - test/test_tree_node.rb
123
129
  - test/test_views.rb
124
130
  - test/trees/simple_tree.yml
@@ -1,84 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'forester'
3
-
4
- class TestTreeNode < Minitest::Test
5
-
6
- def test_tree_node
7
-
8
- path_to_trees = "#{File.dirname(__FILE__)}/trees"
9
- tree = Forester::TreeFactory.from_yaml_file("#{path_to_trees}/simple_tree.yml")
10
-
11
- assert_equal 10, tree.size
12
-
13
- assert_equal (0..9).reduce(:+), tree.reduce(0) { |acum, node| acum + node.get('value') }
14
-
15
- expected_levels = [
16
- ["root"],
17
- ["First node of level 1", "Second node of level 1"],
18
- ["First node of level 2", "Second node of level 2", "Third node of level 2"],
19
- ["First node of level 3", "Second node of level 3"],
20
- ["First node of level 4", "Second node of level 4"]
21
- ]
22
-
23
- assert_equal expected_levels, tree.each_level.map { |level| level.map { |n| n.get('name') } }.to_a
24
-
25
- expected = {
26
- ["First node of level 1", "First node of level 2"] => ["Already in level 2", "I want to be the very best", "like no one ever was"],
27
- ["First node of level 1", "Second node of level 2"] => ["I have a sibling to my left", "She wants to catch them all"],
28
- ["Second node of level 1", "Third node of level 2"] => ["Reached level 3", "It's dark", "A hidden secret lies in the deepest leaves...", "Just kidding.", "Could forester handle trees with hundreds of levels?", "Maybe."]}
29
-
30
- aggregation_result = tree.values_by_subtree_of_level(level: 2, aggregation_field: 'strings', include_ancestry_in_keys: true)
31
-
32
- assert_equal expected, aggregation_result
33
-
34
-
35
- expected_value = [7]
36
- actual_value = tree.search({
37
- by_field: 'name',
38
- keywords: 'Second node of level 3',
39
- then_get: 'value',
40
- of_subtree: false,
41
- })
42
- assert_equal expected_value, actual_value
43
-
44
- expected_value = [7]
45
- actual_value = tree.search({
46
- by_field: 'name',
47
- keywords: ['Second node of level 3', 'Not present name'],
48
- then_get: 'value',
49
- of_subtree: false,
50
- })
51
- assert_equal expected_value, actual_value
52
-
53
- expected_values = [7, 8, 9]
54
- actual_values = tree.search({
55
- by_field: 'name',
56
- keywords: 'Second node of level 3',
57
- then_get: 'value',
58
- of_subtree: true,
59
- })
60
- assert_equal expected_values, actual_values
61
-
62
- expected_value = [7]
63
- actual_value = tree.search({
64
- by_field: 'strings',
65
- keywords: 'A hidden secret lies in the deepest leaves...',
66
- then_get: 'value',
67
- of_subtree: false
68
- })
69
- assert_equal expected_value, actual_value
70
-
71
-
72
- expected_names = ["A hidden secret lies in the deepest leaves...", "Just kidding.", "Could forester handle trees with hundreds of levels?", "Maybe."]
73
-
74
- found_nodes = tree.nodes_with('name', 'Second node of level 3')
75
- assert_equal 1, found_nodes.length
76
-
77
- actual_names = found_nodes.flat_map do |node|
78
- node.own_and_descendants('strings')
79
- end
80
-
81
- assert_equal expected_names, actual_names
82
- end
83
-
84
- end