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
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
geoff (0.2.4.beta)
|
5
|
+
activesupport (~> 3.2.3)
|
6
|
+
json
|
7
|
+
neo4j
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
actionpack (3.2.6)
|
13
|
+
activemodel (= 3.2.6)
|
14
|
+
activesupport (= 3.2.6)
|
15
|
+
builder (~> 3.0.0)
|
16
|
+
erubis (~> 2.7.0)
|
17
|
+
journey (~> 1.0.1)
|
18
|
+
rack (~> 1.4.0)
|
19
|
+
rack-cache (~> 1.2)
|
20
|
+
rack-test (~> 0.6.1)
|
21
|
+
sprockets (~> 2.1.3)
|
22
|
+
activemodel (3.2.6)
|
23
|
+
activesupport (= 3.2.6)
|
24
|
+
builder (~> 3.0.0)
|
25
|
+
activesupport (3.2.6)
|
26
|
+
i18n (~> 0.6)
|
27
|
+
multi_json (~> 1.0)
|
28
|
+
builder (3.0.0)
|
29
|
+
coderay (1.0.7)
|
30
|
+
columnize (0.3.6)
|
31
|
+
diff-lcs (1.1.3)
|
32
|
+
erubis (2.7.0)
|
33
|
+
hike (1.2.1)
|
34
|
+
i18n (0.6.0)
|
35
|
+
journey (1.0.4)
|
36
|
+
json (1.7.4-java)
|
37
|
+
linecache (0.46)
|
38
|
+
rbx-require-relative (> 0.0.4)
|
39
|
+
method_source (0.8)
|
40
|
+
multi_json (1.3.6)
|
41
|
+
neo4j (2.0.1-java)
|
42
|
+
activemodel (>= 3.0.0, < 3.3)
|
43
|
+
neo4j-wrapper (= 2.0.1)
|
44
|
+
orm_adapter (>= 0.0.3)
|
45
|
+
railties (>= 3.0.0, < 3.3)
|
46
|
+
neo4j-community (1.7.1-java)
|
47
|
+
neo4j-core (2.0.1-java)
|
48
|
+
neo4j-community (>= 1.7.0)
|
49
|
+
neo4j-wrapper (2.0.1-java)
|
50
|
+
neo4j-core (= 2.0.1)
|
51
|
+
orm_adapter (0.3.0)
|
52
|
+
pry (0.9.10)
|
53
|
+
coderay (~> 1.0.5)
|
54
|
+
method_source (~> 0.8)
|
55
|
+
slop (~> 3.3.1)
|
56
|
+
pry (0.9.10-java)
|
57
|
+
coderay (~> 1.0.5)
|
58
|
+
method_source (~> 0.8)
|
59
|
+
slop (~> 3.3.1)
|
60
|
+
spoon (~> 0.0)
|
61
|
+
rack (1.4.1)
|
62
|
+
rack-cache (1.2)
|
63
|
+
rack (>= 0.4)
|
64
|
+
rack-ssl (1.3.2)
|
65
|
+
rack
|
66
|
+
rack-test (0.6.1)
|
67
|
+
rack (>= 1.0)
|
68
|
+
railties (3.2.6)
|
69
|
+
actionpack (= 3.2.6)
|
70
|
+
activesupport (= 3.2.6)
|
71
|
+
rack-ssl (~> 1.3.2)
|
72
|
+
rake (>= 0.8.7)
|
73
|
+
rdoc (~> 3.4)
|
74
|
+
thor (>= 0.14.6, < 2.0)
|
75
|
+
rake (0.9.2.2)
|
76
|
+
rbx-require-relative (0.0.9)
|
77
|
+
rdoc (3.12)
|
78
|
+
json (~> 1.4)
|
79
|
+
rspec (2.11.0)
|
80
|
+
rspec-core (~> 2.11.0)
|
81
|
+
rspec-expectations (~> 2.11.0)
|
82
|
+
rspec-mocks (~> 2.11.0)
|
83
|
+
rspec-core (2.11.1)
|
84
|
+
rspec-expectations (2.11.2)
|
85
|
+
diff-lcs (~> 1.1.3)
|
86
|
+
rspec-mocks (2.11.1)
|
87
|
+
ruby-debug (0.10.4)
|
88
|
+
columnize (>= 0.1)
|
89
|
+
ruby-debug-base (~> 0.10.4.0)
|
90
|
+
ruby-debug-base (0.10.4)
|
91
|
+
linecache (>= 0.3)
|
92
|
+
ruby-debug-base (0.10.4-java)
|
93
|
+
slop (3.3.2)
|
94
|
+
spoon (0.0.1)
|
95
|
+
sprockets (2.1.3)
|
96
|
+
hike (~> 1.2)
|
97
|
+
rack (~> 1.0)
|
98
|
+
tilt (~> 1.1, != 1.3.0)
|
99
|
+
thor (0.15.4)
|
100
|
+
tilt (1.3.3)
|
101
|
+
|
102
|
+
PLATFORMS
|
103
|
+
java
|
104
|
+
ruby
|
105
|
+
|
106
|
+
DEPENDENCIES
|
107
|
+
geoff!
|
108
|
+
pry
|
109
|
+
rspec (~> 2.11.0)
|
110
|
+
ruby-debug
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 David Rouchy
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
geoff
|
2
|
+
=====
|
3
|
+
|
4
|
+
Ruby Geoff DSL
|
5
|
+
|
6
|
+
|
7
|
+
Preview
|
8
|
+
=======
|
9
|
+
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
|
13
|
+
#Basic tree like structure for DSL
|
14
|
+
#the first line generates the class nodes used by Neo4jWrapper
|
15
|
+
Geoff(Company, Person) do
|
16
|
+
company 'Acme' do
|
17
|
+
address "13 Something Road"
|
18
|
+
|
19
|
+
children :employees do
|
20
|
+
person 'Geoff' do
|
21
|
+
name 'Geoff'
|
22
|
+
end
|
23
|
+
|
24
|
+
person 'Nigel' do
|
25
|
+
name 'Nigel Small'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
company 'Github' do
|
31
|
+
children :customers do
|
32
|
+
person 'Tom'
|
33
|
+
person 'Dick'
|
34
|
+
person 'Harry'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
```
|
40
|
+
|
41
|
+
```
|
42
|
+
(ROOT)-[:Company]->(Company)
|
43
|
+
(ROOT)-[:Person]->(Person)
|
44
|
+
(Acme) {"_classname":"Company","address":"13 Something Road"}
|
45
|
+
(Company)-[:all]->(Acme)
|
46
|
+
(Geoff) {"_classname":"Person","name":"Geoff"}
|
47
|
+
(Person)-[:all]->(Geoff)
|
48
|
+
(Acme)-[:employees]->(Geoff)
|
49
|
+
(Nigel) {"_classname":"Person","name":"Nigel Small"}
|
50
|
+
(Person)-[:all]->(Nigel)
|
51
|
+
(Acme)-[:employees]->(Nigel)
|
52
|
+
(Github) {"_classname":"Company"}
|
53
|
+
(Company)-[:all]->(Github)
|
54
|
+
(Tom) {"_classname":"Person"}
|
55
|
+
(Person)-[:all]->(Tom)
|
56
|
+
(Github)-[:customers]->(Tom)
|
57
|
+
(Dick) {"_classname":"Person"}
|
58
|
+
(Person)-[:all]->(Dick)
|
59
|
+
(Github)-[:customers]->(Dick)
|
60
|
+
(Harry) {"_classname":"Person"}
|
61
|
+
(Person)-[:all]->(Harry)
|
62
|
+
(Github)-[:customers]->(Harry)
|
63
|
+
```
|
64
|
+
|
65
|
+
#Individual relationship overrides
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
Geoff(Company, Person) do
|
69
|
+
company 'Amazon' do
|
70
|
+
children do
|
71
|
+
person 'Tom', type: :customers
|
72
|
+
person 'Tom', type: :supplier
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
```
|
79
|
+
(ROOT)-[:Company]->(Company)
|
80
|
+
(ROOT)-[:Person]->(Person)
|
81
|
+
(Amazon) {"_classname":"Company"}
|
82
|
+
(Company)-[:all]->(Amazon)
|
83
|
+
(Tom) {"_classname":"Person"}
|
84
|
+
(Person)-[:all]->(Tom)
|
85
|
+
(Amazon)-[:customers]->(Tom)
|
86
|
+
(Tom) {"_classname":"Person"}
|
87
|
+
(Person)-[:all]->(Tom)
|
88
|
+
(Amazon)-[:supplier]->(Tom)
|
89
|
+
```
|
90
|
+
|
91
|
+
|
92
|
+
#Link arbitrary nodes in different branches of the tree
|
93
|
+
##Uses the magic 'b' method
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
Geoff(Company, Person) do
|
97
|
+
company 'Amazon' do
|
98
|
+
children 'employees' do
|
99
|
+
b.judas = person 'Judas'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
company 'Moonlighters' do
|
104
|
+
children do
|
105
|
+
b.judas type: 'employees'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
111
|
+
```
|
112
|
+
(ROOT)-[:Company]->(Company)
|
113
|
+
(ROOT)-[:Person]->(Person)
|
114
|
+
(Amazon) {"_classname":"Company"}
|
115
|
+
(Company)-[:all]->(Amazon)
|
116
|
+
(Tom) {"_classname":"Person"}
|
117
|
+
(Person)-[:all]->(Tom)
|
118
|
+
(Amazon)-[:employees]->(Tom)
|
119
|
+
(Moonlighters) {"_classname":"Company"}
|
120
|
+
(Company)-[:all]->(Moonlighters)
|
121
|
+
(Moonlighters)-[:employees]->(Tom)
|
122
|
+
```
|
123
|
+
|
124
|
+
|
125
|
+
#Using the outer bindings scope
|
126
|
+
```ruby
|
127
|
+
@hours = SomeFancyAttributeParser.parse <<-EOF
|
128
|
+
Monday 09:00-15:00
|
129
|
+
Tuesday 13:00-19:00
|
130
|
+
Saturday 09:00-16:00
|
131
|
+
Sunday closed
|
132
|
+
EOF
|
133
|
+
|
134
|
+
Geoff(Company, binding: binding) do
|
135
|
+
company 'Amazon' do
|
136
|
+
opening_hours ->{ @hours }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
|
data/Rakefile
ADDED
data/geoff.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/geoff/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["David Rouchy", "Mark Burns", "Phyo Wai Win",
|
6
|
+
"Kajetan Bojko", "Volker Pacher"]
|
7
|
+
|
8
|
+
gem.email = ["davidr@shutl.co.uk", "phyo@shutl.co.uk",
|
9
|
+
"volkerpacher@gmail.com", "kai@shutl.co.uk",
|
10
|
+
"markthedeveloper@gmail.com"]
|
11
|
+
|
12
|
+
gem.description = %q{Geoff syntax builder and Neo4j batch importer}
|
13
|
+
gem.summary = %q{DSL to allow easy generating of data for Neo4j}
|
14
|
+
gem.homepage = ""
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($\)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.name = "geoff"
|
20
|
+
gem.require_paths = ["lib"]
|
21
|
+
gem.version = Geoff::VERSION
|
22
|
+
|
23
|
+
gem.add_dependency 'neo4j'
|
24
|
+
gem.add_dependency 'activesupport', '~> 3.2.3'
|
25
|
+
gem.add_dependency 'json'
|
26
|
+
|
27
|
+
gem.add_development_dependency 'rspec', '~>2.11.0'
|
28
|
+
gem.add_development_dependency 'ruby-debug'
|
29
|
+
gem.add_development_dependency 'pry'
|
30
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'geoff/node_dsl'
|
2
|
+
require 'geoff/container'
|
3
|
+
require 'active_support/core_ext/string'
|
4
|
+
|
5
|
+
class ChildrenDsl
|
6
|
+
def initialize options, &block
|
7
|
+
@parent_node_name = options[:parent_node_name]
|
8
|
+
@parent_rel_type = options[:type]
|
9
|
+
@outer_binding = options[:binding]
|
10
|
+
@outer_geoff = options[:geoff]
|
11
|
+
|
12
|
+
@write_mode = false
|
13
|
+
|
14
|
+
@node_dsls = {}
|
15
|
+
@container = options[:container] || Container.new
|
16
|
+
write_mode { instance_eval(&block) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_geoff
|
20
|
+
"#{@outer_geoff}\n#{geoff_lines.join("\n")}".strip
|
21
|
+
end
|
22
|
+
|
23
|
+
def geoff_lines
|
24
|
+
lines = []
|
25
|
+
|
26
|
+
@node_dsls.each do |node_dsl, properties|
|
27
|
+
lines += node_dsl.geoff_lines
|
28
|
+
lines << rel_geoff(node_dsl, properties) unless top_level_node?
|
29
|
+
end
|
30
|
+
|
31
|
+
lines
|
32
|
+
end
|
33
|
+
|
34
|
+
def rel_geoff node_dsl, properties
|
35
|
+
properties = properties.dup
|
36
|
+
type = properties.delete :type
|
37
|
+
|
38
|
+
"(#{@parent_node_name})-[:#{type}]->(#{node_dsl.node_name})".tap do |r|
|
39
|
+
r << " #{properties.to_json}" if properties.any?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def b
|
44
|
+
@container.set_recipient_of_node_dsl self
|
45
|
+
@container
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_node_dsl node_dsl, rel_properties
|
49
|
+
@node_dsls[node_dsl] = rel_properties
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
#prevent very confusing method_missing calls during debugging by
|
55
|
+
#only writing to the object when expected
|
56
|
+
def write_mode
|
57
|
+
@write_mode = true
|
58
|
+
yield
|
59
|
+
@write_mode = false
|
60
|
+
end
|
61
|
+
|
62
|
+
# e.g.
|
63
|
+
#company "ACME" do
|
64
|
+
# children "works_at" do
|
65
|
+
# employee 'Bob'
|
66
|
+
# employee 'Lenny'
|
67
|
+
# employee 'Tom'
|
68
|
+
# end
|
69
|
+
#end
|
70
|
+
#
|
71
|
+
def method_missing m, *args, &blk
|
72
|
+
return super unless @write_mode
|
73
|
+
relationship, options = parse_method_missing m, *args
|
74
|
+
|
75
|
+
options.merge!(binding: @outer_binding)
|
76
|
+
NodeDsl.new(options, &blk).tap do |n|
|
77
|
+
@node_dsls[n] = relationship
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_method_missing m, *args
|
82
|
+
node_name = args.first
|
83
|
+
relationship = args[1] || {}
|
84
|
+
relationship[:type] ||= @parent_rel_type
|
85
|
+
|
86
|
+
if type_missing? relationship
|
87
|
+
raise ArgumentError, "Missing relationship type for node #{node_name}"
|
88
|
+
end
|
89
|
+
|
90
|
+
return [
|
91
|
+
relationship,
|
92
|
+
|
93
|
+
{
|
94
|
+
node_name: node_name,
|
95
|
+
klass_name: m.to_s.camelize,
|
96
|
+
rel_type: relationship[:type],
|
97
|
+
container: @container
|
98
|
+
}
|
99
|
+
]
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
def type_missing? r
|
104
|
+
!top_level_node? and !r[:type]
|
105
|
+
end
|
106
|
+
|
107
|
+
def top_level_node?
|
108
|
+
@parent_node_name == "ROOT"
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Container
|
2
|
+
def initialize validator=nil
|
3
|
+
@node_dsls = {}
|
4
|
+
@validator = validator || ->(_, _){}
|
5
|
+
end
|
6
|
+
|
7
|
+
def method_missing m, *args, &blk
|
8
|
+
if m.to_s.last == "=" # assignment
|
9
|
+
@node_dsls[m.to_s[0..-2]] = args.first
|
10
|
+
else
|
11
|
+
node_dsl = @node_dsls[m.to_s]
|
12
|
+
rel_properties = args.first
|
13
|
+
@recipient.add_node_dsl node_dsl, rel_properties
|
14
|
+
node_dsl
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_recipient_of_node_dsl children_dsl
|
19
|
+
@recipient = children_dsl
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'fileutils'
|
3
|
+
Dir["lib/jars/*"].each {|file| require file }
|
4
|
+
|
5
|
+
module Geoff
|
6
|
+
class MissingIndexedProperty < StandardError; end
|
7
|
+
|
8
|
+
class Importer
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def import_files *files, options
|
12
|
+
concatenated = files.map{|f|File.read f}.join "\n"
|
13
|
+
|
14
|
+
import concatenated, options
|
15
|
+
end
|
16
|
+
|
17
|
+
def import_file(file, options = {})
|
18
|
+
rules = File.read(file)
|
19
|
+
import(rules, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def import(geoff, options = {})
|
23
|
+
db_path = options[:test] ? 'db/test' : 'db/development'
|
24
|
+
|
25
|
+
Neo4j::Config[:storage_path] = db_path
|
26
|
+
|
27
|
+
geoff = geoff.to_geoff unless geoff.is_a? String
|
28
|
+
|
29
|
+
geoff = geoff.split "\n" if geoff.is_a? String
|
30
|
+
|
31
|
+
new(geoff, options).go
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(rules, options)
|
36
|
+
@rules = rules
|
37
|
+
@options = options
|
38
|
+
log @options.inspect
|
39
|
+
end
|
40
|
+
|
41
|
+
def go
|
42
|
+
raise 'Invalid rules' unless validate_rules(@rules)
|
43
|
+
delete_database if drop?
|
44
|
+
|
45
|
+
log 'importing the database'
|
46
|
+
|
47
|
+
import_geoff_rules @rules
|
48
|
+
rebuild_indexes
|
49
|
+
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def delete_database
|
56
|
+
if testmode?
|
57
|
+
Neo4j::Transaction.run do
|
58
|
+
Neo4j.db.each_node do |n|
|
59
|
+
n.del unless n.neo_id == 0
|
60
|
+
end
|
61
|
+
end
|
62
|
+
else
|
63
|
+
Neo4j.db.shutdown
|
64
|
+
FileUtils.rm_rf Neo4j::Config[:storage_path]
|
65
|
+
log "restarting the database #{Neo4j::Config[:storage_path]}"
|
66
|
+
Neo4j.db.start
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def import_geoff_rules(rules)
|
71
|
+
sub_graph = org.neo4j.geoff.Subgraph.new(rules)
|
72
|
+
node_map = { '0' => root_node }
|
73
|
+
org.neo4j.geoff.Geoff.insertIntoNeo4j(sub_graph, Neo4j.db.graph, node_map );
|
74
|
+
end
|
75
|
+
|
76
|
+
def rebuild_indexes
|
77
|
+
log 're-building indexes'
|
78
|
+
types = root_node.relationships.map { |r| Kernel.const_get(r.get_type.to_s) }
|
79
|
+
|
80
|
+
Neo4j::Transaction.run do
|
81
|
+
types.each do |type|
|
82
|
+
re_index(type)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def re_index(type)
|
88
|
+
log "-> re-indexing #{type}"
|
89
|
+
props = type.instance_variable_get(:@_decl_props)
|
90
|
+
indexed_props = props.find_all { |_, p| p.has_key?(:index) }
|
91
|
+
|
92
|
+
indexed_props.each do |index|
|
93
|
+
all = type.all
|
94
|
+
|
95
|
+
all.each do |node|
|
96
|
+
@current_node = node
|
97
|
+
@current_index = index.first
|
98
|
+
node.add_index index.first
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
rescue NativeException => e
|
103
|
+
log "-" * 80
|
104
|
+
message = "Exception during re-indexing #{type} with #{@current_node} and index #{@current_index}"
|
105
|
+
log message
|
106
|
+
log "-" * 80
|
107
|
+
log e.message
|
108
|
+
log "-" * 80
|
109
|
+
#raise Geoff::MissingIndexedProperty, message
|
110
|
+
end
|
111
|
+
|
112
|
+
def tx
|
113
|
+
Neo4j::Transaction.run { yield if block_given? }
|
114
|
+
end
|
115
|
+
|
116
|
+
def root_node
|
117
|
+
Neo4j.ref_node
|
118
|
+
end
|
119
|
+
|
120
|
+
def log(message)
|
121
|
+
$stdout.puts message unless silent?
|
122
|
+
end
|
123
|
+
|
124
|
+
def drop?
|
125
|
+
option_value?(:drop, false)
|
126
|
+
end
|
127
|
+
|
128
|
+
def silent?
|
129
|
+
option_value? :silent
|
130
|
+
end
|
131
|
+
|
132
|
+
def testmode?
|
133
|
+
option_value? :test
|
134
|
+
end
|
135
|
+
|
136
|
+
def option_value?(key, default = false)
|
137
|
+
@options[key].nil? ? default : @options[key]
|
138
|
+
end
|
139
|
+
|
140
|
+
def validate_rules(rules)
|
141
|
+
invalid = Array(rules).reject{ |r| validate_rule(r) }
|
142
|
+
invalid.each { |r| log "Rule '#{r}' is invalid" }
|
143
|
+
invalid.empty?
|
144
|
+
end
|
145
|
+
|
146
|
+
def validate_rule(rule)
|
147
|
+
org.neo4j.geoff.Subgraph.new(Array(rule))
|
148
|
+
true
|
149
|
+
rescue
|
150
|
+
false
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'geoff/children_dsl'
|
2
|
+
|
3
|
+
class Neo4jWrapperDsl
|
4
|
+
def initialize *classes, &block
|
5
|
+
options = classes.last.is_a?(Hash) ? classes.pop : {}
|
6
|
+
|
7
|
+
validate classes
|
8
|
+
|
9
|
+
options.merge!({
|
10
|
+
parent_node_name: 'ROOT',
|
11
|
+
type: nil,
|
12
|
+
container: Container.new
|
13
|
+
})
|
14
|
+
|
15
|
+
@children_dsl = ChildrenDsl.new(options, &block) if block_given?
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_geoff
|
19
|
+
geoff = "#{add_classes}\n"
|
20
|
+
geoff += @children_dsl.to_geoff if @children_dsl
|
21
|
+
geoff.chomp
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
to_geoff
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def add_classes
|
31
|
+
@classes.map{|c| root_to_class_as_geoff c }.join("\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
def root_to_class_as_geoff c
|
35
|
+
"(ROOT)-[:#{c}]->(#{c})"
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate classes
|
39
|
+
@classes = classes || []
|
40
|
+
|
41
|
+
@classes.each do |c|
|
42
|
+
m = "Class #{c} should include Neo4j::NodeMixin"
|
43
|
+
raise ArgumentError, m unless c.is_a?(Class) and c.included_modules.include? Neo4j::NodeMixin
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Neo4jWrapperValidator
|
2
|
+
NODE_NAME_REGEX = /^\(([^\)]+)\)/
|
3
|
+
|
4
|
+
def call container, node
|
5
|
+
node_name = node.match(NODE_NAME_REGEX)[1]
|
6
|
+
|
7
|
+
unless class_present? container, node_name
|
8
|
+
raise Geoff::MissingNodeDefinition, node_name
|
9
|
+
end
|
10
|
+
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def class_present? container, node_name
|
15
|
+
container.node_list.include? node_name
|
16
|
+
end
|
17
|
+
end
|