tangle 0.8.2 → 0.11.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/.github/workflows/ruby.yml +37 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +15 -4
- data/CHANGELOG.md +45 -0
- data/Gemfile +4 -10
- data/README.md +8 -3
- data/Rakefile +3 -1
- data/bin/console +5 -4
- data/bin/setup +0 -1
- data/lib/tangle.rb +7 -4
- data/lib/tangle/base_graph.rb +154 -0
- data/lib/tangle/{graph_vertices.rb → base_graph_private.rb} +12 -35
- data/lib/tangle/base_graph_protected.rb +50 -0
- data/lib/tangle/currify.rb +58 -0
- data/lib/tangle/directed/acyclic/graph.rb +3 -0
- data/lib/tangle/directed/acyclic/partial_order.rb +3 -0
- data/lib/tangle/directed/edge.rb +4 -2
- data/lib/tangle/directed/graph.rb +28 -6
- data/lib/tangle/edge.rb +5 -14
- data/lib/tangle/errors.rb +2 -0
- data/lib/tangle/mixin.rb +2 -1
- data/lib/tangle/mixin/directory.rb +48 -13
- data/lib/tangle/undirected/edge.rb +30 -0
- data/lib/tangle/undirected/graph.rb +73 -0
- data/lib/tangle/undirected/simple/graph.rb +22 -0
- data/lib/tangle/version.rb +8 -4
- data/ruby-tangle.sublime-project +8 -0
- data/tangle.gemspec +19 -7
- metadata +103 -27
- data/.travis.yml +0 -5
- data/lib/tangle/graph.rb +0 -81
- data/lib/tangle/graph_edges.rb +0 -49
- data/lib/tangle/mixin/connectedness.rb +0 -68
- data/lib/tangle/simple/graph.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be7e93897d5dcfbe1b739f977740f46d2bef0a050503244e1204f9bb2a387bac
|
4
|
+
data.tar.gz: ca037f66996798b77e0fdd2b2c734a880ce5f495caac85cd84a49748daa04547
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f94196947e837391f4c09b194f7d2d07e76ce7bc0215faad944eecf3c75109840f309e4b07ddd976b9466f382f7c8c0a0caffde4b68fa09c4d6fea32eb10669
|
7
|
+
data.tar.gz: 464b75f77486d466c0621bd272fdab22db7047ee58648e8af98799dfc0c1ff2da64534304c55c1bc41b7db67052801aa9ac41ec53d9b352e3d93cbae9499d958
|
@@ -0,0 +1,37 @@
|
|
1
|
+
%YAML 1.1
|
2
|
+
---
|
3
|
+
# This workflow uses actions that are not certified by GitHub.
|
4
|
+
# They are provided by a third-party and are governed by
|
5
|
+
# separate terms of service, privacy policy, and support
|
6
|
+
# documentation.
|
7
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
8
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
9
|
+
|
10
|
+
name: Ruby
|
11
|
+
|
12
|
+
on:
|
13
|
+
push:
|
14
|
+
pull_request:
|
15
|
+
branches: [ main ]
|
16
|
+
|
17
|
+
jobs:
|
18
|
+
test:
|
19
|
+
|
20
|
+
runs-on: ubuntu-latest
|
21
|
+
strategy:
|
22
|
+
matrix:
|
23
|
+
ruby-version: ['2.6', '2.7', '3.0']
|
24
|
+
|
25
|
+
steps:
|
26
|
+
- uses: actions/checkout@v2
|
27
|
+
- name: Set up Ruby
|
28
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
29
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
30
|
+
# uses: ruby/setup-ruby@v1
|
31
|
+
uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
32
|
+
with:
|
33
|
+
ruby-version: ${{ matrix.ruby-version }}
|
34
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
35
|
+
- name: Run tests
|
36
|
+
run: bundle exec rake
|
37
|
+
...
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
%YAML 1.1
|
2
|
+
---
|
3
|
+
AllCops:
|
4
|
+
TargetRubyVersion: 2.6
|
5
|
+
NewCops: enable
|
6
|
+
|
1
7
|
Metrics/BlockLength:
|
2
8
|
Exclude:
|
3
9
|
- 'spec/*_spec.rb'
|
@@ -6,7 +12,12 @@ Style/BlockDelimiters:
|
|
6
12
|
Exclude:
|
7
13
|
- 'spec/*_spec.rb'
|
8
14
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
15
|
+
Style/HashEachMethods:
|
16
|
+
Enabled: true
|
17
|
+
|
18
|
+
Style/HashTransformKeys:
|
19
|
+
Enabled: true
|
20
|
+
|
21
|
+
Style/HashTransformValues:
|
22
|
+
Enabled: true
|
23
|
+
...
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
|
9
|
+
|
10
|
+
## [0.11.0] - 2021-05-30
|
11
|
+
|
12
|
+
This release updates dependencies, changes the minimum Ruby version to 2.6, and adds support for Ruby version 3.
|
13
|
+
|
14
|
+
### Added
|
15
|
+
|
16
|
+
- Ruby version 3 is now supported, and mixin initializers should not rely on keyword splat semantics for a `Hash` argument.
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
|
20
|
+
- The minimum required Ruby version is now 2.6.
|
21
|
+
|
22
|
+
- Dependencies have been updated.
|
23
|
+
|
24
|
+
|
25
|
+
## [0.10.2] - 2019-07-05
|
26
|
+
|
27
|
+
### Changed
|
28
|
+
|
29
|
+
- `Currify::currify`now raises a `CurrifyError` < `ArgumentError` when the method takes no arguments, instead of the curried method failing when called
|
30
|
+
|
31
|
+
- Development dependency on `bundler` is updated to `~> 2.0`.
|
32
|
+
|
33
|
+
### Fixed
|
34
|
+
|
35
|
+
- `Graph#connected_subgraph` and `Graph#disconnected_subgraph` called an invalid method to determine connectedness of candidate vertices
|
36
|
+
|
37
|
+
|
38
|
+
## [Older]
|
39
|
+
These releases have no change logs.
|
40
|
+
|
41
|
+
|
42
|
+
[Unreleased]: https://github.com/notCalle/ruby-tangle/compare/v0.11.0..HEAD
|
43
|
+
[0.11.0]: https://github.com/notCalle/ruby-tangle/compare/v0.10.2..v0.11.0
|
44
|
+
[0.10.2]: https://github.com/notCalle/ruby-tangle/compare/v0.10.1..v0.10.2
|
45
|
+
[Older]: https://github.com/notCalle/ruby-tangle/releases/tag/v0.10.1
|
data/Gemfile
CHANGED
@@ -1,12 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
source 'https://rubygems.org'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
rescue LoadError
|
8
|
-
gem 'git-version-bump', '~> 0.15'
|
9
|
-
else
|
10
|
-
# Specify your gem's dependencies in tangle.gemspec
|
11
|
-
gemspec
|
12
|
-
end
|
5
|
+
# Specify your gem's dependencies in tangle.gemspec
|
6
|
+
gemspec
|
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
[](https://badge.fury.io/rb/tangle)
|
1
|
+
[](https://badge.fury.io/rb/tangle)
|
2
|
+
[](https://codeclimate.com/github/notCalle/ruby-tangle/maintainability)
|
3
|
+
[](https://codecov.io/gh/notCalle/ruby-tangle)
|
4
|
+
[](https://github.com/notCalle/ruby-tangle/actions/workflows/ruby.yml)
|
2
5
|
|
3
6
|
# Tangle
|
4
7
|
|
@@ -12,10 +15,12 @@ Tangle aims to untangle your graphs, by providing a set of classes for managing
|
|
12
15
|
* ~~Tree~~
|
13
16
|
|
14
17
|
**Feature mixins**:
|
15
|
-
*
|
18
|
+
* Filesystem directory graph
|
16
19
|
* ~~Coloring~~
|
17
20
|
* ~~GraphViz~~
|
18
21
|
|
22
|
+
See the [changelog](CHANGELOG.md) for recent changes.
|
23
|
+
|
19
24
|
## Installation
|
20
25
|
|
21
26
|
Add this line to your application's Gemfile:
|
@@ -81,7 +86,7 @@ Tangle::Graph.new(mixins: [WeightedEdges])
|
|
81
86
|
|
82
87
|
## Contributing
|
83
88
|
|
84
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/notCalle/tangle. Pull requests should be rebased to HEAD of `
|
89
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/notCalle/tangle. Pull requests should be rebased to HEAD of `main` before submitting, and all commits must be signed with valid GPG key. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
85
90
|
|
86
91
|
## License
|
87
92
|
|
data/Rakefile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler/setup'
|
2
4
|
require 'bundler/gem_tasks'
|
3
5
|
|
@@ -20,7 +22,7 @@ task :check_version do
|
|
20
22
|
raise 'Internal revision!' unless GVB.internal_revision.empty?
|
21
23
|
end
|
22
24
|
|
23
|
-
task test: %i[spec
|
25
|
+
task test: %i[rubocop spec]
|
24
26
|
task build: %i[spec]
|
25
27
|
task release: %i[rubocop check_version]
|
26
28
|
task default: %i[test]
|
data/bin/console
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'bundler/setup'
|
4
5
|
require 'tangle'
|
@@ -7,8 +8,8 @@ require 'tangle'
|
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
8
9
|
|
9
10
|
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
|
11
|
-
|
11
|
+
require 'pry'
|
12
|
+
Pry.start
|
12
13
|
|
13
|
-
require 'irb'
|
14
|
-
IRB.start(__FILE__)
|
14
|
+
# require 'irb'
|
15
|
+
# IRB.start(__FILE__)
|
data/bin/setup
CHANGED
data/lib/tangle.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'tangle/version'
|
2
4
|
require 'tangle/errors'
|
3
|
-
require 'tangle/graph'
|
4
|
-
require 'tangle/simple/graph'
|
5
|
+
require 'tangle/undirected/graph'
|
6
|
+
require 'tangle/undirected/simple/graph'
|
5
7
|
require 'tangle/directed/graph'
|
6
8
|
require 'tangle/directed/acyclic/graph'
|
7
9
|
|
@@ -20,8 +22,9 @@ require 'tangle/directed/acyclic/graph'
|
|
20
22
|
# => Directed graph with no edge cycles
|
21
23
|
#
|
22
24
|
module Tangle
|
23
|
-
|
24
|
-
|
25
|
+
Graph = Tangle::Undirected::Graph
|
26
|
+
MultiGraph = Tangle::Undirected::Graph
|
27
|
+
SimpleGraph = Tangle::Undirected::Simple::Graph
|
25
28
|
DiGraph = Tangle::Directed::Graph
|
26
29
|
DAG = Tangle::Directed::Acyclic::Graph
|
27
30
|
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'currify'
|
4
|
+
require_relative 'mixin'
|
5
|
+
require_relative 'base_graph_protected'
|
6
|
+
require_relative 'base_graph_private'
|
7
|
+
|
8
|
+
module Tangle
|
9
|
+
#
|
10
|
+
# Abstract base class for (un)directed graphs
|
11
|
+
#
|
12
|
+
class BaseGraph
|
13
|
+
include Tangle::Currify
|
14
|
+
include Tangle::Mixin::Initialize
|
15
|
+
include Tangle::BaseGraphProtected
|
16
|
+
include Tangle::BaseGraphPrivate
|
17
|
+
|
18
|
+
# Initialize a new graph, preloading it with vertices and edges
|
19
|
+
#
|
20
|
+
# Graph[+vertices+] => Graph
|
21
|
+
# Graph[+vertices+, +edges+) => Graph
|
22
|
+
#
|
23
|
+
# When +vertices+ is a hash, it contains initialization kwargs as
|
24
|
+
# values and vertex names as keys. When +vertices+ is an array of
|
25
|
+
# initialization kwargs, the vertices will be be anonymous.
|
26
|
+
#
|
27
|
+
# +edges+ can contain an array of exactly two, either names of vertices
|
28
|
+
# or vertices.
|
29
|
+
#
|
30
|
+
# Any kwarg supported by Graph.new is also allowed.
|
31
|
+
#
|
32
|
+
def self.[](vertices, edges = {}, **kwargs)
|
33
|
+
graph = new(**kwargs)
|
34
|
+
vertices.each { |vertex| graph.add_vertex(vertex) }
|
35
|
+
edges.each { |from, to| graph.add_edge(from, to) }
|
36
|
+
graph
|
37
|
+
end
|
38
|
+
|
39
|
+
# Initialize a new graph, optionally preloading it with vertices and edges
|
40
|
+
#
|
41
|
+
# Graph.new() => Graph
|
42
|
+
# Graph.new(mixins: [MixinModule, ...], ...) => Graph
|
43
|
+
#
|
44
|
+
# +mixins+ is an array of modules that can be mixed into the various
|
45
|
+
# classes that makes up a graph. Initialization of a Graph, Vertex or Edge
|
46
|
+
# looks for submodules in each mixin, with the same name and extends
|
47
|
+
# any created object. Defaults to [Tangle::Mixin::Connectedness].
|
48
|
+
#
|
49
|
+
# Any subclass of Graph should also subclass Edge to manage its unique
|
50
|
+
# constraints.
|
51
|
+
#
|
52
|
+
def initialize(currify: false, mixins: [], **kwargs)
|
53
|
+
@currify = currify
|
54
|
+
initialize_vertices
|
55
|
+
initialize_edges
|
56
|
+
initialize_mixins(mixins: mixins, **kwargs)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Return a subgraph, optionally filtered by a vertex selector block
|
60
|
+
#
|
61
|
+
# subgraph => Graph
|
62
|
+
# subgraph { |vertex| ... } => Graph
|
63
|
+
#
|
64
|
+
# Unless a selector is provided, the subgraph contains the entire graph.
|
65
|
+
#
|
66
|
+
def subgraph(included = nil, &selector)
|
67
|
+
result = clone
|
68
|
+
result.select_vertices!(included) unless included.nil?
|
69
|
+
result.select_vertices!(&selector) if block_given?
|
70
|
+
result
|
71
|
+
end
|
72
|
+
|
73
|
+
def clone
|
74
|
+
result = super
|
75
|
+
result.copy_vertices_and_edges(self)
|
76
|
+
result
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_s
|
80
|
+
"#<#{self.class}: #{vertices.count} vertices, #{edges.count} edges>"
|
81
|
+
end
|
82
|
+
alias inspect to_s
|
83
|
+
|
84
|
+
# Fetch a vertex by its name
|
85
|
+
def fetch(name)
|
86
|
+
@vertices_by_name.fetch(name)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Return a named vertex
|
90
|
+
def [](name)
|
91
|
+
@vertices_by_name[name]
|
92
|
+
end
|
93
|
+
|
94
|
+
# Return all vertices in the graph
|
95
|
+
def vertices
|
96
|
+
@vertices.keys
|
97
|
+
end
|
98
|
+
|
99
|
+
# Select vertices in the graph
|
100
|
+
def select(&selector)
|
101
|
+
@vertices.each_key.select(&selector)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Add a vertex into the graph
|
105
|
+
#
|
106
|
+
# If a name: is given, or the vertex responds to :name,
|
107
|
+
# it will be registered by name in the graph
|
108
|
+
def add_vertex(vertex, name: nil)
|
109
|
+
name ||= callback(vertex, :name)
|
110
|
+
insert_vertex(vertex, name)
|
111
|
+
define_currified_methods(vertex, :vertex) if @currify
|
112
|
+
callback(vertex, :added_to_graph, self)
|
113
|
+
self
|
114
|
+
end
|
115
|
+
alias << add_vertex
|
116
|
+
|
117
|
+
# Remove a vertex from the graph
|
118
|
+
def remove_vertex(vertex)
|
119
|
+
@vertices[vertex].each do |edge|
|
120
|
+
remove_edge(edge) if edge.include?(vertex)
|
121
|
+
end
|
122
|
+
delete_vertex(vertex)
|
123
|
+
callback(vertex, :removed_from_graph, self)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Get all edges.
|
127
|
+
#
|
128
|
+
# edges => Array
|
129
|
+
#
|
130
|
+
def edges(vertex = nil)
|
131
|
+
return @edges if vertex.nil?
|
132
|
+
|
133
|
+
@vertices.fetch(vertex)
|
134
|
+
end
|
135
|
+
currify :vertex, :edges
|
136
|
+
|
137
|
+
# Add a new edge to the graph
|
138
|
+
#
|
139
|
+
# add_edge(vtx1, vtx2, ...) => Edge
|
140
|
+
#
|
141
|
+
def add_edge(*vertices, **kvargs)
|
142
|
+
edge = new_edge(*vertices, mixins: @mixins, **kvargs)
|
143
|
+
insert_edge(edge)
|
144
|
+
vertices.each { |vertex| callback(vertex, :edge_added, edge) }
|
145
|
+
edge
|
146
|
+
end
|
147
|
+
|
148
|
+
# Remove an edge from the graph
|
149
|
+
def remove_edge(edge)
|
150
|
+
delete_edge(edge)
|
151
|
+
edge.each_vertex { |vertex| callback(vertex, :edge_removed, edge) }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -1,49 +1,26 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Tangle
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
# Return a named vertex
|
12
|
-
def [](name)
|
13
|
-
@vertices_by_name[name]
|
14
|
-
end
|
15
|
-
|
16
|
-
# Return all vertices in the graph
|
17
|
-
def vertices
|
18
|
-
@vertices.keys
|
19
|
-
end
|
20
|
-
|
21
|
-
# Add a vertex into the graph
|
22
|
-
#
|
23
|
-
# If a name: is given, it will be registered by name
|
24
|
-
def add_vertex(vertex, name: nil)
|
25
|
-
@vertices[vertex] = Set[]
|
26
|
-
@vertices_by_name[name] = vertex unless name.nil?
|
27
|
-
self
|
28
|
-
end
|
29
|
-
alias << add_vertex
|
4
|
+
#
|
5
|
+
# Private methods of BaseGraph
|
6
|
+
#
|
7
|
+
module BaseGraphPrivate
|
8
|
+
private
|
30
9
|
|
31
|
-
|
32
|
-
|
33
|
-
@vertices[vertex].each do |edge|
|
34
|
-
remove_edge(edge) if edge.include?(vertex)
|
35
|
-
end
|
36
|
-
@vertices.delete(vertex)
|
10
|
+
def callback(receiver, method, *args)
|
11
|
+
receiver.send(method, *args) if receiver.respond_to?(method)
|
37
12
|
end
|
38
13
|
|
39
|
-
private
|
40
|
-
|
41
14
|
# Initialize vertex related attributes
|
42
15
|
def initialize_vertices
|
43
16
|
@vertices = {}
|
44
17
|
@vertices_by_name = {}
|
45
18
|
end
|
46
19
|
|
20
|
+
def initialize_edges
|
21
|
+
@edges = Set[]
|
22
|
+
end
|
23
|
+
|
47
24
|
# Yield each reachable vertex to a block, breadth first
|
48
25
|
def each_vertex_breadth_first(start_vertex, walk_method)
|
49
26
|
remaining = [start_vertex]
|