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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 419399d50dad3b0243a7756f259e99f64bec6da5
4
- data.tar.gz: 12a25815a2bb6fd8beebdfd1d9aaf696f1ee5e03
3
+ metadata.gz: 1a13c8d4774fbdfad2b26d1180ae51cf1befb116
4
+ data.tar.gz: c58bafb8b1aeb71e7179bc64947809537e7c90e7
5
5
  SHA512:
6
- metadata.gz: 4a8c602c0b356d31062eac51d63fc4e93b1740abc417baed0824018d04f75308ac12fdfb47953689bce86bfa8600a62675036d7e15a7107a4e3d3ae287125b85
7
- data.tar.gz: b68d9dd85cf13935e41006c8814242243d1b3701672037a79e831433b011ac2b6a3861b1b13dc959c8a1a560349e01967d58ac8c727b0ded0cf3c2d1d5694e96
6
+ metadata.gz: a4973b6f437b39fbbd0d01dbc9a296ed26cb60e4d18554c9c78fc41d4cc6140edc6847e117afcd690b2088485e08e6c4d7c74c688f1162e19654c850568240f4
7
+ data.tar.gz: 43cb0c147dbaebb0964b5896432915aec14b16f34f64b25e6e55b678345adfcba016e4648a06c4c2a8fecb439b17b8964c22783d959fded2340599984fafc023
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 maetl
1
+ Copyright (c) 2014-2016 Editorial Technology
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,3 +1,19 @@
1
1
  # Mementus
2
2
 
3
3
  [![Build Status](https://travis-ci.org/maetl/mementus.svg?branch=master)](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
@@ -21,7 +21,7 @@ module Mementus
21
21
  end
22
22
 
23
23
  def to_edge
24
- Edge.new(id: id, from: from, to: to, label: label)
24
+ Edge.new(id: id, from: from, to: to, label: label, props: props)
25
25
  end
26
26
  end
27
27
  end
@@ -1,5 +1,17 @@
1
1
  module Mementus
2
2
  module ElementBuilder
3
- attr_accessor :id, :label, :props
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
- @structure.set_node(Node.new(id: id, label: label))
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
- @structure.set_edge(Edge.new(id: id, from: from, to: to, label: label))
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
@@ -3,7 +3,7 @@ module Mementus
3
3
  include ElementBuilder
4
4
 
5
5
  def to_node
6
- Node.new(id: id, label: label)
6
+ Node.new(id: id, label: label, props: props)
7
7
  end
8
8
  end
9
9
  end
@@ -13,8 +13,8 @@ module Mementus
13
13
  @node.label
14
14
  end
15
15
 
16
- def outgoing
17
-
16
+ def [](prop)
17
+ @node[prop]
18
18
  end
19
19
 
20
20
  def each_adjacent(&block)
@@ -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,13 @@
1
+ module Mementus
2
+ module Pipes
3
+ class Node
4
+ def initialize(id)
5
+ @id = id
6
+ end
7
+
8
+ def process(graph, id)
9
+ graph.node(id || @id)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module Mementus
2
+ module Pipes
3
+ class Outgoing
4
+ def process(graph, source)
5
+ if source.respond_to?(:adjacent)
6
+ source.adjacent
7
+ else
8
+ source.inject([]) do |result, node|
9
+ result.concat(node.adjacent)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Mementus
2
+ module Pipes
3
+ class OutgoingEdges
4
+ def process(graph, node)
5
+ graph.each_adjacent(node.id).map do |id|
6
+ Mementus::Edge.new(from: node, to: id)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ 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
@@ -1,3 +1,3 @@
1
1
  module Mementus
2
- VERSION = '0.5.0'.freeze
2
+ VERSION = '0.5.1'.freeze
3
3
  end
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/pipeline'
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
- expect(graph.node(20).id).to eq(20)
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 = "A"
71
- edge.to = "B"
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
- expect(graph.edge(123).id).to eq(123)
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 'should initialize with id' do
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 'should initialize with id and label' do
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.0
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-20 00:00:00.000000000 Z
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/pipeline.rb
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
@@ -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