geoff 0.0.2.beta
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.
- 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
|