tangle 0.8.2 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Gem Version](https://badge.fury.io/rb/tangle.svg)](https://badge.fury.io/rb/tangle)
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/tangle.svg)](https://badge.fury.io/rb/tangle)
|
2
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/0d92a4d05b6bb5c06dce/maintainability)](https://codeclimate.com/github/notCalle/ruby-tangle/maintainability)
|
3
|
+
[![codecov](https://codecov.io/gh/notCalle/ruby-tangle/branch/master/graph/badge.svg)](https://codecov.io/gh/notCalle/ruby-tangle)
|
4
|
+
[![Ruby](https://github.com/notCalle/ruby-tangle/actions/workflows/ruby.yml/badge.svg)](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]
|