mementus 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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