mementus 0.5.0 → 0.5.1
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/LICENSE.txt +1 -1
- data/README.md +16 -0
- data/lib/mementus/edge.rb +6 -1
- data/lib/mementus/edge_builder.rb +1 -1
- data/lib/mementus/element_builder.rb +13 -1
- data/lib/mementus/graph_builder.rb +6 -4
- data/lib/mementus/node.rb +6 -1
- data/lib/mementus/node_builder.rb +1 -1
- data/lib/mementus/node_proxy.rb +2 -2
- data/lib/mementus/pipes/incoming.rb +17 -0
- data/lib/mementus/pipes/incoming_edges.rb +18 -0
- data/lib/mementus/pipes/node.rb +13 -0
- data/lib/mementus/pipes/outgoing.rb +15 -0
- data/lib/mementus/pipes/outgoing_edges.rb +11 -0
- data/lib/mementus/processor.rb +58 -0
- data/lib/mementus/version.rb +1 -1
- data/lib/mementus.rb +6 -1
- data/spec/edge_spec.rb +18 -0
- data/spec/graph_spec.rb +62 -4
- data/spec/node_spec.rb +26 -2
- metadata +8 -3
- data/lib/mementus/pipeline.rb +0 -119
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a13c8d4774fbdfad2b26d1180ae51cf1befb116
|
4
|
+
data.tar.gz: c58bafb8b1aeb71e7179bc64947809537e7c90e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4973b6f437b39fbbd0d01dbc9a296ed26cb60e4d18554c9c78fc41d4cc6140edc6847e117afcd690b2088485e08e6c4d7c74c688f1162e19654c850568240f4
|
7
|
+
data.tar.gz: 43cb0c147dbaebb0964b5896432915aec14b16f34f64b25e6e55b678345adfcba016e4648a06c4c2a8fecb439b17b8964c22783d959fded2340599984fafc023
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
1
|
# Mementus
|
2
2
|
|
3
3
|
[](https://travis-ci.org/maetl/mementus)
|
4
|
+
|
5
|
+
## Roadmap
|
6
|
+
|
7
|
+
| Version | Summary |
|
8
|
+
|---------|----------------------------------------------------------|
|
9
|
+
| `0.6` | Support fiber based lazily evaluated pipeline traversals |
|
10
|
+
| `0.7` | Implement basic filter, side effect and transform pipes |
|
11
|
+
| `0.8` | API support for Dijkstra/A* traversals using edge props |
|
12
|
+
| `0.9` | Pluggable graph structure representations |
|
13
|
+
| `0.10` | API support for tsort and strongly connected components |
|
14
|
+
| `0.11` | Expand graph library with interesting sample data |
|
15
|
+
| `0.12` | Import/export via JSON and XML formats |
|
16
|
+
|
17
|
+
## License
|
18
|
+
|
19
|
+
Provided under the terms of the MIT License. See the `LICENSE` file in the root of this project for more information.
|
data/lib/mementus/edge.rb
CHANGED
@@ -2,9 +2,10 @@ module Mementus
|
|
2
2
|
class Edge
|
3
3
|
attr_reader :from, :to, :label, :id
|
4
4
|
|
5
|
-
def initialize(id: nil, from:, to:, label: :edge)
|
5
|
+
def initialize(id: nil, from:, to:, label: :edge, props: {})
|
6
6
|
@id = id
|
7
7
|
@label = label
|
8
|
+
@props = props.freeze
|
8
9
|
|
9
10
|
@from = if from.is_a?(Integer)
|
10
11
|
Node.new(id: from)
|
@@ -19,6 +20,10 @@ module Mementus
|
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
23
|
+
def [](prop)
|
24
|
+
@props[prop]
|
25
|
+
end
|
26
|
+
|
22
27
|
def nodes
|
23
28
|
[@from, @to]
|
24
29
|
end
|
@@ -1,5 +1,17 @@
|
|
1
1
|
module Mementus
|
2
2
|
module ElementBuilder
|
3
|
-
attr_accessor :id, :label
|
3
|
+
attr_accessor :id, :label
|
4
|
+
|
5
|
+
def props
|
6
|
+
@props ||= {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def props=(props_map)
|
10
|
+
@props = props_map
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(prop_key, value)
|
14
|
+
props[prop_key] = value
|
15
|
+
end
|
4
16
|
end
|
5
17
|
end
|
@@ -14,12 +14,14 @@ module Mementus
|
|
14
14
|
@structure.set_edge(edge)
|
15
15
|
end
|
16
16
|
|
17
|
-
def add_node(id: nil, label: nil)
|
18
|
-
@
|
17
|
+
def add_node(id: nil, label: nil, props: {})
|
18
|
+
id = @node_ids.next_id unless id
|
19
|
+
@structure.set_node(Node.new(id: id, label: label, props: props))
|
19
20
|
end
|
20
21
|
|
21
|
-
def add_edge(id: nil, from: nil, to: nil, label: nil)
|
22
|
-
|
22
|
+
def add_edge(id: nil, from: nil, to: nil, label: nil, props: {})
|
23
|
+
id = @edge_ids.next_id unless id
|
24
|
+
@structure.set_edge(Edge.new(id: id, from: from, to: to, label: label, props: props))
|
23
25
|
end
|
24
26
|
|
25
27
|
def create_node(&block)
|
data/lib/mementus/node.rb
CHANGED
@@ -2,9 +2,14 @@ module Mementus
|
|
2
2
|
class Node
|
3
3
|
attr_reader :id, :label
|
4
4
|
|
5
|
-
def initialize(id: nil, label: nil)
|
5
|
+
def initialize(id: nil, label: nil, props: {})
|
6
6
|
@id = id
|
7
7
|
@label = label
|
8
|
+
@props = props.freeze
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](prop)
|
12
|
+
@props[prop]
|
8
13
|
end
|
9
14
|
end
|
10
15
|
end
|
data/lib/mementus/node_proxy.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Mementus
|
2
|
+
module Pipes
|
3
|
+
class Incoming
|
4
|
+
def process(graph, node)
|
5
|
+
incoming = []
|
6
|
+
|
7
|
+
graph.each_node do |graph_node|
|
8
|
+
graph.each_adjacent(graph_node.id) do |adj_node|
|
9
|
+
incoming << graph_node if adj_node.id == node.id
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
incoming
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Mementus
|
2
|
+
module Pipes
|
3
|
+
class IncomingEdges
|
4
|
+
def process(graph, source)
|
5
|
+
ids = source.respond_to?(:id) ? [source.id] : source.map(&:id)
|
6
|
+
incoming = []
|
7
|
+
|
8
|
+
graph.each_node do |graph_node|
|
9
|
+
graph.each_adjacent(graph_node.id) do |adj_node|
|
10
|
+
incoming << Mementus::Edge.new(from: graph_node, to: adj_node) if ids.include?(adj_node.id)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
incoming
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Mementus
|
2
|
+
class Processor
|
3
|
+
def initialize(graph, start_pipe)
|
4
|
+
@graph = graph
|
5
|
+
@pipeline = [start_pipe]
|
6
|
+
end
|
7
|
+
|
8
|
+
def append_next(pipe)
|
9
|
+
@pipeline << pipe
|
10
|
+
end
|
11
|
+
|
12
|
+
def process
|
13
|
+
output = nil
|
14
|
+
@pipeline.each do |pipe|
|
15
|
+
output = pipe.process(@graph, output)
|
16
|
+
end
|
17
|
+
output
|
18
|
+
end
|
19
|
+
|
20
|
+
def id
|
21
|
+
process.id
|
22
|
+
end
|
23
|
+
|
24
|
+
def one
|
25
|
+
output = process
|
26
|
+
|
27
|
+
if output.respond_to?(:each)
|
28
|
+
output.first
|
29
|
+
else
|
30
|
+
output
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def all
|
35
|
+
process.to_a
|
36
|
+
end
|
37
|
+
|
38
|
+
def out
|
39
|
+
append_next(Pipes::Outgoing.new)
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def out_e
|
44
|
+
append_next(Pipes::OutgoingEdges.new)
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def in
|
49
|
+
append_next(Pipes::Incoming.new)
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def in_e
|
54
|
+
append_next(Pipes::IncomingEdges.new)
|
55
|
+
self
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/mementus/version.rb
CHANGED
data/lib/mementus.rb
CHANGED
@@ -4,7 +4,12 @@ require 'mementus/structure/incidence_list'
|
|
4
4
|
require 'mementus/node'
|
5
5
|
require 'mementus/edge'
|
6
6
|
require 'mementus/node_proxy'
|
7
|
-
require 'mementus/
|
7
|
+
require 'mementus/processor'
|
8
|
+
require 'mementus/pipes/incoming_edges'
|
9
|
+
require 'mementus/pipes/incoming'
|
10
|
+
require 'mementus/pipes/node'
|
11
|
+
require 'mementus/pipes/outgoing_edges'
|
12
|
+
require 'mementus/pipes/outgoing'
|
8
13
|
require 'mementus/depth_first_search'
|
9
14
|
require 'mementus/breadth_first_search'
|
10
15
|
require 'mementus/query/traversal'
|
data/spec/edge_spec.rb
CHANGED
@@ -25,6 +25,24 @@ describe Mementus::Edge do
|
|
25
25
|
expect(edge.label).to eq(:relationship)
|
26
26
|
end
|
27
27
|
|
28
|
+
it 'returns nil when missing prop is accessed' do
|
29
|
+
edge = Mementus::Edge.new(from: 1, to: 2)
|
30
|
+
|
31
|
+
expect(edge[:title]).to be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'initializes with props given' do
|
35
|
+
edge = Mementus::Edge.new(from: 1, to: 2, props: { :title => 'Relationship' })
|
36
|
+
|
37
|
+
expect(edge[:title]).to eq('Relationship')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'does not allow mutation of props' do
|
41
|
+
node = Mementus::Edge.new(from: 1, to: 2, props: { title: 'Relationship' })
|
42
|
+
|
43
|
+
expect { node[:title] = 'Edge' }.to raise_error(NoMethodError)
|
44
|
+
end
|
45
|
+
|
28
46
|
it 'should test equality based on value' do
|
29
47
|
edge1 = Mementus::Edge.new(from: 1, to: 2, label: :relationship)
|
30
48
|
edge2 = Mementus::Edge.new(from: 1, to: 2, label: :relationship)
|
data/spec/graph_spec.rb
CHANGED
@@ -27,17 +27,39 @@ describe Mementus::Graph do
|
|
27
27
|
expect(graph.node(1).id).to eq(1)
|
28
28
|
end
|
29
29
|
|
30
|
+
specify '#add_node -> with auto id' do
|
31
|
+
graph = Mementus::Graph.new do
|
32
|
+
add_node
|
33
|
+
end
|
34
|
+
|
35
|
+
expect(graph.node(1).id).to eq(1)
|
36
|
+
end
|
37
|
+
|
38
|
+
specify '#add_node -> with props' do
|
39
|
+
graph = Mementus::Graph.new do
|
40
|
+
add_node(props: { title: 'Vertex' })
|
41
|
+
end
|
42
|
+
|
43
|
+
expect(graph.node(1)[:title]).to eq('Vertex')
|
44
|
+
end
|
45
|
+
|
30
46
|
specify '#create_node' do
|
31
47
|
graph = Mementus::Graph.new do
|
32
48
|
create_node do |node|
|
33
49
|
node.id = 20
|
34
50
|
node.label = :vertex
|
51
|
+
node.props[:title] = 'Vertex'
|
35
52
|
end
|
36
53
|
end
|
37
54
|
|
38
55
|
expect(graph.nodes_count).to eq(1)
|
39
56
|
expect(graph.edges_count).to eq(0)
|
40
|
-
|
57
|
+
|
58
|
+
graph.node(20).tap do |node|
|
59
|
+
expect(node.id).to eq(20)
|
60
|
+
expect(node.label).to eq(:vertex)
|
61
|
+
expect(node[:title]).to eq('Vertex')
|
62
|
+
end
|
41
63
|
end
|
42
64
|
|
43
65
|
specify '#set_edge' do
|
@@ -60,6 +82,23 @@ describe Mementus::Graph do
|
|
60
82
|
expect(graph.edges_count).to eq(1)
|
61
83
|
expect(graph.node(1).id).to eq(1)
|
62
84
|
expect(graph.node(2).id).to eq(2)
|
85
|
+
expect(graph.edge(3).id).to eq(3)
|
86
|
+
end
|
87
|
+
|
88
|
+
specify '#add_edge -> with auto id' do
|
89
|
+
graph = Mementus::Graph.new do
|
90
|
+
add_edge(from: 1, to: 2)
|
91
|
+
end
|
92
|
+
|
93
|
+
expect(graph.edge(1).id).to eq(1)
|
94
|
+
end
|
95
|
+
|
96
|
+
specify '#add_edge -> with props' do
|
97
|
+
graph = Mementus::Graph.new do
|
98
|
+
add_edge(from: 1, to: 2, props: { title: 'Relationship' })
|
99
|
+
end
|
100
|
+
|
101
|
+
expect(graph.edge(1)[:title]).to eq('Relationship')
|
63
102
|
end
|
64
103
|
|
65
104
|
specify '#create_edge' do
|
@@ -67,14 +106,33 @@ describe Mementus::Graph do
|
|
67
106
|
create_edge do |edge|
|
68
107
|
edge.id = 123
|
69
108
|
edge.label = :relationship
|
70
|
-
edge.from =
|
71
|
-
edge.to =
|
109
|
+
edge.from = 'A'
|
110
|
+
edge.to = 'B'
|
111
|
+
edge.props[:name] = 'Relationship'
|
72
112
|
end
|
73
113
|
end
|
74
114
|
|
75
115
|
expect(graph.nodes_count).to eq(2)
|
76
116
|
expect(graph.edges_count).to eq(1)
|
77
|
-
|
117
|
+
graph.edge(123).tap do |edge|
|
118
|
+
expect(edge.id).to eq(123)
|
119
|
+
expect(edge.label).to eq(:relationship)
|
120
|
+
expect(edge.from.id).to eq('A')
|
121
|
+
expect(edge.to.id).to eq('B')
|
122
|
+
expect(edge[:name]).to eq('Relationship')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
specify '#create_edge -> with props' do
|
127
|
+
graph = Mementus::Graph.new do
|
128
|
+
create_edge do |edge|
|
129
|
+
edge.from = 'A'
|
130
|
+
edge.to = 'B'
|
131
|
+
edge.props = {
|
132
|
+
name: 'Relationship'
|
133
|
+
}
|
134
|
+
end
|
135
|
+
end
|
78
136
|
end
|
79
137
|
|
80
138
|
specify '#has_node?' do
|
data/spec/node_spec.rb
CHANGED
@@ -1,16 +1,40 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Mementus::Node do
|
4
|
-
it '
|
4
|
+
it 'initializes with id' do
|
5
5
|
node = Mementus::Node.new(id: 22)
|
6
6
|
|
7
7
|
expect(node.id).to eq(22)
|
8
8
|
end
|
9
9
|
|
10
|
-
it '
|
10
|
+
it 'initializes with label' do
|
11
|
+
node = Mementus::Node.new(label: :vertex)
|
12
|
+
|
13
|
+
expect(node.label).to eq(:vertex)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'initializes with id and label' do
|
11
17
|
node = Mementus::Node.new(id: 22, label: :vertex)
|
12
18
|
|
13
19
|
expect(node.id).to eq(22)
|
14
20
|
expect(node.label).to eq(:vertex)
|
15
21
|
end
|
22
|
+
|
23
|
+
it 'returns nil when missing prop is accessed' do
|
24
|
+
node = Mementus::Node.new
|
25
|
+
|
26
|
+
expect(node[:title]).to be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'initializes with props hash' do
|
30
|
+
node = Mementus::Node.new(props: { title: 'Vertex' })
|
31
|
+
|
32
|
+
expect(node[:title]).to eq('Vertex')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'does not allow mutation of props' do
|
36
|
+
node = Mementus::Node.new(props: { title: 'Vertex' })
|
37
|
+
|
38
|
+
expect { node[:title] = 'Node' }.to raise_error(NoMethodError)
|
39
|
+
end
|
16
40
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mementus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- maetl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-06-
|
11
|
+
date: 2016-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -79,7 +79,12 @@ files:
|
|
79
79
|
- lib/mementus/node.rb
|
80
80
|
- lib/mementus/node_builder.rb
|
81
81
|
- lib/mementus/node_proxy.rb
|
82
|
-
- lib/mementus/
|
82
|
+
- lib/mementus/pipes/incoming.rb
|
83
|
+
- lib/mementus/pipes/incoming_edges.rb
|
84
|
+
- lib/mementus/pipes/node.rb
|
85
|
+
- lib/mementus/pipes/outgoing.rb
|
86
|
+
- lib/mementus/pipes/outgoing_edges.rb
|
87
|
+
- lib/mementus/processor.rb
|
83
88
|
- lib/mementus/query/source.rb
|
84
89
|
- lib/mementus/query/step.rb
|
85
90
|
- lib/mementus/query/traversal.rb
|
data/lib/mementus/pipeline.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
module Mementus
|
2
|
-
class Processor
|
3
|
-
def initialize(graph, start_pipe)
|
4
|
-
@graph = graph
|
5
|
-
@pipeline = [start_pipe]
|
6
|
-
end
|
7
|
-
|
8
|
-
def append_next(pipe)
|
9
|
-
@pipeline << pipe
|
10
|
-
end
|
11
|
-
|
12
|
-
def process
|
13
|
-
output = nil
|
14
|
-
@pipeline.each do |pipe|
|
15
|
-
output = pipe.process(@graph, output)
|
16
|
-
end
|
17
|
-
output
|
18
|
-
end
|
19
|
-
|
20
|
-
def id
|
21
|
-
process.id
|
22
|
-
end
|
23
|
-
|
24
|
-
def one
|
25
|
-
output = process
|
26
|
-
|
27
|
-
if output.respond_to?(:each)
|
28
|
-
output.first
|
29
|
-
else
|
30
|
-
output
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def all
|
35
|
-
process.to_a
|
36
|
-
end
|
37
|
-
|
38
|
-
def out
|
39
|
-
append_next(Pipes::Outgoing.new)
|
40
|
-
self
|
41
|
-
end
|
42
|
-
|
43
|
-
def out_e
|
44
|
-
append_next(Pipes::OutgoingEdges.new)
|
45
|
-
self
|
46
|
-
end
|
47
|
-
|
48
|
-
def in
|
49
|
-
append_next(Pipes::Incoming.new)
|
50
|
-
self
|
51
|
-
end
|
52
|
-
|
53
|
-
def in_e
|
54
|
-
append_next(Pipes::IncomingEdges.new)
|
55
|
-
self
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
module Pipes
|
60
|
-
class Node
|
61
|
-
def initialize(id)
|
62
|
-
@id = id
|
63
|
-
end
|
64
|
-
|
65
|
-
def process(graph, id)
|
66
|
-
graph.node(id || @id)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
class Outgoing
|
71
|
-
def process(graph, source)
|
72
|
-
if source.respond_to?(:adjacent)
|
73
|
-
source.adjacent
|
74
|
-
else
|
75
|
-
source.inject([]) do |result, node|
|
76
|
-
result.concat(node.adjacent)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
class OutgoingEdges
|
83
|
-
def process(graph, node)
|
84
|
-
graph.each_adjacent(node.id).map do |id|
|
85
|
-
Mementus::Edge.new(from: node, to: id)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
class Incoming
|
91
|
-
def process(graph, node)
|
92
|
-
incoming = []
|
93
|
-
|
94
|
-
graph.each_node do |graph_node|
|
95
|
-
graph.each_adjacent(graph_node.id) do |adj_node|
|
96
|
-
incoming << graph_node if adj_node.id == node.id
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
incoming
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
class IncomingEdges
|
105
|
-
def process(graph, source)
|
106
|
-
ids = source.respond_to?(:id) ? [source.id] : source.map(&:id)
|
107
|
-
incoming = []
|
108
|
-
|
109
|
-
graph.each_node do |graph_node|
|
110
|
-
graph.each_adjacent(graph_node.id) do |adj_node|
|
111
|
-
incoming << Mementus::Edge.new(from: graph_node, to: adj_node) if ids.include?(adj_node.id)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
incoming
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|