forester 3.2.0 → 4.0.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 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