geoff 0.0.3.beta → 0.0.4

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.
@@ -4,8 +4,6 @@ require 'geoff/dsl_exception_handling'
4
4
  class NodeDsl
5
5
  include DslExceptionHandling
6
6
 
7
- attr_reader :node_name
8
-
9
7
  def initialize(options, &block)
10
8
  @node_name = options[:node_name] || 'root'
11
9
  @klass_name = options[:klass_name] || 'ROOT'
@@ -43,11 +41,11 @@ class NodeDsl
43
41
  end
44
42
 
45
43
  def all_rule_to_geoff
46
- "(#{@klass_name})-[:all]->(#{@node_name})"
44
+ "(#{@klass_name})-[:all]->(#{node_name})"
47
45
  end
48
46
 
49
47
  def self_to_geoff
50
- "(#{@node_name}) #{properties.to_json}"
48
+ "(#{node_name}) #{properties.to_json}"
51
49
  end
52
50
 
53
51
  #refactor out wrapper specific stuff later
@@ -88,14 +86,37 @@ class NodeDsl
88
86
  to_geoff
89
87
  end
90
88
 
89
+ def clone
90
+ c = self.class.new(
91
+ node_name: @node_name,
92
+ klass_name: @klass_name,
93
+ container: @container,
94
+ rel_type: @rel_type,
95
+ target: @target
96
+ )
97
+
98
+ # sort it out, instance_variable_set is probably not the best way to clone object
99
+ c.instance_variable_set('@properties', @properties)
100
+
101
+ children_dsls = @children_dsls.map &:clone
102
+ children_dsls.each {|children_dsl| children_dsl.parent_node_dsl = c}
103
+
104
+ c.instance_variable_set('@children_dsls', children_dsls)
105
+ c
106
+ end
107
+
108
+ def node_name
109
+ [@node_name, object_id].compact.join '_'
110
+ end
111
+
91
112
  private
92
113
 
93
114
  def children type=nil, options={}, &block
94
115
  options.merge!({
95
- parent_node_name: @node_name,
96
- type: type,
97
- container: @container,
98
- target: @target
116
+ parent_node_dsl: self,
117
+ type: type,
118
+ container: @container,
119
+ target: @target
99
120
  })
100
121
 
101
122
  @children_dsls << ChildrenDsl.new(options, &block)
data/lib/geoff/version.rb CHANGED
@@ -1,3 +1,3 @@
1
- module Geoff
2
- VERSION = "0.0.3.beta"
1
+ class Geoff
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/geoff.rb CHANGED
@@ -1,13 +1,93 @@
1
1
  require "json"
2
2
  require "geoff/version"
3
3
  require 'geoff/node_dsl'
4
- require 'geoff/neo4j_wrapper_dsl'
5
4
  require 'geoff/importer'
6
-
7
- def Geoff *class_list, options, &block
8
- Neo4jWrapperDsl.new(*class_list, options, &block)
9
- end
5
+ require 'geoff/children_dsl'
10
6
 
11
7
  class Geoff::Error < StandardError; end
12
8
  class Geoff::MissingNodeDefinition < Geoff::Error ; end
9
+ class Geoff::ContainerLabelMissing < Geoff::Error ; end
13
10
  class Geoff::DslSyntaxError < Geoff::Error; end
11
+
12
+
13
+ class Geoff
14
+ attr_reader :children_dsl, :container
15
+
16
+ def initialize *classes_and_builders, &block
17
+ options = classes_and_builders.last.is_a?(Hash) ? classes_and_builders.pop : {}
18
+
19
+ grouped = classes_and_builders.group_by &:class
20
+ classes = grouped[Class] || {}
21
+ builders = grouped[Geoff] || {}
22
+
23
+ validate grouped
24
+
25
+ @container = Container.new
26
+
27
+ builders.each do |builder|
28
+ @container.merge builder.container
29
+ end
30
+
31
+ options.merge!({
32
+ parent_node_dsl: nil,
33
+ type: nil,
34
+ container: @container
35
+ })
36
+
37
+ @children_dsl = ChildrenDsl.new(options, &block) if block_given?
38
+ end
39
+
40
+ def to_geoff
41
+ geoff = "#{add_classes}\n"
42
+ geoff += @children_dsl.to_geoff if @children_dsl
43
+ geoff.chomp
44
+ end
45
+
46
+ def <=> other
47
+ to_geoff == other.to_geoff ? 0 : -1
48
+ end
49
+
50
+ def to_s
51
+ to_geoff
52
+ end
53
+
54
+ def inspect
55
+ "<Geoff: #{to_s}>"
56
+ end
57
+
58
+ private
59
+
60
+ def add_classes
61
+ @classes.map{|c| root_to_class_as_geoff c }.join("\n")
62
+ end
63
+
64
+ def root_to_class_as_geoff c
65
+ "(ROOT)-[:#{c}]->(#{c})"
66
+ end
67
+
68
+ def validate grouped_classes_and_builders
69
+ validate_classes grouped_classes_and_builders[Class]
70
+
71
+ unknown_argument_types = grouped_classes_and_builders.keys - [Class, Geoff]
72
+
73
+ if unknown_argument_types.any?
74
+ formatted = unknown_argument_types.map(&:to_s).join(', ')
75
+ raise ArgumentError, "Unknown argument types #{formatted}"
76
+ end
77
+ end
78
+
79
+ def validate_classes classes
80
+ @classes = classes || []
81
+
82
+ @classes.each do |c|
83
+ m = "Class #{c} should include Neo4j::NodeMixin"
84
+ raise ArgumentError, m unless c.is_a?(Class) and c.included_modules.include? Neo4j::NodeMixin
85
+ end
86
+ end
87
+ end
88
+
89
+
90
+ def Geoff *class_list, options, &block
91
+ Geoff.new(*class_list, options, &block)
92
+ end
93
+
@@ -2,9 +2,13 @@ require 'spec_helper'
2
2
  require 'geoff/children_dsl'
3
3
 
4
4
  describe ChildrenDsl do
5
+ before { stub_node_dsl_object_id }
6
+
7
+ let(:starbucks_node_dsl) { mock node_name: 'starbucks' }
8
+
5
9
  describe 'with no special nodes' do
6
10
  let(:children) do
7
- ChildrenDsl.new({parent_node_name: 'starbucks'}) do
11
+ ChildrenDsl.new({parent_node_dsl: starbucks_node_dsl}) do
8
12
  branch 'starbucks_branch_1', type: 'owns' do
9
13
  delay_time 15
10
14
  end
@@ -34,9 +38,9 @@ describe ChildrenDsl do
34
38
  end
35
39
 
36
40
  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 }}
41
+ let(:missing){ChildrenDsl.new(parent_node_dsl: starbucks_node_dsl) {area 'Luton' } }
42
+ let(:top) {ChildrenDsl.new(parent_node_dsl: starbucks_node_dsl, type: :x ){area 'Luton' } }
43
+ let(:on_node){ChildrenDsl.new(parent_node_dsl: starbucks_node_dsl) {area 'Luton', type: :x }}
40
44
 
41
45
  specify { ->{missing }.should raise_error Geoff::DslSyntaxError}
42
46
  specify { ->{ top }.should_not raise_error }
@@ -45,7 +49,7 @@ describe ChildrenDsl do
45
49
 
46
50
  describe 'with special node' do
47
51
  let(:children) do
48
- ChildrenDsl.new({parent_node_name: 'starbucks'}) do
52
+ ChildrenDsl.new({parent_node_dsl: starbucks_node_dsl}) do
49
53
  b.luton = area 'Luton', type: 'headquarters_location' do
50
54
  name 'LU1'
51
55
  end
@@ -0,0 +1,171 @@
1
+ require File.expand_path('spec/spec_helper')
2
+ require 'neo4j'
3
+ require 'geoff'
4
+
5
+ describe Geoff do
6
+ class Branch; include Neo4j::NodeMixin; end
7
+ class CoffeeMachine; include Neo4j::NodeMixin; end
8
+ class Grinder; include Neo4j::NodeMixin; end
9
+ class Table; include Neo4j::NodeMixin; end
10
+
11
+ describe 'injecting builders' do
12
+ context 'with object_id ignored' do
13
+ before { stub_node_dsl_object_id }
14
+
15
+ let(:builder) do
16
+ Geoff(
17
+ Branch,
18
+ coffee_machines_builder,
19
+ tables_builder
20
+ ) do
21
+ branch 'starbucks_luton_branch' do
22
+ delay_time 15
23
+
24
+ children do
25
+ b.small_coffee_machines type: 'uses', lease: '3 years'
26
+ b.tables type: 'has'
27
+ end
28
+ end
29
+
30
+ branch 'starbucks_reading_branch' do
31
+ delay_time 10
32
+
33
+ children do
34
+ b.large_coffee_machines type: 'uses', lease: '3 years'
35
+ b.tables type: 'has'
36
+ table 'octagonal_table', type: 'has' do
37
+ capacity 8
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ let(:coffee_machines_builder) do
45
+ Geoff(CoffeeMachine) do
46
+ b.large_coffee_machines = children do
47
+ coffee_machine('large_illy' ) { power 2300 }
48
+ coffee_machine('large_gaggia') { power 2600 }
49
+ end
50
+
51
+ b.small_coffee_machines = children do
52
+ coffee_machine('small_illy' ) { power 600 }
53
+ coffee_machine('small_gaggia') { power 750 }
54
+ end
55
+ end
56
+ end
57
+
58
+ let(:tables_builder) do
59
+ Geoff(Table) do
60
+ b.tables = children do
61
+ table('round_table' ) { capacity 3 }
62
+ table('square_table') { capacity 4 }
63
+ end
64
+ end
65
+ end
66
+
67
+ let(:expected_geoff) do
68
+ strip_whitespace <<-EOS
69
+ (ROOT)-[:Branch]->(Branch)
70
+ (starbucks_luton_branch) {"_classname":"Branch","delay_time":15}
71
+ (Branch)-[:all]->(starbucks_luton_branch)
72
+ (small_illy) {"_classname":"CoffeeMachine","power":600}
73
+ (CoffeeMachine)-[:all]->(small_illy)
74
+ (starbucks_luton_branch)-[:uses]->(small_illy) {"lease":"3 years"}
75
+ (small_gaggia) {"_classname":"CoffeeMachine","power":750}
76
+ (CoffeeMachine)-[:all]->(small_gaggia)
77
+ (starbucks_luton_branch)-[:uses]->(small_gaggia) {"lease":"3 years"}
78
+ (round_table) {"_classname":"Table","capacity":3}
79
+ (Table)-[:all]->(round_table)
80
+ (starbucks_luton_branch)-[:has]->(round_table)
81
+ (square_table) {"_classname":"Table","capacity":4}
82
+ (Table)-[:all]->(square_table)
83
+ (starbucks_luton_branch)-[:has]->(square_table)
84
+ (starbucks_reading_branch) {"_classname":"Branch","delay_time":10}
85
+ (Branch)-[:all]->(starbucks_reading_branch)
86
+ (large_illy) {"_classname":"CoffeeMachine","power":2300}
87
+ (CoffeeMachine)-[:all]->(large_illy)
88
+ (starbucks_reading_branch)-[:uses]->(large_illy) {"lease":"3 years"}
89
+ (large_gaggia) {"_classname":"CoffeeMachine","power":2600}
90
+ (CoffeeMachine)-[:all]->(large_gaggia)
91
+ (starbucks_reading_branch)-[:uses]->(large_gaggia) {"lease":"3 years"}
92
+ (round_table) {"_classname":"Table","capacity":3}
93
+ (Table)-[:all]->(round_table)
94
+ (starbucks_reading_branch)-[:has]->(round_table)
95
+ (square_table) {"_classname":"Table","capacity":4}
96
+ (Table)-[:all]->(square_table)
97
+ (starbucks_reading_branch)-[:has]->(square_table)
98
+ (octagonal_table) {"_classname":"Table","capacity":8}
99
+ (Table)-[:all]->(octagonal_table)
100
+ (starbucks_reading_branch)-[:has]->(octagonal_table)
101
+ EOS
102
+ end
103
+
104
+ specify do
105
+ geoff = builder.to_geoff
106
+ geoff.should == expected_geoff
107
+ end
108
+ end
109
+
110
+ context 'with object_id sequence' do
111
+ class NodeDsl
112
+ def self.object_id node_dsl
113
+ @object_ids ||= {}
114
+ @object_id ||= 0
115
+
116
+ @object_id += 1
117
+ @object_ids[node_dsl] ||= @object_id
118
+ end
119
+
120
+ def object_id
121
+ self.class.object_id self
122
+ end
123
+ end
124
+
125
+ let(:builder) do
126
+ Geoff(
127
+ Branch,
128
+ coffee_machines_builder
129
+ ) do
130
+ branch 'starbucks_luton_branch' do
131
+ children do
132
+ b.coffee_machine type: 'uses', lease: '3 years'
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ let(:coffee_machines_builder) do
139
+ Geoff(CoffeeMachine) do
140
+ b.coffee_machine = coffee_machine('illy') do
141
+ power 2300
142
+
143
+ children 'includes' do
144
+ grinder 'fine_grinder'
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ let(:expected_geoff) do
151
+ strip_whitespace <<-EOS
152
+ (ROOT)-[:Branch]->(Branch)
153
+ (starbucks_luton_branch_1) {"_classname":"Branch"}
154
+ (Branch)-[:all]->(starbucks_luton_branch_1)
155
+ (illy_3) {"_classname":"CoffeeMachine","power":2300}
156
+ (CoffeeMachine)-[:all]->(illy_3)
157
+ (fine_grinder_5) {"_classname":"Grinder"}
158
+ (Grinder)-[:all]->(fine_grinder_5)
159
+ (illy_3)-[:includes]->(fine_grinder_5)
160
+ (starbucks_luton_branch_1)-[:uses]->(illy_3) {"lease":"3 years"}
161
+ EOS
162
+ end
163
+
164
+ specify do
165
+ geoff = builder.to_geoff
166
+ geoff.should == expected_geoff
167
+ end
168
+ end
169
+
170
+ end
171
+ end
@@ -3,6 +3,8 @@ require 'geoff/node_dsl'
3
3
  require 'geoff'
4
4
 
5
5
  describe NodeDsl do
6
+ before { stub_node_dsl_object_id }
7
+
6
8
  describe 'with properties and no children' do
7
9
  let(:node) do
8
10
  NodeDsl.new(node_name: 'starbucks_branch', klass_name: 'Branch') do
@@ -23,21 +25,6 @@ describe NodeDsl do
23
25
  end
24
26
 
25
27
  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
28
  let(:expected_geoff) do
42
29
  strip_whitespace <<-EOS
43
30
  (starbucks) {"_classname":"Cafe"}
@@ -48,8 +35,47 @@ describe NodeDsl do
48
35
  EOS
49
36
  end
50
37
 
51
- specify do
52
- node.to_geoff.should == expected_geoff
38
+ context 'having properties defined at node level' do
39
+ let(:node) do
40
+
41
+ NodeDsl.new(node_name: 'starbucks', klass_name: 'Cafe') do
42
+ children do
43
+ branch 'starbucks_branch_1', type: 'owns', some: 'property' do
44
+ closing_buffer 30
45
+ delay_time 15
46
+ postcode 'LU1 1CN'
47
+ schedule 'DEFAULT_SCHEDULE'
48
+ end
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ specify do
55
+ node.to_geoff.should == expected_geoff
56
+ end
57
+ end
58
+
59
+ context 'having properties defined at children level' do
60
+ let(:node) do
61
+
62
+ NodeDsl.new(node_name: 'starbucks', klass_name: 'Cafe') do
63
+ children 'owns', some: 'property' do
64
+ branch 'starbucks_branch_1' do
65
+ closing_buffer 30
66
+ delay_time 15
67
+ postcode 'LU1 1CN'
68
+ schedule 'DEFAULT_SCHEDULE'
69
+ end
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ specify do
76
+ pending 'properties at children level get ignored'
77
+ node.to_geoff.should == expected_geoff
78
+ end
53
79
  end
54
80
  end
55
81
 
@@ -81,25 +107,6 @@ describe NodeDsl do
81
107
  end
82
108
 
83
109
  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
110
  let(:expected_geoff) do
104
111
  strip_whitespace <<-EOS
105
112
  (starbucks) {"_classname":"Cafe"}
@@ -114,12 +121,34 @@ describe NodeDsl do
114
121
  EOS
115
122
  end
116
123
 
117
- specify do
118
- geoff = node.to_geoff
119
- geoff.should == expected_geoff
124
+ context 'and relationship type at node level' do
125
+ let(:node) do
126
+ NodeDsl.new(node_name: 'starbucks', klass_name: 'Cafe') do
127
+ children do
128
+ b.luton = area 'luton', type: 'headquarters_location' do
129
+ name 'LU1'
130
+ end
131
+
132
+ branch 'starbucks_branch_1', type: 'owns', some: 'property' do
133
+ delay_time 15
134
+
135
+ children do
136
+ b.luton type: 'location'
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ specify do
144
+ geoff = node.to_geoff
145
+ geoff.should == expected_geoff
146
+ end
120
147
  end
148
+ end
121
149
 
122
- context "with relation type at the children level" do
150
+ context "with relation type at the children level" do
151
+ context 'and tree graph' do
123
152
  let(:expected_geoff) do
124
153
  strip_whitespace <<-EOS
125
154
  (starbucks) {"_classname":"Cafe"}
@@ -130,7 +159,6 @@ describe NodeDsl do
130
159
  EOS
131
160
  end
132
161
 
133
-
134
162
  specify do
135
163
  node = NodeDsl.new(node_name: 'starbucks', klass_name: 'Cafe') do
136
164
  children "location" do
@@ -142,6 +170,34 @@ describe NodeDsl do
142
170
  node.to_geoff.should == expected_geoff
143
171
  end
144
172
  end
173
+ end
145
174
 
146
- end
175
+ # Cloning nodes is needed for injecting same builder multiple times.
176
+ describe '#clone' do
177
+ let(:node) do
178
+
179
+ NodeDsl.new(node_name: 'starbucks', klass_name: 'Cafe') do
180
+ children do
181
+ branch 'starbucks_branch_1', type: 'owns', some: 'property' do
182
+ closing_buffer 30
183
+ delay_time 15
184
+ postcode 'LU1 1CN'
185
+ schedule 'DEFAULT_SCHEDULE'
186
+ end
187
+ end
188
+ end
189
+
190
+ end
191
+
192
+ specify do
193
+ geoff = node.to_geoff
194
+ geoff_of_clone = node.clone.to_geoff
195
+
196
+ geoff.should == geoff_of_clone
197
+ end
198
+
199
+ specify do
200
+ node.should_not == node.clone
201
+ end
202
+ end
147
203
  end
@@ -0,0 +1,27 @@
1
+ require File.expand_path('spec/spec_helper')
2
+ require 'geoff'
3
+ require 'geoff/container'
4
+
5
+ describe Container do
6
+ subject(:container) { Container.new }
7
+
8
+ context 'attempt to access missing key' do
9
+ specify do
10
+ lambda do
11
+ container.egg
12
+ end.should raise_error Geoff::ContainerLabelMissing
13
+ end
14
+ end
15
+
16
+ context '#merge' do
17
+ let(:other_container) do
18
+ Container.new(node_dsls: {egg: 'cheese'})
19
+ end
20
+
21
+ specify do
22
+ container.merge other_container
23
+
24
+ container.node_dsls[:egg].should == 'cheese'
25
+ end
26
+ end
27
+ end