neography-batch 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .idea
2
+ .rvmrc
3
+ neo4j
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in neography-batch.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ neography-batch (1.0.0)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.4)
10
+ httpclient (2.3.3)
11
+ json (1.8.0)
12
+ multi_json (1.7.7)
13
+ neography (1.0.10)
14
+ httpclient (>= 2.3.3)
15
+ json (>= 1.7.7)
16
+ multi_json (>= 1.3.2)
17
+ os (>= 0.9.6)
18
+ rubyzip (>= 0.9.7)
19
+ os (0.9.6)
20
+ rake (10.0.4)
21
+ rspec (2.13.0)
22
+ rspec-core (~> 2.13.0)
23
+ rspec-expectations (~> 2.13.0)
24
+ rspec-mocks (~> 2.13.0)
25
+ rspec-core (2.13.1)
26
+ rspec-expectations (2.13.0)
27
+ diff-lcs (>= 1.1.3, < 2.0)
28
+ rspec-mocks (2.13.1)
29
+ rubyzip (0.9.9)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ neography (>= 1.0.6)
36
+ neography-batch!
37
+ rake (>= 0.8.7)
38
+ rspec (>= 2.11)
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # neography-batch
2
+
3
+ ## Introduction
4
+ makes neography-batches better composable
5
+
6
+ TODO: writing something nice here
7
+
8
+ ## Installation
9
+ TODO: Test & Describe installation
10
+
11
+ ## Usage
12
+ TODO: Describe usage
13
+
14
+ # Running tests
15
+ A few tests run against a neo4j-instance. This instance can be installed and run using following rake commands:
16
+ ```sh
17
+ rake neo4j:install # Install Neo4j to the neo4j directory under your project
18
+ rake neo4j:start # Start Neo4j
19
+ rake neo4j:stop # Stop Neo4j
20
+ ```
21
+
22
+ For a complete documentation see [neography's rake tasks](https://github.com/maxdemarzi/neography/wiki/Rake-tasks "Rake tasks")
23
+
24
+ # License
25
+ TODO: Think hard about it
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ require 'neography/tasks'
6
+
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.rspec_opts = "--color"
9
+ t.pattern = "spec/**/*_spec.rb"
10
+ end
11
+
12
+ desc "Run Tests"
13
+ task :default => :spec
@@ -0,0 +1,3 @@
1
+ require 'neography'
2
+ require_relative '../lib/neography-batch/batch'
3
+ require_relative '../lib/neography-batch/batch_reference'
@@ -0,0 +1,95 @@
1
+ class Batch
2
+ private
3
+ def initialize(graph_db = Neography::Rest.new, commands = [])
4
+ @commands = commands
5
+ @db = graph_db
6
+ yield(self) if block_given?
7
+
8
+ end
9
+
10
+ def self.get_resolved_commands(commands)
11
+ result = []
12
+ reference_and_index = {}
13
+
14
+ # write each command_or_batch and remember index of command_or_batch
15
+ commands.each do |command|
16
+ index = result.length
17
+ reference_and_index[command[:reference]] = index
18
+ result << command[:cmd]
19
+ end
20
+
21
+ # replace references contained in the commands with the effective index in the batch
22
+ result.each do |command|
23
+ command.each_with_index do |element, index|
24
+ if reference_and_index.has_key?(element)
25
+ command[index] = "{#{reference_and_index[element]}}"
26
+ end
27
+ end
28
+ end
29
+ result
30
+ end
31
+
32
+ protected
33
+ def commands
34
+ @commands
35
+ end
36
+
37
+ public
38
+ def self.unit
39
+ Batch.new
40
+ end
41
+
42
+ def add(command_or_batch)
43
+ if command_or_batch.class == Batch
44
+ bind(command_or_batch)
45
+ else
46
+ unless command_or_batch.respond_to?(:each)
47
+ raise StandardError, "command_or_batch must respond to :each"
48
+ end
49
+
50
+ reference = BatchReference.new(command_or_batch)
51
+ @commands << {:cmd => command_or_batch, :reference => reference}
52
+ reference
53
+ end
54
+ end
55
+
56
+ def find_reference
57
+ @commands.select { |c| !block_given? || yield(c[:cmd]) }.map { |c| c[:reference] }
58
+ end
59
+
60
+ alias :<< :add
61
+
62
+ def submit
63
+ return [] if @commands.empty?
64
+ command_list = Batch.get_resolved_commands(@commands)
65
+ result = @db.batch(*command_list)
66
+ if result.nil?
67
+ raise StandardError, "batch returned no result (nil)"
68
+ end
69
+ batch_errors = result.select { |r| r["status"] >= 400 }
70
+ if batch_errors.count > 0
71
+ raise StandardError, "batch returned one or more errors: #{batch_errors.map { |e| e["message"] }.join("|")}"
72
+ end
73
+ results = result.map { |r| r["body"] }
74
+ @commands.each_with_index { |c, i| c[:reference].notify_after_submit(results[i]) }
75
+ @commands.clear()
76
+ results
77
+ end
78
+
79
+ def bind(batch)
80
+ Batch.new(@db, @commands.concat(batch.commands))
81
+ end
82
+
83
+ def ==(other)
84
+ eql?(other)
85
+ end
86
+
87
+ def eql?(other)
88
+ if other.class != self.class
89
+ false
90
+ else
91
+ commands.length == other.commands.length &&
92
+ commands.zip(other.commands).all? { |z| z[0] == z[1] }
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,31 @@
1
+ class BatchReference
2
+ def initialize(command)
3
+ @command = command
4
+ end
5
+
6
+ protected
7
+ def after_submit_action
8
+ @after_submit_action
9
+ end
10
+
11
+ def command
12
+ @command
13
+ end
14
+
15
+ public
16
+ def after_submit(&after_commit_action)
17
+ @after_submit_action = after_commit_action
18
+ end
19
+
20
+ def notify_after_submit(result)
21
+ unless @after_submit_action.nil?
22
+ @after_submit_action.call(result)
23
+ end
24
+ end
25
+
26
+ def ==(other)
27
+ return false if other.nil?
28
+ @after_submit_action.equal?(other.after_submit_action) &&
29
+ @command.equal?(other.command)
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "neography-batch"
6
+ s.version = "1.0.0"
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = "Jorg Jenni"
9
+ s.email = "jorg.jenni@jennius.co.uk"
10
+ s.homepage = "https://github.com/Enceradeira/neography-batch"
11
+ s.summary = "Composable neography-batches"
12
+ s.description = "Makes neography-batches better composable (for neography-batches see https://github.com/maxdemarzi/neography/wiki/Batch)"
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_development_dependency "neography", ">= 1.0.6"
20
+ s.add_development_dependency "rspec", ">= 2.11"
21
+ s.add_development_dependency "rake", ">= 0.8.7"
22
+ end
@@ -0,0 +1,321 @@
1
+ require_relative 'spec_helper'
2
+
3
+ class Function
4
+ def self.persist_inserts(elements)
5
+ batch = Batch.new
6
+ elements.each do |e|
7
+ batch << [:create_node, e]
8
+ end
9
+ return batch
10
+ end
11
+ end
12
+
13
+ def create_batches(command1, command2, command3)
14
+ batch1 = Batch.new do |b|
15
+ b<<command1
16
+ end
17
+
18
+ batch2 = Batch.new do |b|
19
+ b<<command2
20
+ end
21
+
22
+ batch3 = Batch.new do |b|
23
+ b<<command3
24
+ end
25
+ return batch1, batch2, batch3
26
+ end
27
+
28
+ describe "Batch" do
29
+ let(:db) { Neography::Rest.new }
30
+ subject { Batch.new(db) }
31
+
32
+ describe "new" do
33
+ it "should create unit when no block provided" do
34
+ Batch.new.should == Batch.unit()
35
+ end
36
+ it "should call block with itself when block is provided" do
37
+ command = [:command]
38
+ batch_configured_without_block = Batch.new
39
+ batch_configured_without_block << command
40
+
41
+ batch_with_block = Batch.new do |b|
42
+ b << command
43
+ end
44
+
45
+ batch_with_block.should == batch_configured_without_block
46
+ end
47
+ end
48
+
49
+ describe "==" do
50
+ specify { (Batch.new==Batch.new).should be_true }
51
+ specify { (Batch.new==[]).should be_false }
52
+ specify do
53
+ command1 = [:command_1]
54
+ command2 = [:command_2]
55
+ batch1 = Batch.new do |b|
56
+ b << command1
57
+ b << command2
58
+ end
59
+ batch2 = Batch.new do |b|
60
+ b << command1
61
+ b << command2
62
+ end
63
+ (batch1==batch2).should be_true
64
+ end
65
+ specify do
66
+ batch1 = Batch.new do |b|
67
+ b << [:command_1]
68
+ b << [:command_2]
69
+ end
70
+ batch2 = Batch.new do |b|
71
+ b << [:command_2]
72
+ b << [:command_1]
73
+ end
74
+ (batch1==batch2).should be_false
75
+ end
76
+ specify do
77
+ batch1 = Batch.new do |b|
78
+ b << [:command_1, {:id => 7}]
79
+ b << [:command_2, {:id => 9}]
80
+ end
81
+ batch2 = Batch.new do |b|
82
+ b << [:command_1, {:id => 7}]
83
+ b << [:command_2, {:id => 8}]
84
+ end
85
+ (batch1==batch2).should be_false
86
+ end
87
+ specify do
88
+ batch1 = Batch.new { |b| b << [:command_1] }
89
+ batch2 = Batch.new { |b| b << [:command_2] }
90
+ (batch1==batch2).should be_false
91
+ end
92
+ specify do
93
+ batch1 = Batch.new { |b| b << [] }
94
+ batch2 = Batch.new { |b| b << [:command_2] }
95
+ (batch1==batch2).should be_false
96
+ end
97
+ specify do
98
+ batch1 = Batch.new
99
+ batch2 = Batch.new { |b| b << [:command_2] }
100
+ (batch1==batch2).should be_false
101
+ end
102
+ specify do
103
+ batch1 = Batch.new { |b| b << [:command_1] }
104
+ batch2 = Batch.new { |b| b << [] }
105
+ (batch1==batch2).should be_false
106
+ end
107
+ end
108
+
109
+ describe "bind" do
110
+ let(:command1) { [:create_node, {"id" => 7}] }
111
+ let(:command2) { [:create_unique_node, {"id" => 8}] }
112
+ let(:command3) { [:create_node, {"id" => 9}] }
113
+ it "on unit with f should be equal f" do
114
+ batch1, _, _ = create_batches(command1, command2, command3)
115
+
116
+ Batch.unit().bind(batch1).should == batch1
117
+ end
118
+
119
+ it "with unit should be equal Batch" do
120
+ batch1, _, _ = create_batches(command1, command2, command3)
121
+
122
+ batch1.bind(Batch.unit()).should == batch1
123
+ end
124
+
125
+ it "chained should be equal to bind with inner binds" do
126
+ batch1, batch2, batch3 = create_batches(command1, command2, command3)
127
+ result1 = batch3.bind(batch1).bind(batch2)
128
+
129
+ batch1, batch2, batch3 = create_batches(command1, command2, command3)
130
+ f = ->() { batch1 }
131
+ g = ->() { batch2 }
132
+ result2 = batch3.bind(batch1.bind(batch2))
133
+
134
+ result1.should == result2
135
+ end
136
+
137
+ describe "and submit" do
138
+ it "should combine separate batches into one batch" do
139
+ batch1 = Batch.new(db)
140
+ ref11 = batch1 << [:create_node, {"id" => 1}]
141
+ ref12 = batch1 << [:create_node, {"id" => 2}]
142
+ batch1 << [:create_relationship, ref11, ref12, {}]
143
+
144
+ batch2 = Batch.new(db)
145
+ ref21 = batch2 << [:create_node, {"id" => 3}]
146
+ ref22 = batch2 << [:create_node, {"id" => 4}]
147
+ batch2 << [:create_relationship, ref21, ref22, {}]
148
+
149
+ batch = batch2.bind(batch1)
150
+
151
+ db.should_receive(:batch).with do |*commands|
152
+ commands[0].should == [:create_node, {"id" => 3}]
153
+ commands[1].should == [:create_node, {"id" => 4}]
154
+ commands[2].should == [:create_relationship, "{0}", "{1}", {}]
155
+ commands[3].should == [:create_node, {"id" => 1}]
156
+ commands[4].should == [:create_node, {"id" => 2}]
157
+ commands[5].should == [:create_relationship, "{3}", "{4}", {}]
158
+ end.and_return([])
159
+
160
+ batch.submit()
161
+ end
162
+ end
163
+ end
164
+ describe "find_reference" do
165
+ it "should return reference when command is found" do
166
+ ref1 = subject << [:create_node, {"id" => 7}]
167
+ ref2 = subject << [:create_node, {"id" => 8}]
168
+ subject << [:create_relationship, ref2, ref1, {}]
169
+
170
+ result = subject.find_reference { |c| c[0] == :create_node && c[1]["id"] == 8 }
171
+ result.should have(1).item
172
+ result.should include(ref2)
173
+ end
174
+
175
+ it "should return empty when command is not found" do
176
+ result = subject.find_reference { |c| c[0] == :create_node && c[1]["id"] == 8 }
177
+ result.should be_empty
178
+ end
179
+
180
+ it "should return all reference when no predicate specified" do
181
+ ref1 = subject << [:create_node, {"id" => 7}]
182
+ ref2 = subject << [:create_node, {"id" => 8}]
183
+
184
+ result = subject.find_reference()
185
+ result.should have(2).item
186
+ end
187
+ end
188
+ describe "<<" do
189
+ it "should be the same as add when command is argument" do
190
+ command = [:create_node]
191
+ batch_used_with_add = Batch.new
192
+ batch_used_with_add.add command
193
+
194
+ subject << command
195
+
196
+ subject.should == batch_used_with_add
197
+ end
198
+ it "should be the same as bind when command is Batch" do
199
+ another_batch = Batch.new do |b|
200
+ b << [:create_node]
201
+ end
202
+ subject << [:create_node]
203
+
204
+ subject << another_batch
205
+
206
+ db.should_receive(:batch) do |*commands|
207
+ commands.should have(2).items
208
+ end.and_return([])
209
+ subject.submit()
210
+ end
211
+ end
212
+ describe "add and submit" do
213
+ it "should raise exception when added element doesn't respond to :each" do
214
+ -> { subject.add(1) }.should raise_exception(StandardError)
215
+ end
216
+
217
+ it "should add command to be submitted later" do
218
+ command = [:create_node]
219
+ subject.add(command)
220
+
221
+ db.should_receive(:batch).with(command).and_return([])
222
+
223
+ subject.submit()
224
+ end
225
+
226
+ it "should resolve references to preceding commands" do
227
+ jim = subject << [:create_node]
228
+ john = subject << [:create_node]
229
+ subject << [:create_relationship, john, jim]
230
+
231
+ db.should_receive(:batch).with do |*commands|
232
+ relationship_cmd = commands.last
233
+ relationship_cmd[1].should == "{1}" # john was resolved to index where john was created
234
+ relationship_cmd[2].should == "{0}" # jim was resolved to index where john was created
235
+ end.and_return([])
236
+
237
+ subject.submit()
238
+ end
239
+
240
+ context "when list 2 commands" do
241
+ before do
242
+ @john = subject.add [:create_node]
243
+ @markus = subject.add [:create_node]
244
+ end
245
+ context "and another command is added" do
246
+ let(:another_command) { [:create_relationship] }
247
+ before do
248
+ subject.add(another_command)
249
+ end
250
+ it "should contain 3 commands" do
251
+ db.should_receive(:batch).with do |*commands|
252
+ commands.should have(3).items
253
+ end.and_return([])
254
+
255
+ subject.submit()
256
+ end
257
+ it "should append command" do
258
+ db.should_receive(:batch).with do |*commands|
259
+ commands.last.should == another_command
260
+ end.and_return([])
261
+
262
+ subject.submit()
263
+ end
264
+ end
265
+ end
266
+ end
267
+ describe "submit" do
268
+ it "should raise exception when server returns error" do
269
+ # this provokes a server error, because the second create_unique_node cannot be reference in create_relationship
270
+ subject << [:create_unique_node, "test", "id", "1", {}]
271
+ subject << [:create_unique_node, "test", "id", "2", {}]
272
+ subject << [:create_relationship, "test", "{0}", "{1}", {}]
273
+
274
+ ->() { subject.submit() }.should raise_error(StandardError)
275
+ end
276
+ it "should notify reference handler" do
277
+ node_1_result = nil
278
+ node_2_result = nil
279
+ ref1 = subject << [:create_node, {"id" => 1}]
280
+ ref2 = subject << [:create_node, {"id" => 2}]
281
+ ref1.after_submit do |node|
282
+ node_1_result = node
283
+ end
284
+ ref2.after_submit do |node|
285
+ node_2_result = node
286
+ end
287
+
288
+ subject.submit()
289
+
290
+ node_1_result.should_not be_nil
291
+ node_2_result.should_not be_nil
292
+ node_1_result["data"]["id"].should == 1
293
+ node_2_result["data"]["id"].should == 2
294
+ end
295
+
296
+ it "should reset state of batch" do
297
+ subject << [:create_node, {"id" => 1}]
298
+ subject.submit()
299
+
300
+ db.should_not_receive(:batch)
301
+ result = subject.submit()
302
+ result.should be_empty
303
+ end
304
+
305
+ context "one create_node added" do
306
+ before { subject << [:create_node, {}] }
307
+ it "should return created node" do
308
+ result = subject.submit()
309
+ result.should have(1).items
310
+ end
311
+ it "should create-node on Db" do
312
+ result = subject.submit()
313
+
314
+ node = result.first
315
+ node_id = db.get_id(node)
316
+ count = db.execute_query("start s=node(#{node_id}) return count(*)")
317
+ count["data"][0][0].should == 1
318
+ end
319
+ end
320
+ end
321
+ end
@@ -0,0 +1 @@
1
+ require_relative '../lib/neography-batch'
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: neography-batch
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jorg Jenni
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: neography
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.6
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.6
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '2.11'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '2.11'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 0.8.7
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.8.7
62
+ description: Makes neography-batches better composable (for neography-batches see
63
+ https://github.com/maxdemarzi/neography/wiki/Batch)
64
+ email: jorg.jenni@jennius.co.uk
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - Gemfile.lock
72
+ - README.md
73
+ - Rakefile
74
+ - lib/neography-batch.rb
75
+ - lib/neography-batch/batch.rb
76
+ - lib/neography-batch/batch_reference.rb
77
+ - neography-batch.gemspec
78
+ - spec/batch_spec.rb
79
+ - spec/spec_helper.rb
80
+ homepage: https://github.com/Enceradeira/neography-batch
81
+ licenses: []
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 1.8.24
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Composable neography-batches
104
+ test_files: []