geoff 0.0.2.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +110 -0
- data/LICENSE +22 -0
- data/README.md +141 -0
- data/Rakefile +2 -0
- data/geoff.gemspec +30 -0
- data/lib/geoff/children_dsl.rb +110 -0
- data/lib/geoff/container.rb +21 -0
- data/lib/geoff/importer.rb +155 -0
- data/lib/geoff/neo4j_wrapper_dsl.rb +46 -0
- data/lib/geoff/neo4j_wrapper_validator.rb +17 -0
- data/lib/geoff/node_dsl.rb +107 -0
- data/lib/geoff/version.rb +3 -0
- data/lib/geoff.rb +12 -0
- data/spec/integration/children_dsl_spec.rb +62 -0
- data/spec/integration/node_dsl_spec.rb +147 -0
- data/spec/models/geoff_spec.rb +72 -0
- data/spec/models/neo4j_wrapper_dsl_spec.rb +86 -0
- data/spec/models/neo4j_wrapper_validator_spec.rb +28 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/geof_matchers.rb +51 -0
- metadata +180 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'geoff/children_dsl'
|
2
|
+
|
3
|
+
class NodeDsl
|
4
|
+
attr_reader :node_name
|
5
|
+
|
6
|
+
def initialize(options, &block)
|
7
|
+
@node_name = options[:node_name] || 'root'
|
8
|
+
@klass_name = options[:klass_name] || 'ROOT'
|
9
|
+
@container = options[:container] || Container.new
|
10
|
+
@rel_type = options[:rel_type]
|
11
|
+
@outer_binding = options[:binding]
|
12
|
+
@properties = {}
|
13
|
+
@children_dsls = []
|
14
|
+
|
15
|
+
@rendered = false
|
16
|
+
|
17
|
+
write_mode { instance_eval(&block) } if block_given?
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_geoff
|
21
|
+
geoff_lines.join "\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
def geoff_lines
|
25
|
+
#we need this to prevent rendering same node which is rendered already
|
26
|
+
return [] if @rendered
|
27
|
+
@rendered = true
|
28
|
+
|
29
|
+
lines = [self_to_geoff]
|
30
|
+
lines << all_rule_to_geoff if use_neo4j_wrapper?
|
31
|
+
|
32
|
+
lines += @children_dsls.map(&:geoff_lines)
|
33
|
+
|
34
|
+
lines
|
35
|
+
end
|
36
|
+
|
37
|
+
def b
|
38
|
+
@container
|
39
|
+
end
|
40
|
+
|
41
|
+
def all_rule_to_geoff
|
42
|
+
"(#{@klass_name})-[:all]->(#{@node_name})"
|
43
|
+
end
|
44
|
+
|
45
|
+
def self_to_geoff
|
46
|
+
"(#{@node_name}) #{properties.to_json}"
|
47
|
+
end
|
48
|
+
|
49
|
+
#refactor out wrapper specific stuff later
|
50
|
+
def use_neo4j_wrapper?
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
def properties
|
55
|
+
if use_neo4j_wrapper?
|
56
|
+
{ '_classname' => @klass_name }.merge @properties
|
57
|
+
else
|
58
|
+
@properties
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#prevent very confusing method_missing calls during debugging by
|
63
|
+
#only writing to the object when expected
|
64
|
+
def write_mode
|
65
|
+
@write_mode = true
|
66
|
+
yield
|
67
|
+
@write_mode = false
|
68
|
+
end
|
69
|
+
|
70
|
+
# e.g.
|
71
|
+
#sandwich "BLT", type: :on_menu do
|
72
|
+
# filling 'bacon'
|
73
|
+
# filling 'lettuce'
|
74
|
+
# filling 'tomato'
|
75
|
+
#end
|
76
|
+
def method_missing m, *args, &blk
|
77
|
+
|
78
|
+
if args.empty?
|
79
|
+
@properties[m]
|
80
|
+
else
|
81
|
+
return super unless @write_mode
|
82
|
+
val = if args.first.is_a? Proc
|
83
|
+
eval("@#{m}", @outer_binding)
|
84
|
+
else
|
85
|
+
args.first
|
86
|
+
end
|
87
|
+
@properties[m] = val
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_s
|
92
|
+
to_geoff
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def children type=nil, &block
|
98
|
+
options = {
|
99
|
+
parent_node_name: @node_name,
|
100
|
+
type: type,
|
101
|
+
container: @container,
|
102
|
+
binding: @outer_binding
|
103
|
+
}
|
104
|
+
|
105
|
+
@children_dsls << ChildrenDsl.new(options, &block)
|
106
|
+
end
|
107
|
+
end
|
data/lib/geoff.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "json"
|
2
|
+
require "geoff/version"
|
3
|
+
require 'geoff/node_dsl'
|
4
|
+
require 'geoff/neo4j_wrapper_dsl'
|
5
|
+
require 'geoff/importer'
|
6
|
+
|
7
|
+
def Geoff *class_list, options, &block
|
8
|
+
Neo4jWrapperDsl.new(*class_list, options, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
class Geoff::Error < StandardError; end
|
12
|
+
class Geoff::MissingNodeDefinition < Geoff::Error ; end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'geoff/children_dsl'
|
3
|
+
|
4
|
+
describe ChildrenDsl do
|
5
|
+
describe 'with no special nodes' do
|
6
|
+
let(:children) do
|
7
|
+
ChildrenDsl.new({parent_node_name: 'starbucks'}) do
|
8
|
+
branch 'starbucks_branch_1', type: 'owns' do
|
9
|
+
delay_time 15
|
10
|
+
end
|
11
|
+
|
12
|
+
branch 'starbucks_branch_2', type: 'owns' do
|
13
|
+
delay_time 30
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:expected_geoff) do
|
19
|
+
strip_whitespace <<-EOS
|
20
|
+
(starbucks_branch_1) {"_classname":"Branch","delay_time":15}
|
21
|
+
(Branch)-[:all]->(starbucks_branch_1)
|
22
|
+
(starbucks)-[:owns]->(starbucks_branch_1)
|
23
|
+
(starbucks_branch_2) {"_classname":"Branch","delay_time":30}
|
24
|
+
(Branch)-[:all]->(starbucks_branch_2)
|
25
|
+
(starbucks)-[:owns]->(starbucks_branch_2)
|
26
|
+
EOS
|
27
|
+
end
|
28
|
+
|
29
|
+
specify do
|
30
|
+
geoff = children.to_geoff
|
31
|
+
|
32
|
+
geoff.should == expected_geoff
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "with missing rel type" do
|
37
|
+
let(:missing){ChildrenDsl.new(parent_node_name: 'starbucks') {area 'Luton' } }
|
38
|
+
let(:top) {ChildrenDsl.new(parent_node_name: 'starbucks', type: :x ){area 'Luton' } }
|
39
|
+
let(:on_node){ChildrenDsl.new(parent_node_name: 'starbucks') {area 'Luton', type: :x }}
|
40
|
+
|
41
|
+
specify { ->{missing }.should raise_error ArgumentError}
|
42
|
+
specify { ->{ top }.should_not raise_error ArgumentError }
|
43
|
+
specify { ->{ on_node}.should_not raise_error ArgumentError }
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'with special node' do
|
47
|
+
let(:children) do
|
48
|
+
ChildrenDsl.new({parent_node_name: 'starbucks'}) do
|
49
|
+
b.luton = area 'Luton', type: 'headquarters_location' do
|
50
|
+
name 'LU1'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
specify do
|
56
|
+
luton = children.b.luton
|
57
|
+
|
58
|
+
luton.node_name.should == 'Luton'
|
59
|
+
luton.name.should == 'LU1'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'geoff/node_dsl'
|
3
|
+
require 'geoff'
|
4
|
+
|
5
|
+
describe NodeDsl do
|
6
|
+
describe 'with properties and no children' do
|
7
|
+
let(:node) do
|
8
|
+
NodeDsl.new(node_name: 'starbucks_branch', klass_name: 'Branch') do
|
9
|
+
delay_time 15
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:expected_geoff) do
|
14
|
+
strip_whitespace <<-EOS
|
15
|
+
(starbucks_branch) {"_classname":"Branch","delay_time":15}
|
16
|
+
(Branch)-[:all]->(starbucks_branch)
|
17
|
+
EOS
|
18
|
+
end
|
19
|
+
|
20
|
+
specify do
|
21
|
+
node.to_geoff.should == expected_geoff
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'with children' do
|
26
|
+
let(:node) do
|
27
|
+
|
28
|
+
NodeDsl.new(node_name: 'starbucks', klass_name: 'Cafe') do
|
29
|
+
children do
|
30
|
+
branch 'starbucks_branch_1', type: 'owns', some: 'property' do
|
31
|
+
closing_buffer 30
|
32
|
+
delay_time 15
|
33
|
+
postcode 'LU1 1CN'
|
34
|
+
schedule 'DEFAULT_SCHEDULE'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:expected_geoff) do
|
42
|
+
strip_whitespace <<-EOS
|
43
|
+
(starbucks) {"_classname":"Cafe"}
|
44
|
+
(Cafe)-[:all]->(starbucks)
|
45
|
+
(starbucks_branch_1) {"_classname":"Branch","closing_buffer":30,"delay_time":15,"postcode":"LU1 1CN","schedule":"DEFAULT_SCHEDULE"}
|
46
|
+
(Branch)-[:all]->(starbucks_branch_1)
|
47
|
+
(starbucks)-[:owns]->(starbucks_branch_1) {"some":"property"}
|
48
|
+
EOS
|
49
|
+
end
|
50
|
+
|
51
|
+
specify do
|
52
|
+
node.to_geoff.should == expected_geoff
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#with a lambda to access the outer scope" do
|
57
|
+
let(:expected_geoff) do
|
58
|
+
strip_whitespace <<-EOS
|
59
|
+
(starbucks) {"_classname":"Cafe"}
|
60
|
+
(Cafe)-[:all]->(starbucks)
|
61
|
+
(starbucks_branch_1) {"_classname":"Branch","delay_time":15,"hours":"3"}
|
62
|
+
(Branch)-[:all]->(starbucks_branch_1)
|
63
|
+
(starbucks)-[:owns]->(starbucks_branch_1) {"some":"property"}
|
64
|
+
EOS
|
65
|
+
end
|
66
|
+
|
67
|
+
specify do
|
68
|
+
@hours = "3"
|
69
|
+
|
70
|
+
node = NodeDsl.new(node_name: 'starbucks', klass_name: 'Cafe', binding: binding) do
|
71
|
+
children do
|
72
|
+
branch 'starbucks_branch_1', type: 'owns', some: 'property' do
|
73
|
+
delay_time 15
|
74
|
+
hours ->{@hours}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
node.to_s.should == expected_geoff
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'with non-tree graphs' do
|
84
|
+
let(:node) do
|
85
|
+
|
86
|
+
NodeDsl.new(node_name: 'starbucks', klass_name: 'Cafe') do
|
87
|
+
children do
|
88
|
+
b.luton = area 'luton', type: 'headquarters_location' do
|
89
|
+
name 'LU1'
|
90
|
+
end
|
91
|
+
|
92
|
+
branch 'starbucks_branch_1', type: 'owns', some: 'property' do
|
93
|
+
delay_time 15
|
94
|
+
|
95
|
+
children do
|
96
|
+
b.luton type: 'location'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
let(:expected_geoff) do
|
104
|
+
strip_whitespace <<-EOS
|
105
|
+
(starbucks) {"_classname":"Cafe"}
|
106
|
+
(Cafe)-[:all]->(starbucks)
|
107
|
+
(luton) {"_classname":"Area","name":"LU1"}
|
108
|
+
(Area)-[:all]->(luton)
|
109
|
+
(starbucks)-[:headquarters_location]->(luton)
|
110
|
+
(starbucks_branch_1) {"_classname":"Branch","delay_time":15}
|
111
|
+
(Branch)-[:all]->(starbucks_branch_1)
|
112
|
+
(starbucks_branch_1)-[:location]->(luton)
|
113
|
+
(starbucks)-[:owns]->(starbucks_branch_1) {"some":"property"}
|
114
|
+
EOS
|
115
|
+
end
|
116
|
+
|
117
|
+
specify do
|
118
|
+
geoff = node.to_geoff
|
119
|
+
geoff.should == expected_geoff
|
120
|
+
end
|
121
|
+
|
122
|
+
context "with relation type at the children level" do
|
123
|
+
let(:expected_geoff) do
|
124
|
+
strip_whitespace <<-EOS
|
125
|
+
(starbucks) {"_classname":"Cafe"}
|
126
|
+
(Cafe)-[:all]->(starbucks)
|
127
|
+
(luton) {"_classname":"Area","name":"LU1"}
|
128
|
+
(Area)-[:all]->(luton)
|
129
|
+
(starbucks)-[:location]->(luton)
|
130
|
+
EOS
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
specify do
|
135
|
+
node = NodeDsl.new(node_name: 'starbucks', klass_name: 'Cafe') do
|
136
|
+
children "location" do
|
137
|
+
area 'luton' do
|
138
|
+
name 'LU1'
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
node.to_geoff.should == expected_geoff
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.expand_path('spec/spec_helper')
|
2
|
+
require 'neo4j'
|
3
|
+
require 'geoff'
|
4
|
+
|
5
|
+
describe Geoff do
|
6
|
+
class CoverageArea
|
7
|
+
include Neo4j::NodeMixin
|
8
|
+
end
|
9
|
+
class Cafe
|
10
|
+
include Neo4j::NodeMixin
|
11
|
+
end
|
12
|
+
|
13
|
+
class Carrier
|
14
|
+
include Neo4j::NodeMixin
|
15
|
+
end
|
16
|
+
|
17
|
+
class Branch
|
18
|
+
include Neo4j::NodeMixin
|
19
|
+
end
|
20
|
+
describe 'base nodes' do
|
21
|
+
|
22
|
+
let(:builder) do
|
23
|
+
Geoff(Cafe, Carrier, Branch, CoverageArea)
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:expected_geoff) do
|
27
|
+
strip_whitespace <<-EOS
|
28
|
+
(ROOT)-[:Cafe]->(Cafe)
|
29
|
+
(ROOT)-[:Carrier]->(Carrier)
|
30
|
+
(ROOT)-[:Branch]->(Branch)
|
31
|
+
(ROOT)-[:CoverageArea]->(CoverageArea)
|
32
|
+
EOS
|
33
|
+
end
|
34
|
+
|
35
|
+
specify do
|
36
|
+
builder.to_s.should == expected_geoff
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'Geoff sugar' do
|
41
|
+
let(:node) do
|
42
|
+
Geoff(Branch) do
|
43
|
+
branch 'starbucks_branch' do
|
44
|
+
delay_time 15
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
let(:expected_geoff) do
|
50
|
+
strip_whitespace <<-EOS
|
51
|
+
(ROOT)-[:Branch]->(Branch)
|
52
|
+
(starbucks_branch) {"_classname":"Branch","delay_time":15}
|
53
|
+
(Branch)-[:all]->(starbucks_branch)
|
54
|
+
EOS
|
55
|
+
end
|
56
|
+
|
57
|
+
specify do
|
58
|
+
node.to_geoff.should == expected_geoff
|
59
|
+
end
|
60
|
+
|
61
|
+
specify do
|
62
|
+
pending 'not implemented'
|
63
|
+
->{
|
64
|
+
Geoff do
|
65
|
+
branch 'starbucks_branch' do
|
66
|
+
delay_time 15
|
67
|
+
end
|
68
|
+
end
|
69
|
+
}.should raise_error Geoff::MissingClassDefinition, "branch"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.expand_path('spec/spec_helper')
|
2
|
+
require 'neo4j'
|
3
|
+
require './lib/geoff/neo4j_wrapper_dsl'
|
4
|
+
|
5
|
+
describe Neo4jWrapperDsl do
|
6
|
+
let(:expected_geoff) do
|
7
|
+
strip_whitespace <<-EOS
|
8
|
+
(ROOT)-[:Sandwich]->(Sandwich)
|
9
|
+
(ROOT)-[:Cheese]->(Cheese)
|
10
|
+
(ROOT)-[:Egg]->(Egg)
|
11
|
+
EOS
|
12
|
+
end
|
13
|
+
|
14
|
+
class Sandwich
|
15
|
+
include Neo4j::NodeMixin
|
16
|
+
end
|
17
|
+
|
18
|
+
class Cafe
|
19
|
+
include Neo4j::NodeMixin
|
20
|
+
end
|
21
|
+
|
22
|
+
class Branch
|
23
|
+
include Neo4j::NodeMixin
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
class Cheese
|
28
|
+
include Neo4j::NodeMixin
|
29
|
+
end
|
30
|
+
|
31
|
+
class Egg
|
32
|
+
include Neo4j::NodeMixin
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
describe "#to_s" do
|
37
|
+
specify do
|
38
|
+
dsl = Neo4jWrapperDsl.new Sandwich, Cheese, Egg
|
39
|
+
dsl.to_s.should == dsl.to_geoff
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#to_geoff" do
|
44
|
+
it "outputs root node geoff syntax" do
|
45
|
+
dsl = Neo4jWrapperDsl.new(Sandwich, Cheese, Egg)
|
46
|
+
dsl.to_geoff.should == expected_geoff
|
47
|
+
end
|
48
|
+
|
49
|
+
specify do
|
50
|
+
->{Neo4jWrapperDsl.new 2 } .should raise_error ArgumentError
|
51
|
+
->{Neo4jWrapperDsl.new '' } .should raise_error ArgumentError
|
52
|
+
->{Neo4jWrapperDsl.new ['']}.should raise_error ArgumentError
|
53
|
+
->{Neo4jWrapperDsl.new [1]} .should raise_error ArgumentError
|
54
|
+
|
55
|
+
->{Neo4jWrapperDsl.new(Egg)}.should_not raise_error ArgumentError
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
context "with a block" do
|
60
|
+
let(:expected) {
|
61
|
+
strip_whitespace <<-EOS
|
62
|
+
(ROOT)-[:Cafe]->(Cafe)
|
63
|
+
(ROOT)-[:Branch]->(Branch)
|
64
|
+
(Starbucks) {"_classname":"Cafe"}
|
65
|
+
(Cafe)-[:all]->(Starbucks)
|
66
|
+
(starbucks_branch) {"_classname":"Branch"}
|
67
|
+
(Branch)-[:all]->(starbucks_branch)
|
68
|
+
(Starbucks)-[:owns]->(starbucks_branch)
|
69
|
+
EOS
|
70
|
+
}
|
71
|
+
|
72
|
+
specify "with parent node is root node, but no relationship,
|
73
|
+
don't create relationship to root node" do
|
74
|
+
Neo4jWrapperDsl.new(Cafe, Branch) do
|
75
|
+
cafe 'Starbucks' do
|
76
|
+
children 'owns' do
|
77
|
+
#create relationship to parent node of type owns (comes from children DSL)
|
78
|
+
branch 'starbucks_branch'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end.to_geoff.should == expected
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require './lib/geoff'
|
2
|
+
require './lib/geoff/neo4j_wrapper_validator'
|
3
|
+
|
4
|
+
describe Neo4jWrapperValidator do
|
5
|
+
let(:validator) { Neo4jWrapperValidator.new }
|
6
|
+
|
7
|
+
{
|
8
|
+
"(Egg)-[:relates]->(1)" => {existing_nodes: %w(Branch Starbucks Cafe), node_name: "Egg", valid: false},
|
9
|
+
"((Branch)-[:relates]->(1)" => {existing_nodes: %w(Branch Cafe Starbucks), node_name: "Branch", valid: false},
|
10
|
+
"(Starbucks)-[:relates]->(1)" => {existing_nodes: %w(Branch Cafe Starbucks), node_name: "Starbucks", valid: true},
|
11
|
+
"((_Branch)-[:relates]->(1)" => {existing_nodes: %w(Branch), node_name: "(_Branch", valid: false},
|
12
|
+
"((Bra nch)-[:relates]->(1)" => {existing_nodes: %w(Branch Cafe), node_name: "(Bra nch", valid: false},
|
13
|
+
"((Branch)-[:relates]->(1)" => {existing_nodes: %w(Branch Cafe), node_name: "(Branch", valid: false},
|
14
|
+
"(Branch)-[:relates]->(1)" => {existing_nodes: [], node_name: "Branch", valid: false}}.each do |input, details|
|
15
|
+
specify do
|
16
|
+
valid = details[:valid]
|
17
|
+
existing_nodes = details[:existing_nodes]
|
18
|
+
node_name = details[:node_name]
|
19
|
+
container = mock 'container', node_list: existing_nodes
|
20
|
+
|
21
|
+
if valid
|
22
|
+
validator.call(container, input).should == true
|
23
|
+
else
|
24
|
+
->{validator.call(container, input)}.should raise_error Geoff::MissingNodeDefinition, node_name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
def parse_geoff_node(expr)
|
4
|
+
GeoffNodeMatcher.new(expr)
|
5
|
+
end
|
6
|
+
|
7
|
+
def parse_geoff_relation(expr)
|
8
|
+
GeoffRelationshipMatcher.new(expr)
|
9
|
+
end
|
10
|
+
|
11
|
+
def strip_whitespace s
|
12
|
+
s.gsub(/^\s*/, '').chomp
|
13
|
+
end
|
14
|
+
|
15
|
+
class GeoffNodeMatcher
|
16
|
+
|
17
|
+
@@regex = /\((\w+)\) (.*)/
|
18
|
+
|
19
|
+
def initialize(expression)
|
20
|
+
@@regex.match expression
|
21
|
+
|
22
|
+
@node_name = $1
|
23
|
+
@attributes = JSON.parse($2, symbolize_names: true)
|
24
|
+
rescue
|
25
|
+
@attributes = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def named?(name)
|
29
|
+
@node_name == name
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_attribute?(name, value)
|
33
|
+
@attributes[name.to_sym] == value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class GeoffRelationshipMatcher
|
38
|
+
@@regex = /\((.*)\)-\[\:(.*)\]->\((.*)\)/
|
39
|
+
|
40
|
+
def initialize(expression)
|
41
|
+
@@regex.match expression
|
42
|
+
|
43
|
+
@source_name = $1
|
44
|
+
@relationship_name = $2
|
45
|
+
@dest_name = $3
|
46
|
+
end
|
47
|
+
|
48
|
+
def named?(name)
|
49
|
+
@relationship_name == name
|
50
|
+
end
|
51
|
+
end
|