dag_link_calculator 0.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.
data/bin/rspec ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'rspec' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("rspec-core", "rspec")
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dag_link_calculator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'dag_link_calculator'
8
+ spec.version = DagLinkCalculator::VERSION
9
+ spec.authors = ['Eduardo Turiño']
10
+ spec.email = ['eturino@eturino.com']
11
+ spec.required_ruby_version = '>= 2.1'
12
+
13
+ spec.summary = <<-TXT.gsub(/[\s]*/, ' ')
14
+ Given a list of parent-child relationships, it will return a list of links ancestor-descendant, with count
15
+ TXT
16
+ spec.description = <<-TXT.gsub(/[\s]*/, ' ')
17
+ Given a list of parent-child relationships, it will return a list of links ancestor-descendant, with count.
18
+ Can be used to restore a list of links in `act-as-dag`, providing that the direct links are correct.
19
+ TXT
20
+ spec.homepage = 'https://github.com/artirix/dag_link_calculator'
21
+ spec.license = 'MIT'
22
+
23
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
24
+ f.match(%r{^(test|spec|features)/})
25
+ end
26
+ spec.bindir = 'exe'
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ['lib']
29
+
30
+ spec.add_development_dependency 'bundler', '~> 1.14'
31
+ spec.add_development_dependency 'guard-rspec', '~> 4.7'
32
+ spec.add_development_dependency 'rake', '~> 10.0'
33
+ spec.add_development_dependency 'rspec', '~> 3.0'
34
+ end
@@ -0,0 +1,17 @@
1
+ require 'dag_link_calculator/version'
2
+ require 'dag_link_calculator/cycle_exception'
3
+ require 'dag_link_calculator/node_link'
4
+ require 'dag_link_calculator/direct_link'
5
+ require 'dag_link_calculator/node_route'
6
+ require 'dag_link_calculator/calculator'
7
+
8
+ module DagLinkCalculator
9
+ def self.from_direct_links_structs(direct_links_structs)
10
+ Calculator.new(direct_links_structs)
11
+ end
12
+
13
+ def self.from_direct_links_hashes(direct_links_hashes)
14
+ direct_links_structs = direct_links_hashes.map { |h| DirectLink.from_hash(h) }
15
+ from_direct_links_structs(direct_links_structs)
16
+ end
17
+ end
@@ -0,0 +1,79 @@
1
+ module DagLinkCalculator
2
+ class Calculator
3
+ attr_reader :direct_links_structs
4
+
5
+ def initialize(direct_links_structs)
6
+ @direct_links_structs = direct_links_structs
7
+ end
8
+
9
+ def all_links_hashes
10
+ @all_links_hashes ||= build_all_links_hashes
11
+ end
12
+
13
+ def all_links_structs
14
+ @all_links_structs ||= build_all_links_structs
15
+ end
16
+
17
+ def all_routes_structs
18
+ @all_routes_structs ||= build_all_routes_structs
19
+ end
20
+
21
+ def parents_map
22
+ @parent_map ||= build_parents_map
23
+ end
24
+
25
+ def parents_of(descendant_id)
26
+ parents_map[descendant_id] || []
27
+ end
28
+
29
+ private
30
+
31
+ def build_all_links_hashes
32
+ all_links_structs.map(&:to_hash)
33
+ end
34
+
35
+ def build_all_links_structs
36
+ grouped = all_routes_structs.group_by { |node_route| [node_route.descendant_id, node_route.ancestor_id] }
37
+ grouped.map do |(descendant_id, ancestor_id), list|
38
+ count = list.size
39
+ direct = list.any?(&:direct?)
40
+ NodeLink.new(ancestor_id, descendant_id, direct, count)
41
+ end.sort
42
+ end
43
+
44
+ def build_all_routes_structs
45
+ parents_map.keys.map do |descendant_id|
46
+ routes_for_node descendant_id
47
+ end.flatten.sort
48
+ end
49
+
50
+ def build_parents_map
51
+ direct_links_structs
52
+ .group_by(&:descendant_id)
53
+ .map { |k, list| [k, list.map(&:ancestor_id)] }
54
+ .to_h
55
+ end
56
+
57
+ def routes_for_node(node_id, recursive_ids_list = [])
58
+ @routes_map ||= {}
59
+ @routes_map[node_id] ||= build_routes_for_node node_id, recursive_ids_list
60
+ end
61
+
62
+ def build_routes_for_node(node_id, recursive_ids_list)
63
+ raise CycleException if recursive_ids_list.include? node_id
64
+ recursive_ids_list << node_id
65
+
66
+ parents_of(node_id).map do |parent_id|
67
+ [NodeRoute.new([node_id, parent_id])].concat(build_parent_routes_for(node_id, parent_id, recursive_ids_list))
68
+ end.flatten
69
+ end
70
+
71
+ def build_parent_routes_for(node_id, parent_id, recursive_ids_list)
72
+ routes_for_node(parent_id, recursive_ids_list).map do |r|
73
+ NodeRoute.new([node_id].concat(r.nodes))
74
+ end
75
+ rescue CycleException
76
+ raise CycleException, "nodes #{parent_id.inspect} and #{node_id.inspect} are ancestor and descendant of each other: cycle detected!"
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,4 @@
1
+ module DagLinkCalculator
2
+ class CycleException < StandardError
3
+ end
4
+ end
@@ -0,0 +1,40 @@
1
+ module DagLinkCalculator
2
+ ANCESTOR_KEYS = [:ancestor_id, :ancestor, :parent_id, :parent].freeze
3
+ DESCENDANT_KEYS = [:descendant_id, :descendant, :child_id, :child].freeze
4
+
5
+ DirectLink = Struct.new(:ancestor_id, :descendant_id) do
6
+ def self.from_hash(hash)
7
+ ancestor_id = fetch_from hash, ANCESTOR_KEYS
8
+ descendant_id = fetch_from hash, DESCENDANT_KEYS
9
+ new(ancestor_id, descendant_id)
10
+ end
11
+
12
+ def self.fetch_from(hash, keys)
13
+ keys.each do |k|
14
+ return hash[k] if hash[k]
15
+ end
16
+
17
+ raise KeyError, "none of these keys found in the given hash: #{keys}"
18
+ end
19
+
20
+ def direct
21
+ true
22
+ end
23
+
24
+ def direct?
25
+ direct
26
+ end
27
+
28
+ def count
29
+ 1
30
+ end
31
+
32
+ def to_link
33
+ NodeLink.new(ancestor_id, descendant_id, direct?, count)
34
+ end
35
+
36
+ def <=>(other)
37
+ [ancestor_id, descendant_id] <=> [other.ancestor_id, other.descendant_id]
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,20 @@
1
+ module DagLinkCalculator
2
+ NodeLink = Struct.new(:ancestor_id, :descendant_id, :direct, :count) do
3
+ def direct?
4
+ direct
5
+ end
6
+
7
+ def <=>(other)
8
+ [ancestor_id, descendant_id] <=> [other.ancestor_id, other.descendant_id]
9
+ end
10
+
11
+ def to_hash
12
+ {
13
+ ancestor_id: ancestor_id,
14
+ descendant_id: descendant_id,
15
+ direct: direct,
16
+ count: count,
17
+ }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ module DagLinkCalculator
2
+ NodeRoute = Struct.new(:nodes) do
3
+ def size
4
+ nodes.size
5
+ end
6
+
7
+ def direct?
8
+ size == 2
9
+ end
10
+
11
+ def descendant_id
12
+ nodes.first
13
+ end
14
+
15
+ def ancestor_id
16
+ nodes.last
17
+ end
18
+
19
+ def <=>(other)
20
+ nodes <=> other.nodes
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module DagLinkCalculator
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dag_link_calculator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Eduardo Turiño
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-04-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: guard-rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: " G i v e n a l i s t o f p a r e n t - c h i l d r e l a t i o
70
+ n s h i p s , i t w i l l r e t u r n a l i s t o f l i n k s a n c e s
71
+ t o r - d e s c e n d a n t , w i t h c o u n t . C a n b e u s e d t o r
72
+ e s t o r e a l i s t o f l i n k s i n ` a c t - a s - d a g ` , p r o v
73
+ i d i n g t h a t t h e d i r e c t l i n k s a r e c o r r e c t . "
74
+ email:
75
+ - eturino@eturino.com
76
+ executables: []
77
+ extensions: []
78
+ extra_rdoc_files: []
79
+ files:
80
+ - ".codeclimate.yml"
81
+ - ".coveralls.yml"
82
+ - ".gitignore"
83
+ - ".rspec"
84
+ - ".rubocop.yml"
85
+ - ".ruby-version"
86
+ - ".travis.yml"
87
+ - CODE_OF_CONDUCT.md
88
+ - Gemfile
89
+ - Guardfile
90
+ - LICENSE.txt
91
+ - README.md
92
+ - Rakefile
93
+ - bin/_guard-core
94
+ - bin/console
95
+ - bin/guard
96
+ - bin/rake
97
+ - bin/rspec
98
+ - bin/setup
99
+ - dag_link_calculator.gemspec
100
+ - lib/dag_link_calculator.rb
101
+ - lib/dag_link_calculator/calculator.rb
102
+ - lib/dag_link_calculator/cycle_exception.rb
103
+ - lib/dag_link_calculator/direct_link.rb
104
+ - lib/dag_link_calculator/node_link.rb
105
+ - lib/dag_link_calculator/node_route.rb
106
+ - lib/dag_link_calculator/version.rb
107
+ homepage: https://github.com/artirix/dag_link_calculator
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '2.1'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.6.11
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: G i v e n a l i s t o f p a r e n t - c h i l d r e l a t i o n s h
131
+ i p s , i t w i l l r e t u r n a l i s t o f l i n k s a n c e s t o r
132
+ - d e s c e n d a n t , w i t h c o u n t
133
+ test_files: []