geoff 0.0.2.beta → 0.0.3.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rvmrc +1 -0
- data/Gemfile.lock +12 -12
- data/README.md +52 -6
- data/lib/geoff.rb +1 -0
- data/lib/geoff/children_dsl.rb +7 -11
- data/lib/geoff/dsl_exception_handling.rb +52 -0
- data/lib/geoff/importer.rb +24 -18
- data/lib/geoff/neo4j_wrapper_dsl.rb +4 -0
- data/lib/geoff/node_dsl.rb +11 -15
- data/lib/geoff/version.rb +1 -1
- data/spec/integration/children_dsl_spec.rb +3 -3
- data/spec/integration/node_dsl_spec.rb +4 -4
- metadata +4 -2
data/.gitignore
CHANGED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use jruby
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
geoff (0.
|
4
|
+
geoff (0.0.3.beta)
|
5
5
|
activesupport (~> 3.2.3)
|
6
6
|
json
|
7
7
|
neo4j
|
@@ -9,20 +9,20 @@ PATH
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
actionpack (3.2.
|
13
|
-
activemodel (= 3.2.
|
14
|
-
activesupport (= 3.2.
|
12
|
+
actionpack (3.2.8)
|
13
|
+
activemodel (= 3.2.8)
|
14
|
+
activesupport (= 3.2.8)
|
15
15
|
builder (~> 3.0.0)
|
16
16
|
erubis (~> 2.7.0)
|
17
|
-
journey (~> 1.0.
|
17
|
+
journey (~> 1.0.4)
|
18
18
|
rack (~> 1.4.0)
|
19
19
|
rack-cache (~> 1.2)
|
20
20
|
rack-test (~> 0.6.1)
|
21
21
|
sprockets (~> 2.1.3)
|
22
|
-
activemodel (3.2.
|
23
|
-
activesupport (= 3.2.
|
22
|
+
activemodel (3.2.8)
|
23
|
+
activesupport (= 3.2.8)
|
24
24
|
builder (~> 3.0.0)
|
25
|
-
activesupport (3.2.
|
25
|
+
activesupport (3.2.8)
|
26
26
|
i18n (~> 0.6)
|
27
27
|
multi_json (~> 1.0)
|
28
28
|
builder (3.0.0)
|
@@ -48,7 +48,7 @@ GEM
|
|
48
48
|
neo4j-community (>= 1.7.0)
|
49
49
|
neo4j-wrapper (2.0.1-java)
|
50
50
|
neo4j-core (= 2.0.1)
|
51
|
-
orm_adapter (0.
|
51
|
+
orm_adapter (0.4.0)
|
52
52
|
pry (0.9.10)
|
53
53
|
coderay (~> 1.0.5)
|
54
54
|
method_source (~> 0.8)
|
@@ -65,9 +65,9 @@ GEM
|
|
65
65
|
rack
|
66
66
|
rack-test (0.6.1)
|
67
67
|
rack (>= 1.0)
|
68
|
-
railties (3.2.
|
69
|
-
actionpack (= 3.2.
|
70
|
-
activesupport (= 3.2.
|
68
|
+
railties (3.2.8)
|
69
|
+
actionpack (= 3.2.8)
|
70
|
+
activesupport (= 3.2.8)
|
71
71
|
rack-ssl (~> 1.3.2)
|
72
72
|
rake (>= 0.8.7)
|
73
73
|
rdoc (~> 3.4)
|
data/README.md
CHANGED
@@ -1,18 +1,39 @@
|
|
1
1
|
geoff
|
2
2
|
=====
|
3
3
|
|
4
|
-
|
4
|
+
Geoff is "a declarative notation for representing graph data within concise
|
5
|
+
human-readable text, designed specifically with Neo4j in mind"
|
5
6
|
|
7
|
+
http://geoff.nigelsmall.net/
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
+
|
10
|
+
This gem is a Ruby DSL for
|
11
|
+
* generating geoff syntax files
|
12
|
+
* batch inserting data into Neo4j
|
13
|
+
|
14
|
+
The reason for creating this gem is to:
|
15
|
+
* easily build data sets for tests
|
16
|
+
* that are readable and maintainable
|
17
|
+
* quickly batch insert the whole data set in one transaction
|
18
|
+
|
19
|
+
Prerequesites/ Caveats
|
20
|
+
---------------------
|
21
|
+
* A ruby project
|
22
|
+
* Gemfile with `gem 'geoff', '0.0.2.beta'`
|
23
|
+
* neo4j jar and geoff jar files in lib/jars (not included, but required!)
|
24
|
+
* Current implementation assumes usage of the neo4j wrapper gem
|
25
|
+
* Neo4j::Rails::Model classes or a class that includes the Neo4j::NodeMixin
|
26
|
+
|
27
|
+
Usage
|
28
|
+
-------
|
9
29
|
|
10
30
|
|
11
31
|
```ruby
|
12
32
|
|
13
33
|
#Basic tree like structure for DSL
|
14
34
|
#the first line generates the class nodes used by Neo4jWrapper
|
15
|
-
|
35
|
+
|
36
|
+
Geoff(Company, Person) do # NB 'Company' and 'Person' are classes with the Neo4j::NodeMixin
|
16
37
|
company 'Acme' do
|
17
38
|
address "13 Something Road"
|
18
39
|
|
@@ -38,6 +59,31 @@ end
|
|
38
59
|
|
39
60
|
```
|
40
61
|
|
62
|
+
|
63
|
+
###Using a ramdisk see http://neo4j.rubyforge.org/guides/configuration.html
|
64
|
+
|
65
|
+
```sh
|
66
|
+
diskutil erasevolume HFS+ "ramdisk" `hdiutil attach -nomount ram://1165430`
|
67
|
+
```
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
|
71
|
+
#in spec helper
|
72
|
+
Neo4j::Config[:storage_path] = '/Volumes/ramdisk'
|
73
|
+
|
74
|
+
#in rspec
|
75
|
+
before do
|
76
|
+
geoff = Geoff(Company) do
|
77
|
+
company 'Acme' do
|
78
|
+
address "13 Something Road"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#Don't display output, and delete existing database
|
83
|
+
Geoff::Importer.import geoff, silent: false, delete: true
|
84
|
+
end
|
85
|
+
|
86
|
+
|
41
87
|
```
|
42
88
|
(ROOT)-[:Company]->(Company)
|
43
89
|
(ROOT)-[:Person]->(Person)
|
@@ -122,7 +168,7 @@ end
|
|
122
168
|
```
|
123
169
|
|
124
170
|
|
125
|
-
#Using the outer
|
171
|
+
#Using the outer scope
|
126
172
|
```ruby
|
127
173
|
@hours = SomeFancyAttributeParser.parse <<-EOF
|
128
174
|
Monday 09:00-15:00
|
@@ -131,7 +177,7 @@ Saturday 09:00-16:00
|
|
131
177
|
Sunday closed
|
132
178
|
EOF
|
133
179
|
|
134
|
-
Geoff(Company,
|
180
|
+
Geoff(Company, target: self) do
|
135
181
|
company 'Amazon' do
|
136
182
|
opening_hours ->{ @hours }
|
137
183
|
end
|
data/lib/geoff.rb
CHANGED
data/lib/geoff/children_dsl.rb
CHANGED
@@ -1,19 +1,23 @@
|
|
1
1
|
require 'geoff/node_dsl'
|
2
2
|
require 'geoff/container'
|
3
|
+
require 'geoff/dsl_exception_handling'
|
3
4
|
require 'active_support/core_ext/string'
|
4
5
|
|
5
6
|
class ChildrenDsl
|
7
|
+
include DslExceptionHandling
|
8
|
+
|
6
9
|
def initialize options, &block
|
7
10
|
@parent_node_name = options[:parent_node_name]
|
8
11
|
@parent_rel_type = options[:type]
|
9
|
-
@
|
12
|
+
@target = options[:target]
|
10
13
|
@outer_geoff = options[:geoff]
|
11
14
|
|
12
15
|
@write_mode = false
|
13
16
|
|
14
17
|
@node_dsls = {}
|
15
18
|
@container = options[:container] || Container.new
|
16
|
-
|
19
|
+
|
20
|
+
eval_with_exceptions(&block)
|
17
21
|
end
|
18
22
|
|
19
23
|
def to_geoff
|
@@ -51,14 +55,6 @@ class ChildrenDsl
|
|
51
55
|
|
52
56
|
private
|
53
57
|
|
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
58
|
# e.g.
|
63
59
|
#company "ACME" do
|
64
60
|
# children "works_at" do
|
@@ -72,7 +68,7 @@ class ChildrenDsl
|
|
72
68
|
return super unless @write_mode
|
73
69
|
relationship, options = parse_method_missing m, *args
|
74
70
|
|
75
|
-
options.merge!(
|
71
|
+
options.merge!(target: @target)
|
76
72
|
NodeDsl.new(options, &blk).tap do |n|
|
77
73
|
@node_dsls[n] = relationship
|
78
74
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module DslExceptionHandling
|
2
|
+
#prevent very confusing method_missing calls during debugging by
|
3
|
+
#only writing to the object when expected
|
4
|
+
def write_mode
|
5
|
+
@write_mode = true
|
6
|
+
yield
|
7
|
+
@write_mode = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def eval_with_exceptions &block
|
11
|
+
write_mode do
|
12
|
+
begin
|
13
|
+
instance_eval(&block)
|
14
|
+
|
15
|
+
rescue Exception => e
|
16
|
+
#don't swallow exceptions further down the tree
|
17
|
+
raise e if e.is_a? Geoff::DslSyntaxError
|
18
|
+
|
19
|
+
lines = caller.select{|c| (c !~ /geoff\/lib\/geoff/)and (c !~ /ruby-debug/) }
|
20
|
+
|
21
|
+
file, number, method = lines.first.split ":"
|
22
|
+
message = "Syntax Error in #{method} in Geoff DSL block for:\n#{file}:#{number}\n"
|
23
|
+
|
24
|
+
message << lines_around(file, number) do |m|
|
25
|
+
"\n\n#{e.message}\n\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
raise Geoff::DslSyntaxError, message
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def lines_around file, number, context = 10
|
35
|
+
number = number.to_i
|
36
|
+
range = (number - context) .. (number + context)
|
37
|
+
lines = File.read(file).split("\n")
|
38
|
+
|
39
|
+
filtered_lines = range.inject({}) do |l, i|
|
40
|
+
l[i] = lines[i]
|
41
|
+
l
|
42
|
+
end
|
43
|
+
|
44
|
+
filtered_lines.map do |line_number, line|
|
45
|
+
if line_number == number
|
46
|
+
yield + ">#{line_number} #{line}"
|
47
|
+
else
|
48
|
+
" #{line_number} #{line}"
|
49
|
+
end
|
50
|
+
end.join "\n"
|
51
|
+
end
|
52
|
+
end
|
data/lib/geoff/importer.rb
CHANGED
@@ -3,12 +3,19 @@ require 'fileutils'
|
|
3
3
|
Dir["lib/jars/*"].each {|file| require file }
|
4
4
|
|
5
5
|
module Geoff
|
6
|
-
class
|
6
|
+
class Error < StandardError ; end
|
7
|
+
class MissingIndexedProperty < Geoff::Error ; end
|
8
|
+
class InvalidRules < Geoff::Error ; end
|
9
|
+
|
10
|
+
def self.import *args
|
11
|
+
Importer.import *args
|
12
|
+
end
|
7
13
|
|
8
14
|
class Importer
|
9
|
-
|
15
|
+
Subgraph = org.neo4j.geoff.Subgraph
|
10
16
|
|
11
|
-
|
17
|
+
class << self
|
18
|
+
def import_files(*files, options)
|
12
19
|
concatenated = files.map{|f|File.read f}.join "\n"
|
13
20
|
|
14
21
|
import concatenated, options
|
@@ -20,10 +27,6 @@ module Geoff
|
|
20
27
|
end
|
21
28
|
|
22
29
|
def import(geoff, options = {})
|
23
|
-
db_path = options[:test] ? 'db/test' : 'db/development'
|
24
|
-
|
25
|
-
Neo4j::Config[:storage_path] = db_path
|
26
|
-
|
27
30
|
geoff = geoff.to_geoff unless geoff.is_a? String
|
28
31
|
|
29
32
|
geoff = geoff.split "\n" if geoff.is_a? String
|
@@ -39,7 +42,7 @@ module Geoff
|
|
39
42
|
end
|
40
43
|
|
41
44
|
def go
|
42
|
-
raise 'Invalid rules' unless validate_rules(@rules)
|
45
|
+
raise Geoff::InvalidRules.new('Invalid rules') unless validate_rules(@rules)
|
43
46
|
delete_database if drop?
|
44
47
|
|
45
48
|
log 'importing the database'
|
@@ -68,7 +71,7 @@ module Geoff
|
|
68
71
|
end
|
69
72
|
|
70
73
|
def import_geoff_rules(rules)
|
71
|
-
sub_graph =
|
74
|
+
sub_graph = Subgraph.new(rules)
|
72
75
|
node_map = { '0' => root_node }
|
73
76
|
org.neo4j.geoff.Geoff.insertIntoNeo4j(sub_graph, Neo4j.db.graph, node_map );
|
74
77
|
end
|
@@ -101,12 +104,12 @@ module Geoff
|
|
101
104
|
|
102
105
|
rescue NativeException => e
|
103
106
|
log "-" * 80
|
104
|
-
message = "
|
107
|
+
message = "#{type} missing indexed attribute #{@current_index}, on node: #{@current_node.attributes}"
|
105
108
|
log message
|
106
109
|
log "-" * 80
|
107
110
|
log e.message
|
108
111
|
log "-" * 80
|
109
|
-
|
112
|
+
raise Geoff::MissingIndexedProperty, message
|
110
113
|
end
|
111
114
|
|
112
115
|
def tx
|
@@ -138,16 +141,19 @@ module Geoff
|
|
138
141
|
end
|
139
142
|
|
140
143
|
def validate_rules(rules)
|
141
|
-
invalid = Array(rules).
|
142
|
-
|
143
|
-
|
144
|
+
@invalid = Array(rules).map do |r|
|
145
|
+
validate_rule(r)
|
146
|
+
end.compact
|
147
|
+
|
148
|
+
@invalid.each { |r, message| log "Rule '#{r}' is invalid: #{message}" }
|
149
|
+
@invalid.empty?
|
144
150
|
end
|
145
151
|
|
146
152
|
def validate_rule(rule)
|
147
|
-
|
148
|
-
|
149
|
-
rescue
|
150
|
-
|
153
|
+
Subgraph.new(Array(rule))
|
154
|
+
nil
|
155
|
+
rescue Exception => e
|
156
|
+
return [rule, e.message]
|
151
157
|
end
|
152
158
|
|
153
159
|
end
|
data/lib/geoff/node_dsl.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'geoff/children_dsl'
|
2
|
+
require 'geoff/dsl_exception_handling'
|
2
3
|
|
3
4
|
class NodeDsl
|
5
|
+
include DslExceptionHandling
|
6
|
+
|
4
7
|
attr_reader :node_name
|
5
8
|
|
6
9
|
def initialize(options, &block)
|
@@ -8,19 +11,20 @@ class NodeDsl
|
|
8
11
|
@klass_name = options[:klass_name] || 'ROOT'
|
9
12
|
@container = options[:container] || Container.new
|
10
13
|
@rel_type = options[:rel_type]
|
11
|
-
@
|
14
|
+
@target = options[:target]
|
12
15
|
@properties = {}
|
13
16
|
@children_dsls = []
|
14
17
|
|
15
18
|
@rendered = false
|
16
19
|
|
17
|
-
|
20
|
+
eval_with_exceptions(&block) if block_given?
|
18
21
|
end
|
19
22
|
|
20
23
|
def to_geoff
|
21
24
|
geoff_lines.join "\n"
|
22
25
|
end
|
23
26
|
|
27
|
+
|
24
28
|
def geoff_lines
|
25
29
|
#we need this to prevent rendering same node which is rendered already
|
26
30
|
return [] if @rendered
|
@@ -59,14 +63,6 @@ class NodeDsl
|
|
59
63
|
end
|
60
64
|
end
|
61
65
|
|
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
66
|
# e.g.
|
71
67
|
#sandwich "BLT", type: :on_menu do
|
72
68
|
# filling 'bacon'
|
@@ -80,7 +76,7 @@ class NodeDsl
|
|
80
76
|
else
|
81
77
|
return super unless @write_mode
|
82
78
|
val = if args.first.is_a? Proc
|
83
|
-
|
79
|
+
@target.instance_exec &args.first
|
84
80
|
else
|
85
81
|
args.first
|
86
82
|
end
|
@@ -94,13 +90,13 @@ class NodeDsl
|
|
94
90
|
|
95
91
|
private
|
96
92
|
|
97
|
-
def children type=nil, &block
|
98
|
-
options
|
93
|
+
def children type=nil, options={}, &block
|
94
|
+
options.merge!({
|
99
95
|
parent_node_name: @node_name,
|
100
96
|
type: type,
|
101
97
|
container: @container,
|
102
|
-
|
103
|
-
}
|
98
|
+
target: @target
|
99
|
+
})
|
104
100
|
|
105
101
|
@children_dsls << ChildrenDsl.new(options, &block)
|
106
102
|
end
|
data/lib/geoff/version.rb
CHANGED
@@ -38,9 +38,9 @@ describe ChildrenDsl do
|
|
38
38
|
let(:top) {ChildrenDsl.new(parent_node_name: 'starbucks', type: :x ){area 'Luton' } }
|
39
39
|
let(:on_node){ChildrenDsl.new(parent_node_name: 'starbucks') {area 'Luton', type: :x }}
|
40
40
|
|
41
|
-
specify { ->{missing }.should raise_error
|
42
|
-
specify { ->{ top }.should_not raise_error
|
43
|
-
specify { ->{ on_node}.should_not raise_error
|
41
|
+
specify { ->{missing }.should raise_error Geoff::DslSyntaxError}
|
42
|
+
specify { ->{ top }.should_not raise_error }
|
43
|
+
specify { ->{ on_node}.should_not raise_error }
|
44
44
|
end
|
45
45
|
|
46
46
|
describe 'with special node' do
|
@@ -58,20 +58,20 @@ describe NodeDsl do
|
|
58
58
|
strip_whitespace <<-EOS
|
59
59
|
(starbucks) {"_classname":"Cafe"}
|
60
60
|
(Cafe)-[:all]->(starbucks)
|
61
|
-
(starbucks_branch_1) {"_classname":"Branch","delay_time":15,"
|
61
|
+
(starbucks_branch_1) {"_classname":"Branch","delay_time":15,"opening_hours":3}
|
62
62
|
(Branch)-[:all]->(starbucks_branch_1)
|
63
63
|
(starbucks)-[:owns]->(starbucks_branch_1) {"some":"property"}
|
64
64
|
EOS
|
65
65
|
end
|
66
66
|
|
67
67
|
specify do
|
68
|
-
@hours =
|
68
|
+
@hours = 3
|
69
69
|
|
70
|
-
node = NodeDsl.new(node_name: 'starbucks', klass_name: 'Cafe',
|
70
|
+
node = NodeDsl.new(node_name: 'starbucks', klass_name: 'Cafe', target: self) do
|
71
71
|
children do
|
72
72
|
branch 'starbucks_branch_1', type: 'owns', some: 'property' do
|
73
73
|
delay_time 15
|
74
|
-
|
74
|
+
opening_hours ->{@hours}
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: geoff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: 6
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.3.beta
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- David Rouchy
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2012-08-
|
16
|
+
date: 2012-08-15 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: neo4j
|
@@ -123,6 +123,7 @@ extensions: []
|
|
123
123
|
extra_rdoc_files: []
|
124
124
|
files:
|
125
125
|
- .gitignore
|
126
|
+
- .rvmrc
|
126
127
|
- Gemfile
|
127
128
|
- Gemfile.lock
|
128
129
|
- LICENSE
|
@@ -132,6 +133,7 @@ files:
|
|
132
133
|
- lib/geoff.rb
|
133
134
|
- lib/geoff/children_dsl.rb
|
134
135
|
- lib/geoff/container.rb
|
136
|
+
- lib/geoff/dsl_exception_handling.rb
|
135
137
|
- lib/geoff/importer.rb
|
136
138
|
- lib/geoff/neo4j_wrapper_dsl.rb
|
137
139
|
- lib/geoff/neo4j_wrapper_validator.rb
|