neography-batch 1.0.0

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 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: []