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