drain 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.
- checksums.yaml +7 -0
- data/.document +3 -0
- data/.gitignore +2 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +4 -0
- data/LICENSE.txt +20 -0
- data/README.md +25 -0
- data/Rakefile +34 -0
- data/drain.gemspec +59 -0
- data/gemspec.yml +13 -0
- data/lib/drain.rb +1 -0
- data/lib/drain/base.rb +5 -0
- data/lib/drain/base/bool.rb +25 -0
- data/lib/drain/base/encoding.rb +43 -0
- data/lib/drain/base/eruby.rb +28 -0
- data/lib/drain/base/functional.rb +41 -0
- data/lib/drain/base/graph.rb +213 -0
- data/lib/drain/parse.rb +5 -0
- data/lib/drain/parse/simple_parser.rb +61 -0
- data/lib/drain/parse/time_parse.rb +71 -0
- data/lib/drain/ruby_ext.rb +5 -0
- data/lib/drain/ruby_ext/core_ext.rb +203 -0
- data/lib/drain/ruby_ext/meta_ext.rb +211 -0
- data/lib/drain/tools.rb +5 -0
- data/lib/drain/tools/git.rb +116 -0
- data/lib/drain/tools/gtk.rb +49 -0
- data/lib/drain/version.rb +4 -0
- data/test/helper.rb +2 -0
- data/test/test_drain.rb +12 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4801bbbd9081149ae66059ccabf69e10a6002c16
|
4
|
+
data.tar.gz: bd24d077d42a458160c61e985a8f3e2e1d14f323
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f6c18209c458007657d6bc26e79d34f64a50de127c4db0b4d1dd525f3de950eb3d2cf8543a816aaddb4f322fc146e7bde45645f0af0bc95dd01360afe4fd3015
|
7
|
+
data.tar.gz: 0f64e58698354e4b75385aaa8127eedad9d55d6413fe141f3dea621eeacffb94cc531d3f80ea892ab16465134e28d9879c10884a835f43376de8bc609a615947
|
data/.document
ADDED
data/.gitignore
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown -M kramdown --title "drain Documentation" --protected
|
data/ChangeLog.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2015 Damien Robert
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# drain
|
2
|
+
|
3
|
+
* [Homepage](https://github.com/DamienRobert/drain#readme)
|
4
|
+
* [Gems]("https://rubygems.org/gems/drain)
|
5
|
+
* [Issues](https://github.com/DamienRobert/drain/issues)
|
6
|
+
* [Documentation](http://rubydoc.info/gems/drain/frames)
|
7
|
+
* [Email](mailto:Damien.Olivier.Robert+gems at gmail.com)
|
8
|
+
|
9
|
+
## Description
|
10
|
+
|
11
|
+
Drain is a small set of libraries that I use in my other gems.
|
12
|
+
The Api is far from stable yet, so use at your own risk!
|
13
|
+
|
14
|
+
## Install
|
15
|
+
|
16
|
+
$ gem install drain
|
17
|
+
|
18
|
+
## Copyright
|
19
|
+
|
20
|
+
Copyright (c) 2015 Damien Robert
|
21
|
+
|
22
|
+
MIT License. See {file:LICENSE.txt} for details.
|
23
|
+
|
24
|
+
Some of the code is inspired by other project, in general I give proper
|
25
|
+
Acknowledgement in the corresponding file.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
|
6
|
+
begin
|
7
|
+
gem 'rubygems-tasks', '~> 0.2'
|
8
|
+
require 'rubygems/tasks'
|
9
|
+
|
10
|
+
Gem::Tasks.new
|
11
|
+
rescue LoadError => e
|
12
|
+
warn e.message
|
13
|
+
warn "Run `gem install rubygems-tasks` to install Gem::Tasks."
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'rake/testtask'
|
17
|
+
|
18
|
+
Rake::TestTask.new do |test|
|
19
|
+
test.libs << 'test'
|
20
|
+
test.pattern = 'test/**/test_*.rb'
|
21
|
+
test.verbose = true
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
gem 'yard', '~> 0.8'
|
26
|
+
require 'yard'
|
27
|
+
|
28
|
+
YARD::Rake::YardocTask.new
|
29
|
+
rescue LoadError => e
|
30
|
+
task :yard do
|
31
|
+
abort "Please run `gem install yard` to install YARD."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
task :doc => :yard
|
data/drain.gemspec
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gemspec = YAML.load_file('gemspec.yml')
|
7
|
+
|
8
|
+
gem.name = gemspec.fetch('name')
|
9
|
+
gem.version = gemspec.fetch('version') do
|
10
|
+
lib_dir = File.join(File.dirname(__FILE__),'lib')
|
11
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
12
|
+
|
13
|
+
require 'drain/version'
|
14
|
+
Drain::VERSION
|
15
|
+
end
|
16
|
+
|
17
|
+
gem.summary = gemspec['summary']
|
18
|
+
gem.description = gemspec['description']
|
19
|
+
gem.licenses = Array(gemspec['license'])
|
20
|
+
gem.authors = Array(gemspec['authors'])
|
21
|
+
gem.email = gemspec['email']
|
22
|
+
gem.homepage = gemspec['homepage']
|
23
|
+
|
24
|
+
glob = lambda { |patterns| gem.files & Dir[*patterns] }
|
25
|
+
|
26
|
+
gem.files = `git ls-files`.split($/)
|
27
|
+
gem.files = glob[gemspec['files']] if gemspec['files']
|
28
|
+
|
29
|
+
gem.executables = gemspec.fetch('executables') do
|
30
|
+
glob['bin/*'].map { |path| File.basename(path) }
|
31
|
+
end
|
32
|
+
gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
|
33
|
+
|
34
|
+
gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
|
35
|
+
gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
|
36
|
+
|
37
|
+
gem.require_paths = Array(gemspec.fetch('require_paths') {
|
38
|
+
%w[ext lib].select { |dir| File.directory?(dir) }
|
39
|
+
})
|
40
|
+
|
41
|
+
gem.requirements = gemspec['requirements']
|
42
|
+
gem.required_ruby_version = gemspec['required_ruby_version']
|
43
|
+
gem.required_rubygems_version = gemspec['required_rubygems_version']
|
44
|
+
gem.post_install_message = gemspec['post_install_message']
|
45
|
+
|
46
|
+
split = lambda { |string| string.split(/,\s*/) }
|
47
|
+
|
48
|
+
if gemspec['dependencies']
|
49
|
+
gemspec['dependencies'].each do |name,versions|
|
50
|
+
gem.add_dependency(name,split[versions])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if gemspec['development_dependencies']
|
55
|
+
gemspec['development_dependencies'].each do |name,versions|
|
56
|
+
gem.add_development_dependency(name,split[versions])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/gemspec.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
name: drain
|
2
|
+
summary: "Use a drain for a dryer ruby!"
|
3
|
+
description: |
|
4
|
+
Drain is a small set of libraries that I use in my other gems.
|
5
|
+
license: MIT
|
6
|
+
authors: Damien Robert
|
7
|
+
email: Damien.Olivier.Robert+gems@gmail.com
|
8
|
+
homepage: https://github.com/DamienRobert/drain#readme
|
9
|
+
|
10
|
+
development_dependencies:
|
11
|
+
minitest: ~> 5.0
|
12
|
+
rubygems-tasks: ~> 0.2
|
13
|
+
yard: ~> 0.8
|
data/lib/drain.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'drain/version'
|
data/lib/drain/base.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module DR
|
2
|
+
module Bool
|
3
|
+
extend(self)
|
4
|
+
def to_bool(el, default=nil)
|
5
|
+
case el
|
6
|
+
when String
|
7
|
+
string=el.chomp
|
8
|
+
return true if string =~ (/(true|t|yes|y|1)$/i)
|
9
|
+
return false if string.empty? || string =~ (/(false|f|no|n|0)$/i)
|
10
|
+
when Fixnum
|
11
|
+
return ! (el == 0)
|
12
|
+
when Process::Status
|
13
|
+
exitstatus=el.exitstatus
|
14
|
+
return exitstatus == 0
|
15
|
+
else
|
16
|
+
return true if el == true
|
17
|
+
return false if el == false
|
18
|
+
#we don't return !!el because we don't want nil to be false but to
|
19
|
+
#give an error
|
20
|
+
end
|
21
|
+
return default unless default.nil?
|
22
|
+
raise ArgumentError.new("Invalid value for Boolean: \"#{el}\"")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'iconv' unless String.method_defined?(:encode)
|
2
|
+
#cf http://stackoverflow.com/questions/2982677/ruby-1-9-invalid-byte-sequence-in-utf-8
|
3
|
+
module DR
|
4
|
+
module Encoding
|
5
|
+
module_function
|
6
|
+
#if a mostly utf8 has some mixed in latin1 characters, replace the
|
7
|
+
#invalid characters
|
8
|
+
def fix_utf8(s=nil)
|
9
|
+
s=self if s.nil? #if we are included
|
10
|
+
if String.method_defined?(:scrub)
|
11
|
+
#Ruby 2.1
|
12
|
+
#cf http://ruby-doc.org/core-2.1.0/String.html#method-i-scrub
|
13
|
+
return s.scrub {|bytes| '<'+bytes.unpack('H*')[0]+'>' }
|
14
|
+
else
|
15
|
+
return DR::Encoding.to_utf8(s)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_utf8(s=nil,from:nil)
|
20
|
+
s=self if s.nil? #if we are included
|
21
|
+
from=s.encoding if from.nil?
|
22
|
+
if String.method_defined?(:encode)
|
23
|
+
#Ruby 1.9
|
24
|
+
return s.encode('UTF-8',from, :invalid => :replace, :undef => :replace,
|
25
|
+
:fallback => Proc.new { |bytes| '<'+bytes.unpack('H*')[0]+'>' }
|
26
|
+
)
|
27
|
+
else
|
28
|
+
#Ruby 1.8
|
29
|
+
ic = Iconv.new(from, 'UTF-8//IGNORE')
|
30
|
+
return ic.iconv(s)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
#assume ruby>=1.9 here
|
35
|
+
def to_utf8!(s=nil,from:nil)
|
36
|
+
s=self if s.nil? #if we are included
|
37
|
+
from=s.encoding if from.nil?
|
38
|
+
return s.encode!('UTF-8',from, :invalid => :replace, :undef => :replace,
|
39
|
+
:fallback => Proc.new { |bytes| '<'+bytes.unpack('H*')[0]+'>' }
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module DR
|
2
|
+
module Eruby
|
3
|
+
begin
|
4
|
+
require 'erubis'
|
5
|
+
Erb=::Erubis::Eruby
|
6
|
+
rescue LoadError
|
7
|
+
require 'erb'
|
8
|
+
Erb=::ERB
|
9
|
+
end
|
10
|
+
def erb_include(template, opt={})
|
11
|
+
opt={bind: binding}.merge(opt)
|
12
|
+
file=File.expand_path(template)
|
13
|
+
Dir.chdir(File.dirname(file)) do |cwd|
|
14
|
+
erb = Erb.new(File.read(file))
|
15
|
+
#if context is not empty, then we probably want to evaluate
|
16
|
+
if opt[:evaluate] or opt[:context]
|
17
|
+
r=erb.evaluate(opt[:context])
|
18
|
+
else
|
19
|
+
r=erb.result(opt[:bind])
|
20
|
+
end
|
21
|
+
#if using erubis, it is better to invoke the template in <%= =%> than
|
22
|
+
#to use chop=true
|
23
|
+
r=r.chomp if opt[:chomp]
|
24
|
+
return r
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module DR
|
2
|
+
module Lambda
|
3
|
+
extend self
|
4
|
+
#standard ruby: col.map(&f)
|
5
|
+
#Here: Lambda.map(f,col1,col2,...)
|
6
|
+
#Other implementation:
|
7
|
+
#(shift cols).zip(cols).map {|a| f.call(*a)}
|
8
|
+
#but our implementation stops as soon as a collection is empty
|
9
|
+
#whereas the zip implementation use the length of the first collection
|
10
|
+
#and pads with nil
|
11
|
+
def map(f,*cols)
|
12
|
+
cols=cols.map(&:each)
|
13
|
+
r=[]
|
14
|
+
loop do
|
15
|
+
r<<f.call(*cols.map(&:next))
|
16
|
+
end
|
17
|
+
r
|
18
|
+
rescue StopIteration
|
19
|
+
end
|
20
|
+
|
21
|
+
#like map but return an enumerator
|
22
|
+
def enum_map(f,*cols)
|
23
|
+
cols=cols.map(&:each)
|
24
|
+
Enumerator.new do |y|
|
25
|
+
loop do
|
26
|
+
y<<f.call(*cols.map(&:next))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#compose a list of functions
|
32
|
+
def compose(*f)
|
33
|
+
f.reverse!
|
34
|
+
first=f.shift
|
35
|
+
return lambda do |*args,&b|
|
36
|
+
v=first.call(*args,&b)
|
37
|
+
f.reduce(v) {|v,fun| fun.call(v)}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'set'
|
2
|
+
#Originally inspired by depgraph: https://github.com/dcadenas/depgraph
|
3
|
+
|
4
|
+
module DR
|
5
|
+
class Node
|
6
|
+
include Enumerable
|
7
|
+
attr_reader :graph
|
8
|
+
attr_accessor :name, :attributes, :parents, :children
|
9
|
+
def initialize(name, attributes: nil, graph: nil)
|
10
|
+
@name = name
|
11
|
+
@children = []
|
12
|
+
@parents = []
|
13
|
+
@attributes = attributes
|
14
|
+
@graph=graph
|
15
|
+
graph.nodes << self if @graph
|
16
|
+
end
|
17
|
+
def each
|
18
|
+
@children.each
|
19
|
+
end
|
20
|
+
def <=>(other)
|
21
|
+
return @name <=> other.name
|
22
|
+
end
|
23
|
+
#self.add_child(ploum) marks ploum as a child of self (ie ploum depends on self)
|
24
|
+
def add_child(*nodes)
|
25
|
+
nodes.each do |node|
|
26
|
+
if not @children.include?(node)
|
27
|
+
@children << node
|
28
|
+
node.parents << self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
def rm_child(*nodes)
|
33
|
+
nodes.each do |node|
|
34
|
+
if @children.include?(node)
|
35
|
+
@children.delete(node)
|
36
|
+
node.parents.delete(self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
def add_parent(*nodes)
|
41
|
+
nodes.each do |node|
|
42
|
+
if not @parents.include?(node)
|
43
|
+
@parents << node
|
44
|
+
node.children << self
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
def rm_parent(*nodes)
|
49
|
+
nodes.each do |node|
|
50
|
+
if @parents.include?(node)
|
51
|
+
@parents.delete(node)
|
52
|
+
node.children.delete(self)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
STEP = 4
|
58
|
+
def to_s
|
59
|
+
return @name
|
60
|
+
end
|
61
|
+
def to_graph(indent_level: 0)
|
62
|
+
sout = ""
|
63
|
+
margin = ''
|
64
|
+
0.upto(indent_level/STEP-1) { |p| margin += (p==0 ? ' ' : '|') + ' '*(STEP - 1) }
|
65
|
+
margin += '|' + '-'*(STEP - 2)
|
66
|
+
sout += margin + "#{@name}\n"
|
67
|
+
@children.each do |child|
|
68
|
+
sout += child.to_graph(indent_level: indent_level+STEP)
|
69
|
+
end
|
70
|
+
return sout
|
71
|
+
end
|
72
|
+
def to_dot
|
73
|
+
sout=["\""+name+"\""]
|
74
|
+
@children.each do |child|
|
75
|
+
sout.push "\"#{@name}\" -> \"#{child.name}\""
|
76
|
+
sout += child.to_dot
|
77
|
+
end
|
78
|
+
return sout
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class Graph
|
83
|
+
attr_accessor :nodes
|
84
|
+
include Enumerable
|
85
|
+
def initialize(g=nil)
|
86
|
+
@nodes=[]
|
87
|
+
if g #convert a hash to a graph
|
88
|
+
g.each do |name,children|
|
89
|
+
n=build(name)
|
90
|
+
n.add_child(*children)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
def build(node, children: [], parents: [], **keywords)
|
95
|
+
graph_node=
|
96
|
+
case node
|
97
|
+
when Node
|
98
|
+
match = @nodes.find {|n| n == node} and return match
|
99
|
+
Node.new(node.name, graph: self, **keywords.merge({attributes: node.attributes||keywords[:attributes]}))
|
100
|
+
node.children.each do |c|
|
101
|
+
build(c,**keywords)
|
102
|
+
end
|
103
|
+
else
|
104
|
+
match = @nodes.find {|n| n.name == node}
|
105
|
+
match || Node.new(node, graph: self, **keywords)
|
106
|
+
end
|
107
|
+
graph_node.add_child(*children.map { |child| build(child) })
|
108
|
+
graph_node.add_parent(*parents.map { |child| build(child) })
|
109
|
+
return graph_node
|
110
|
+
end
|
111
|
+
def each
|
112
|
+
@nodes.each
|
113
|
+
end
|
114
|
+
def to_a
|
115
|
+
return @nodes
|
116
|
+
end
|
117
|
+
def all
|
118
|
+
@nodes.sort
|
119
|
+
end
|
120
|
+
def roots
|
121
|
+
@nodes.select{ |n| n.parents.length == 0}.sort
|
122
|
+
end
|
123
|
+
def dump(mode: :graph, nodes_list: :roots, **unused)
|
124
|
+
n=case nodes_list
|
125
|
+
when :roots; roots
|
126
|
+
when :all; all
|
127
|
+
when Symbol; nodes.select {|n| n.attributes[:nodes_list]}
|
128
|
+
else nodes_list.to_a
|
129
|
+
end
|
130
|
+
sout = ""
|
131
|
+
case mode
|
132
|
+
when :graph; n.each do |node| sout+=node.to_graph end
|
133
|
+
when :list; n.each do |i| sout+="- #{i}\n" end
|
134
|
+
when :dot;
|
135
|
+
sout+="digraph gems {\n"
|
136
|
+
sout+=n.map {|node| node.to_dot}.inject(:+).uniq!.join("\n")
|
137
|
+
sout+="}\n"
|
138
|
+
end
|
139
|
+
return sout
|
140
|
+
end
|
141
|
+
|
142
|
+
#return the connected set containing nodes (following the direction
|
143
|
+
#given)
|
144
|
+
def connected(*nodes, down:true, up:true)
|
145
|
+
r=Set.new()
|
146
|
+
nodes.each do |node|
|
147
|
+
unless r.include?(node)
|
148
|
+
new_nodes=Set.new()
|
149
|
+
new_nodes.merge(node.children) if down
|
150
|
+
new_nodes.merge(node.parents) if up
|
151
|
+
r.merge(connected(*new_nodes, down:down,up:up))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
return r
|
155
|
+
end
|
156
|
+
#return all parents
|
157
|
+
def ancestors(*nodes)
|
158
|
+
connected(*nodes, up:true, down:false)
|
159
|
+
end
|
160
|
+
#return all childern
|
161
|
+
def descendants(*nodes)
|
162
|
+
connected(*nodes, up:false, down:true)
|
163
|
+
end
|
164
|
+
|
165
|
+
#from a list of nodes, return all nodes that are not descendants of
|
166
|
+
#other nodes in the graph
|
167
|
+
def unneeded(*nodes)
|
168
|
+
tokeep.merge(@nodes-nodes)
|
169
|
+
nodes.each do |node|
|
170
|
+
unneeded << node unless ancestors(node).any? {|c| tokeep.include?(c)}
|
171
|
+
end
|
172
|
+
end
|
173
|
+
#return all dependencies that are not needed by any more nodes.
|
174
|
+
#If some dependencies should be kept (think manual install), add them
|
175
|
+
#to the unneeded parameter
|
176
|
+
def unneeded_descendants(*nodes, needed:[])
|
177
|
+
needed-=nodes #nodes to delete are in priority
|
178
|
+
deps=descendants(*nodes)
|
179
|
+
deps-=needed #but for children nodes, needed nodes are in priority
|
180
|
+
unneeded(*deps)
|
181
|
+
end
|
182
|
+
#So to implement the equivalent of pacman -Rc packages
|
183
|
+
#it suffices to add the ancestors of packages
|
184
|
+
#For pacman -Rs, this is exactly unneeded_descendants
|
185
|
+
#and pacman -Rcs would be ancestors(unneeded_descendants)
|
186
|
+
#finally to clean all unneeded packages (provided we have a list of
|
187
|
+
#packages 'tokeep' to keep), either use unneeded(@nodes-tokeep)
|
188
|
+
#or unneeded_descendants(roots, needed:tokeep)
|
189
|
+
|
190
|
+
#return the subgraph containing all the nodes passed as parameters,
|
191
|
+
#and the complementary graph. The union of both may not be the full
|
192
|
+
#graph [edges] in case the components are not connected
|
193
|
+
def subgraph(*nodes)
|
194
|
+
subgraph=Graph.new()
|
195
|
+
compgraph=Graph.new()
|
196
|
+
@nodes.each do |node|
|
197
|
+
if nodes.include?(node)
|
198
|
+
n=subgraph.build(node.name)
|
199
|
+
node.children.each do |c|
|
200
|
+
n.add_child(c) if nodes.include?(c)
|
201
|
+
end
|
202
|
+
else
|
203
|
+
n=compgraph.build(node.name)
|
204
|
+
node.children.each do |c|
|
205
|
+
n.add_child(c) unless nodes.include?(c)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
return subgraph, compgraph
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
end
|