activegraph 10.0.0.pre.beta.3 → 10.0.0.pre.beta.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/activegraph.gemspec +2 -2
- data/lib/active_graph/base.rb +14 -32
- data/lib/active_graph/core/instrumentable.rb +3 -2
- data/lib/active_graph/core/label.rb +8 -9
- data/lib/active_graph/core/logging.rb +1 -1
- data/lib/active_graph/core/querable.rb +7 -36
- data/lib/active_graph/core/query.rb +2 -4
- data/lib/active_graph/core/query_builder.rb +1 -3
- data/lib/active_graph/core/schema.rb +0 -5
- data/lib/active_graph/core.rb +0 -1
- data/lib/active_graph/migration.rb +2 -2
- data/lib/active_graph/migrations/base.rb +2 -2
- data/lib/active_graph/migrations/helpers/id_property.rb +5 -8
- data/lib/active_graph/migrations/schema.rb +7 -7
- data/lib/active_graph/model_schema.rb +2 -2
- data/lib/active_graph/node/has_n.rb +2 -2
- data/lib/active_graph/node/persistence.rb +2 -3
- data/lib/active_graph/node/query/query_proxy.rb +1 -1
- data/lib/active_graph/node/query/query_proxy_methods.rb +2 -2
- data/lib/active_graph/node.rb +1 -0
- data/lib/active_graph/railtie.rb +3 -4
- data/lib/active_graph/relationship.rb +1 -0
- data/lib/active_graph/shared/callbacks.rb +8 -10
- data/lib/active_graph/shared/persistence.rb +5 -15
- data/lib/active_graph/shared.rb +1 -1
- data/lib/active_graph/tasks/migration.rake +8 -8
- data/lib/active_graph/transaction.rb +9 -124
- data/lib/active_graph/transactions.rb +25 -22
- data/lib/active_graph/version.rb +1 -1
- data/lib/active_graph.rb +3 -0
- metadata +6 -7
- data/lib/active_graph/core/driver.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef0444b21eb9130b0047fa423911cff404686bb29a7af0e63b66dd093d822f2b
|
4
|
+
data.tar.gz: d359a53fbe4ecfec871b596ecd3aeae4293f9369059090cec06f78e2d8a6c77c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 736e716587d1b7cb30f44263ac05108793b1819765c4e362240421022a3581b96bbbd186ca132846ade50a643f59edb422e2cb9c492c31d6c517556fb96e2c08
|
7
|
+
data.tar.gz: e985e8b8223a56a02c581f50e180851fee59b3fcbad402560f59746df6e64c3bfa8d4ccbf813571235ff5e5af1585b28939c51d835e13b8fd6b516dbe121b66c
|
data/Gemfile
CHANGED
data/activegraph.gemspec
CHANGED
@@ -38,12 +38,12 @@ DESCRIPTION
|
|
38
38
|
s.add_development_dependency('guard-rspec')
|
39
39
|
s.add_development_dependency('guard-rubocop')
|
40
40
|
s.add_development_dependency('neo4j-rake_tasks', '>= 0.3.0')
|
41
|
-
s.add_development_dependency("neo4j-#{ENV['driver'] == 'java' ? 'java' : 'ruby'}-driver", '>= 0.3.
|
41
|
+
s.add_development_dependency("neo4j-#{ENV['driver'] == 'java' ? 'java' : 'ruby'}-driver", '>= 0.3.5')
|
42
42
|
s.add_development_dependency('os')
|
43
43
|
s.add_development_dependency('pry')
|
44
44
|
s.add_development_dependency('railties', '>= 4.0')
|
45
45
|
s.add_development_dependency('rake')
|
46
|
-
s.add_development_dependency('rubocop', '
|
46
|
+
s.add_development_dependency('rubocop', '>= 0.56.0')
|
47
47
|
s.add_development_dependency('yard')
|
48
48
|
s.add_development_dependency('dryspec')
|
49
49
|
end
|
data/lib/active_graph/base.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
+
require 'active_graph/core/querable'
|
2
|
+
require 'active_graph/core/schema'
|
3
|
+
|
1
4
|
module ActiveGraph
|
2
5
|
# To contain any base login for Node/Relationship which
|
3
6
|
# is external to the main classes
|
4
7
|
module Base
|
5
8
|
include ActiveGraph::Transactions
|
9
|
+
include ActiveGraph::Core::Querable
|
10
|
+
extend ActiveGraph::Core::Schema
|
6
11
|
|
7
12
|
at_exit do
|
8
13
|
@driver&.close
|
@@ -10,16 +15,12 @@ module ActiveGraph
|
|
10
15
|
|
11
16
|
class << self
|
12
17
|
# private?
|
13
|
-
def
|
18
|
+
def driver
|
14
19
|
(@driver ||= establish_driver).tap do |driver|
|
15
20
|
fail 'No driver defined!' if driver.nil?
|
16
21
|
end
|
17
22
|
end
|
18
23
|
|
19
|
-
def driver
|
20
|
-
current_driver.driver
|
21
|
-
end
|
22
|
-
|
23
24
|
def on_establish_driver(&block)
|
24
25
|
@establish_driver_block = block
|
25
26
|
end
|
@@ -28,18 +29,10 @@ module ActiveGraph
|
|
28
29
|
@establish_driver_block.call if @establish_driver_block
|
29
30
|
end
|
30
31
|
|
31
|
-
def new_driver(url, auth_token, options = {})
|
32
|
-
verbose_query_logs = ActiveGraph::Config.fetch(:verbose_query_logs, false)
|
33
|
-
ActiveGraph::Core::Driver
|
34
|
-
.new(url, auth_token, options, verbose_query_logs: verbose_query_logs)
|
35
|
-
end
|
36
|
-
|
37
|
-
def transaction
|
38
|
-
current_transaction || Transaction
|
39
|
-
end
|
40
|
-
|
41
32
|
def query(*args)
|
42
|
-
transaction
|
33
|
+
transaction do
|
34
|
+
super(*args)
|
35
|
+
end
|
43
36
|
end
|
44
37
|
|
45
38
|
# Should support setting driver via config options
|
@@ -48,35 +41,24 @@ module ActiveGraph
|
|
48
41
|
@driver = driver
|
49
42
|
end
|
50
43
|
|
51
|
-
def
|
52
|
-
Transaction.run(current_driver, run_in_tx) do |tx|
|
53
|
-
yield tx
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def new_transaction
|
44
|
+
def validating_transaction(&block)
|
58
45
|
validate_model_schema!
|
59
|
-
|
46
|
+
transaction(&block)
|
60
47
|
end
|
61
48
|
|
62
49
|
def new_query(options = {})
|
63
50
|
validate_model_schema!
|
64
|
-
ActiveGraph::Core::Query.new(
|
51
|
+
ActiveGraph::Core::Query.new(options)
|
65
52
|
end
|
66
53
|
|
67
54
|
def magic_query(*args)
|
68
55
|
if args.empty? || args.map(&:class) == [Hash]
|
69
|
-
|
56
|
+
new_query(*args)
|
70
57
|
else
|
71
|
-
|
58
|
+
query(*args)
|
72
59
|
end
|
73
60
|
end
|
74
61
|
|
75
|
-
def current_transaction
|
76
|
-
validate_model_schema!
|
77
|
-
Transaction.root
|
78
|
-
end
|
79
|
-
|
80
62
|
def label_object(label_name)
|
81
63
|
ActiveGraph::Core::Label.new(label_name)
|
82
64
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'active_support/concern'
|
2
2
|
require 'active_support/notifications'
|
3
3
|
require 'active_graph/ansi'
|
4
|
+
require 'active_graph/core/logging'
|
4
5
|
|
5
6
|
module ActiveGraph
|
6
7
|
module Core
|
@@ -14,7 +15,7 @@ module ActiveGraph
|
|
14
15
|
def subscribe_to_request
|
15
16
|
ActiveSupport::Notifications.subscribe('neo4j.core.bolt.request') do |_, start, finish, _id, _payload|
|
16
17
|
ms = (finish - start) * 1000
|
17
|
-
yield " #{ANSI::BLUE}BOLT:#{ANSI::CLEAR} #{ANSI::YELLOW}#{ms.round}ms#{ANSI::CLEAR}
|
18
|
+
yield " #{ANSI::BLUE}BOLT:#{ANSI::CLEAR} #{ANSI::YELLOW}#{ms.round}ms#{ANSI::CLEAR}"
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
@@ -27,7 +28,7 @@ module ActiveGraph
|
|
27
28
|
source_line, line_number = Logging.first_external_path_and_line(caller_locations)
|
28
29
|
|
29
30
|
yield " #{ANSI::CYAN}#{query.context || 'CYPHER'}#{ANSI::CLEAR} #{cypher} #{params_string}" +
|
30
|
-
("\n ↳ #{source_line}:#{line_number}" if
|
31
|
+
("\n ↳ #{source_line}:#{line_number}" if ActiveGraph::Config.fetch(:verbose_query_logs, false) && source_line).to_s
|
31
32
|
end
|
32
33
|
end
|
33
34
|
end
|
@@ -76,7 +76,7 @@ module ActiveGraph
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def constraints(_options = {})
|
79
|
-
ActiveGraph::
|
79
|
+
ActiveGraph::Base.constraints.select do |definition|
|
80
80
|
definition[:label] == @name.to_sym
|
81
81
|
end
|
82
82
|
end
|
@@ -109,13 +109,13 @@ module ActiveGraph
|
|
109
109
|
|
110
110
|
class << self
|
111
111
|
def indexes
|
112
|
-
ActiveGraph::
|
112
|
+
ActiveGraph::Base.indexes
|
113
113
|
end
|
114
114
|
|
115
115
|
def drop_indexes
|
116
116
|
indexes.each do |definition|
|
117
117
|
begin
|
118
|
-
ActiveGraph::
|
118
|
+
ActiveGraph::Base.query("DROP INDEX ON :`#{definition[:label]}`(#{definition[:properties][0]})")
|
119
119
|
rescue Neo4j::Driver::Exceptions::DatabaseException
|
120
120
|
# This will error on each constraint. Ignore and continue.
|
121
121
|
next
|
@@ -124,11 +124,10 @@ module ActiveGraph
|
|
124
124
|
end
|
125
125
|
|
126
126
|
def drop_constraints
|
127
|
-
ActiveGraph::
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
ActiveGraph::Transaction.query("DROP CONSTRAINT ON (n:`#{definition[:label]}`) ASSERT n.`#{definition[:properties][0]}` IS UNIQUE")
|
127
|
+
ActiveGraph::Base.transaction do |tx|
|
128
|
+
tx.run('CALL db.constraints').each do |record|
|
129
|
+
tx.run("DROP #{record.keys.include?(:name) ? "CONSTRAINT #{record[:name]}" : record[:description]}")
|
130
|
+
end
|
132
131
|
end
|
133
132
|
end
|
134
133
|
|
@@ -147,7 +146,7 @@ module ActiveGraph
|
|
147
146
|
end
|
148
147
|
|
149
148
|
def schema_query(cypher)
|
150
|
-
ActiveGraph::
|
149
|
+
ActiveGraph::Base.query(cypher, {})
|
151
150
|
end
|
152
151
|
|
153
152
|
def validate_index_options!(options)
|
@@ -30,7 +30,7 @@ module ActiveGraph
|
|
30
30
|
def neo4j_gem_path
|
31
31
|
return if !defined?(::Rails.root)
|
32
32
|
|
33
|
-
@neo4j_gem_path ||= File.expand_path('../../..', ActiveGraph::Base.method(:
|
33
|
+
@neo4j_gem_path ||= File.expand_path('../../..', ActiveGraph::Base.method(:driver).source_location[0])
|
34
34
|
end
|
35
35
|
|
36
36
|
def active_support_gem_path
|
@@ -27,29 +27,8 @@ module ActiveGraph
|
|
27
27
|
|
28
28
|
query_builder.instance_eval(&block)
|
29
29
|
|
30
|
-
|
31
|
-
query_set(
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# If called without a block, returns a Transaction object
|
36
|
-
# which can be used to call query/queries/mark_failed/commit
|
37
|
-
# If called with a block, the Transaction object is yielded
|
38
|
-
# to the block and `commit` is ensured. Any uncaught exceptions
|
39
|
-
# will mark the transaction as failed first
|
40
|
-
def transaction
|
41
|
-
return Transaction.new unless block_given?
|
42
|
-
|
43
|
-
begin
|
44
|
-
tx = transaction
|
45
|
-
|
46
|
-
yield tx
|
47
|
-
rescue => e
|
48
|
-
tx.mark_failed if tx
|
49
|
-
|
50
|
-
raise e
|
51
|
-
ensure
|
52
|
-
tx.close if tx
|
30
|
+
transaction do
|
31
|
+
query_set(query_builder.queries, options)
|
53
32
|
end
|
54
33
|
end
|
55
34
|
|
@@ -60,28 +39,20 @@ module ActiveGraph
|
|
60
39
|
end
|
61
40
|
end
|
62
41
|
|
63
|
-
def query_set(
|
42
|
+
def query_set(queries, options = {})
|
64
43
|
setup_queries!(queries, skip_instrumentation: options[:skip_instrumentation])
|
65
44
|
|
66
45
|
ActiveSupport::Notifications.instrument('neo4j.core.bolt.request') do
|
67
46
|
self.wrap_level = options[:wrap_level]
|
68
|
-
|
69
|
-
|
47
|
+
transaction do |tx|
|
48
|
+
queries.map do |query|
|
49
|
+
result_from_data(tx.run(query.cypher, query.parameters))
|
50
|
+
end
|
70
51
|
end
|
71
52
|
rescue Neo4j::Driver::Exceptions::Neo4jException => e
|
72
53
|
raise ActiveGraph::Core::CypherError.new_from(e.code, e.message) # , e.stack_track.to_a
|
73
54
|
end
|
74
55
|
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
def new_or_current_transaction(tx, &block)
|
79
|
-
if tx
|
80
|
-
yield(tx)
|
81
|
-
else
|
82
|
-
transaction(&block)
|
83
|
-
end
|
84
|
-
end
|
85
56
|
end
|
86
57
|
end
|
87
58
|
end
|
@@ -68,8 +68,6 @@ module ActiveGraph
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def initialize(options = {})
|
71
|
-
@driver = options[:driver]
|
72
|
-
|
73
71
|
@options = options
|
74
72
|
@clauses = []
|
75
73
|
@_params = {}
|
@@ -237,7 +235,7 @@ module ActiveGraph
|
|
237
235
|
def response
|
238
236
|
return @response if @response
|
239
237
|
|
240
|
-
@response = ActiveGraph::
|
238
|
+
@response = ActiveGraph::Base.query(self, wrap_level: (:core_entity if unwrapped?))
|
241
239
|
end
|
242
240
|
|
243
241
|
def raise_if_cypher_error!(response)
|
@@ -374,7 +372,7 @@ module ActiveGraph
|
|
374
372
|
end
|
375
373
|
|
376
374
|
def &(other)
|
377
|
-
self.class.new
|
375
|
+
self.class.new.tap do |new_query|
|
378
376
|
new_query.options = options.merge(other.options)
|
379
377
|
new_query.clauses = clauses + other.clauses
|
380
378
|
end.params(other._params)
|
data/lib/active_graph/core.rb
CHANGED
@@ -28,7 +28,7 @@ module ActiveGraph
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def query(*args)
|
31
|
-
ActiveGraph::Base.
|
31
|
+
ActiveGraph::Base.query(*args)
|
32
32
|
end
|
33
33
|
|
34
34
|
class AddIdProperty < ActiveGraph::Migration
|
@@ -115,7 +115,7 @@ MESSAGE
|
|
115
115
|
# def id_batch_set(label, id_property, new_ids, count)
|
116
116
|
# tx = ActiveGraph::Base.new_transaction
|
117
117
|
|
118
|
-
# ActiveGraph::Base.
|
118
|
+
# ActiveGraph::Base.query("MATCH (n:`#{label}`) WHERE NOT EXISTS(n.#{id_property})
|
119
119
|
# with COLLECT(n) as nodes, #{new_ids} as ids
|
120
120
|
# FOREACH(i in range(0,#{count - 1})|
|
121
121
|
# FOREACH(node in [nodes[i]]|
|
@@ -63,11 +63,11 @@ module ActiveGraph
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def migration_transaction(&block)
|
66
|
-
ActiveGraph::Base.
|
66
|
+
transactions? ? ActiveGraph::Base.transaction(&block) : block.call
|
67
67
|
end
|
68
68
|
|
69
69
|
def log_queries
|
70
|
-
subscriber = ActiveGraph::
|
70
|
+
subscriber = ActiveGraph::Base.subscribe_to_query(&method(:output))
|
71
71
|
yield
|
72
72
|
ensure
|
73
73
|
ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
|
@@ -27,23 +27,20 @@ module ActiveGraph
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def id_batch_set(label, id_property, new_ids, count)
|
30
|
-
|
31
|
-
|
32
|
-
execute("MATCH (n:`#{label}`) WHERE NOT EXISTS(n.#{id_property})
|
30
|
+
ActiveGraph::Base.transaction do
|
31
|
+
execute("MATCH (n:`#{label}`) WHERE NOT EXISTS(n.#{id_property})
|
33
32
|
with COLLECT(n) as nodes, #{new_ids} as ids
|
34
33
|
FOREACH(i in range(0,#{count - 1})|
|
35
34
|
FOREACH(node in [nodes[i]]|
|
36
35
|
SET node.#{id_property} = ids[i]))
|
37
36
|
RETURN distinct(true)
|
38
37
|
LIMIT #{count}")
|
39
|
-
|
40
|
-
|
41
|
-
rescue ActiveGraph::Server::CypherResponse::ResponseError
|
38
|
+
count
|
39
|
+
end
|
40
|
+
rescue ActiveGraph::Server::CypherResponse::ResponseError
|
42
41
|
new_max_per_batch = (max_per_batch * 0.8).round
|
43
42
|
output "Error querying #{max_per_batch} nodes. Trying #{new_max_per_batch}"
|
44
43
|
new_max_per_batch
|
45
|
-
ensure
|
46
|
-
tx.close
|
47
44
|
end
|
48
45
|
|
49
46
|
def print_status(last_time_taken, max_per_batch, nodes_left)
|
@@ -2,16 +2,16 @@ module ActiveGraph
|
|
2
2
|
module Migrations
|
3
3
|
module Schema
|
4
4
|
class << self
|
5
|
-
def fetch_schema_data
|
6
|
-
{constraints: fetch_constraint_descriptions
|
7
|
-
indexes: fetch_index_descriptions
|
5
|
+
def fetch_schema_data
|
6
|
+
{constraints: fetch_constraint_descriptions.sort,
|
7
|
+
indexes: fetch_index_descriptions.sort}
|
8
8
|
end
|
9
9
|
|
10
|
-
def synchronize_schema_data(
|
10
|
+
def synchronize_schema_data(schema_data, remove_missing)
|
11
11
|
queries = []
|
12
12
|
queries += drop_and_create_queries(fetch_constraint_descriptions(driver), schema_data[:constraints], remove_missing)
|
13
13
|
queries += drop_and_create_queries(fetch_index_descriptions(driver), schema_data[:indexes], remove_missing)
|
14
|
-
|
14
|
+
ActiveGraph::Base.queries do
|
15
15
|
queries.each { |query| append query }
|
16
16
|
end
|
17
17
|
end
|
@@ -19,11 +19,11 @@ module ActiveGraph
|
|
19
19
|
private
|
20
20
|
|
21
21
|
def fetch_constraint_descriptions(driver)
|
22
|
-
|
22
|
+
ActiveGraph::Base.query('CALL db.constraints()').map(&:description)
|
23
23
|
end
|
24
24
|
|
25
25
|
def fetch_index_descriptions(driver)
|
26
|
-
result =
|
26
|
+
result = ActiveGraph::Base.query('CALL db.indexes()')
|
27
27
|
if result.columns.include?(:description)
|
28
28
|
v3_indexes(result)
|
29
29
|
else
|
@@ -32,7 +32,7 @@ module ActiveGraph
|
|
32
32
|
def model_constraints
|
33
33
|
return @model_constraints if @model_constraints
|
34
34
|
|
35
|
-
constraints = ActiveGraph::
|
35
|
+
constraints = ActiveGraph::Base.constraints.each_with_object({}) do |row, result|
|
36
36
|
result[row[:label]] ||= []
|
37
37
|
result[row[:label]] << row[:properties]
|
38
38
|
end
|
@@ -43,7 +43,7 @@ module ActiveGraph
|
|
43
43
|
def model_indexes
|
44
44
|
return @model_indexes if @model_indexes
|
45
45
|
|
46
|
-
indexes = ActiveGraph::
|
46
|
+
indexes = ActiveGraph::Base.indexes.each_with_object({}) do |row, result|
|
47
47
|
result[row[:label]] ||= []
|
48
48
|
result[row[:label]] << row[:properties]
|
49
49
|
end
|
@@ -446,7 +446,7 @@ module ActiveGraph::Node
|
|
446
446
|
|
447
447
|
clear_deferred_nodes_for_association(name)
|
448
448
|
|
449
|
-
|
449
|
+
ActiveGraph::Base.transaction { association_proxy(name).replace_with(other_nodes) }
|
450
450
|
end
|
451
451
|
end
|
452
452
|
|
@@ -524,7 +524,7 @@ module ActiveGraph::Node
|
|
524
524
|
if persisted?
|
525
525
|
other_node.save if other_node.respond_to?(:persisted?) && !other_node.persisted?
|
526
526
|
association_proxy_cache.clear # TODO: Should probably just clear for this association...
|
527
|
-
|
527
|
+
ActiveGraph::Base.transaction { association_proxy(name).replace_with(other_node) }
|
528
528
|
# handle_non_persisted_node(other_node)
|
529
529
|
else
|
530
530
|
defer_create(name, other_node, clear: true)
|
@@ -93,10 +93,9 @@ module ActiveGraph::Node
|
|
93
93
|
end
|
94
94
|
|
95
95
|
# The pending associations are cleared during the save process, so it's necessary to
|
96
|
-
# build the processable hash before it begins.
|
97
|
-
# need to be created after the node is saved, a new transaction is started.
|
96
|
+
# build the processable hash before it begins.
|
98
97
|
def cascade_save
|
99
|
-
|
98
|
+
ActiveGraph::Base.transaction do
|
100
99
|
yield.tap { process_unpersisted_nodes! }
|
101
100
|
end
|
102
101
|
end
|
@@ -205,7 +205,7 @@ module ActiveGraph
|
|
205
205
|
fail 'Can only create relationships on associations' if !@association
|
206
206
|
other_nodes = _nodeify!(*other_nodes)
|
207
207
|
|
208
|
-
ActiveGraph::Base.
|
208
|
+
ActiveGraph::Base.transaction do
|
209
209
|
other_nodes.each do |other_node|
|
210
210
|
if other_node.neo_id
|
211
211
|
other_node.try(:delete_reverse_has_one_core_rel, association)
|
@@ -152,10 +152,10 @@ module ActiveGraph
|
|
152
152
|
fail 'Method invalid when called on Class objects' unless source_object
|
153
153
|
result = self.where(params).first
|
154
154
|
return result unless result.nil?
|
155
|
-
ActiveGraph::Base.
|
155
|
+
ActiveGraph::Base.transaction do
|
156
156
|
node = model.create(params)
|
157
157
|
self << node
|
158
|
-
|
158
|
+
node
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
data/lib/active_graph/node.rb
CHANGED
@@ -46,6 +46,7 @@ module ActiveGraph
|
|
46
46
|
include ActiveGraph::Node::Enum
|
47
47
|
include ActiveGraph::Shared::PermittedAttributes
|
48
48
|
include ActiveGraph::Node::DependentCallbacks
|
49
|
+
include ActiveGraph::Transactions
|
49
50
|
|
50
51
|
def initialize(args = nil)
|
51
52
|
self.class.ensure_id_property_info! # So that we make sure all objects have an id_property
|
data/lib/active_graph/railtie.rb
CHANGED
@@ -2,7 +2,6 @@ require 'active_support/notifications'
|
|
2
2
|
require 'rails/railtie'
|
3
3
|
# Need the action_dispatch railtie to have action_dispatch.rescue_responses initialized correctly
|
4
4
|
require 'action_dispatch/railtie'
|
5
|
-
require 'active_graph/core/driver'
|
6
5
|
|
7
6
|
module ActiveGraph
|
8
7
|
class Railtie < ::Rails::Railtie
|
@@ -65,7 +64,7 @@ module ActiveGraph
|
|
65
64
|
auth_token ||= username ? Neo4j::Driver::AuthTokens.basic(username, password) : Neo4j::Driver::AuthTokens.none
|
66
65
|
register_neo4j_cypher_logging
|
67
66
|
|
68
|
-
|
67
|
+
Neo4j::Driver::GraphDatabase.driver(url || path || default_driver_path_or_url, auth_token, config)
|
69
68
|
end
|
70
69
|
|
71
70
|
def final_driver_config!(neo4j_config)
|
@@ -99,8 +98,8 @@ module ActiveGraph
|
|
99
98
|
logger_proc = ->(message) do
|
100
99
|
(ActiveGraph::Config[:logger] ||= Rails.logger).debug message
|
101
100
|
end
|
102
|
-
ActiveGraph::
|
103
|
-
ActiveGraph::
|
101
|
+
ActiveGraph::Base.subscribe_to_query(&logger_proc)
|
102
|
+
ActiveGraph::Base.subscribe_to_request(&logger_proc)
|
104
103
|
|
105
104
|
@neo4j_cypher_logging_registered = true
|
106
105
|
end
|
@@ -20,16 +20,14 @@ module ActiveGraph
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def destroy #:nodoc:
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
ActiveGraph::Base.validating_transaction do |tx|
|
24
|
+
tx.after_commit { run_callbacks(:destroy_commit) {} }
|
25
|
+
run_callbacks(:destroy) { super }
|
26
|
+
end
|
26
27
|
rescue
|
27
28
|
@_deleted = false
|
28
29
|
@attributes = @attributes.dup
|
29
|
-
tx.mark_failed if tx
|
30
30
|
raise
|
31
|
-
ensure
|
32
|
-
tx.close if tx
|
33
31
|
end
|
34
32
|
|
35
33
|
def touch #:nodoc:
|
@@ -51,15 +49,15 @@ module ActiveGraph
|
|
51
49
|
end
|
52
50
|
|
53
51
|
def create_model #:nodoc:
|
54
|
-
|
55
|
-
tx.
|
52
|
+
ActiveGraph::Base.transaction do |tx|
|
53
|
+
tx.after_commit { run_callbacks(:create_commit) {} }
|
56
54
|
run_callbacks(:create) { super }
|
57
55
|
end
|
58
56
|
end
|
59
57
|
|
60
58
|
def update_model(*) #:nodoc:
|
61
|
-
|
62
|
-
tx.
|
59
|
+
ActiveGraph::Base.transaction do |tx|
|
60
|
+
tx.after_commit { run_callbacks(:update_commit) {} }
|
63
61
|
run_callbacks(:update) { super }
|
64
62
|
end
|
65
63
|
end
|
@@ -89,14 +89,10 @@ module ActiveGraph::Shared
|
|
89
89
|
@_create_or_updating = true
|
90
90
|
apply_default_values
|
91
91
|
result = _persisted_obj ? update_model : create_model
|
92
|
-
current_transaction = ActiveGraph::Base.current_transaction
|
93
92
|
|
94
|
-
|
93
|
+
ActiveGraph::Base.transaction(&:failure) if result == false
|
95
94
|
|
96
95
|
result != false
|
97
|
-
rescue => e
|
98
|
-
current_transaction.mark_failed if current_transaction
|
99
|
-
raise e
|
100
96
|
ensure
|
101
97
|
@_create_or_updating = nil
|
102
98
|
end
|
@@ -180,10 +176,10 @@ module ActiveGraph::Shared
|
|
180
176
|
# Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved.
|
181
177
|
# If saving fails because the resource is invalid then false will be returned.
|
182
178
|
def update(attributes)
|
183
|
-
|
179
|
+
ActiveGraph::Base.transaction do |tx|
|
184
180
|
self.attributes = process_attributes(attributes)
|
185
181
|
saved = save
|
186
|
-
tx.
|
182
|
+
tx.failure unless saved
|
187
183
|
saved
|
188
184
|
end
|
189
185
|
end
|
@@ -197,7 +193,7 @@ module ActiveGraph::Shared
|
|
197
193
|
|
198
194
|
def update_db_properties(hash)
|
199
195
|
fail ::ActiveGraph::Error, 'can not update on a new record object' unless persisted?
|
200
|
-
|
196
|
+
ActiveGraph::Base.transaction do
|
201
197
|
db_values = props_for_db(hash)
|
202
198
|
neo4j_query(query_as(:n).set(n: db_values))
|
203
199
|
db_values.each_pair { |k, v| self.public_send(:"#{k}=", v) }
|
@@ -210,7 +206,7 @@ module ActiveGraph::Shared
|
|
210
206
|
|
211
207
|
# Same as {#update_attributes}, but raises an exception if saving fails.
|
212
208
|
def update!(attributes)
|
213
|
-
|
209
|
+
ActiveGraph::Base.transaction do
|
214
210
|
self.attributes = process_attributes(attributes)
|
215
211
|
save!
|
216
212
|
end
|
@@ -227,12 +223,6 @@ module ActiveGraph::Shared
|
|
227
223
|
end
|
228
224
|
end
|
229
225
|
|
230
|
-
module ClassMethods
|
231
|
-
def run_transaction(run_in_tx = true)
|
232
|
-
ActiveGraph::Base.run_transaction(run_in_tx) { |tx| yield tx }
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
226
|
protected
|
237
227
|
|
238
228
|
def increment_by_query!(match_query, attribute, by, element_name = :n)
|
data/lib/active_graph/shared.rb
CHANGED
@@ -9,8 +9,8 @@ if !defined?(Rails) && !Rake::Task.task_defined?('environment')
|
|
9
9
|
require 'ostruct'
|
10
10
|
neo4j_url = ENV['NEO4J_URL'] || 'http://localhost:7474'
|
11
11
|
$LOAD_PATH.unshift File.dirname('./')
|
12
|
-
ActiveGraph::Base.
|
13
|
-
|
12
|
+
ActiveGraph::Base.on_establish_driver do
|
13
|
+
Neo4j::Driver::GraphDatabase.driver(neo4j_url)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -64,7 +64,7 @@ namespace :neo4j do
|
|
64
64
|
COMMENT
|
65
65
|
|
66
66
|
def check_neo4j_version_3
|
67
|
-
if ActiveGraph::Base.
|
67
|
+
if ActiveGraph::Base.version > '3.0.0'
|
68
68
|
yield
|
69
69
|
else
|
70
70
|
puts 'WARNING: This task does not work for versions of Neo4j before 3.0.0'
|
@@ -76,7 +76,7 @@ COMMENT
|
|
76
76
|
check_neo4j_version_3 do
|
77
77
|
require 'active_graph/migrations/schema'
|
78
78
|
|
79
|
-
schema_data = ActiveGraph::Migrations::Schema.fetch_schema_data
|
79
|
+
schema_data = ActiveGraph::Migrations::Schema.fetch_schema_data
|
80
80
|
|
81
81
|
runner = ActiveGraph::Migrations::Runner.new
|
82
82
|
schema_data[:versions] = runner.complete_migration_versions.sort
|
@@ -97,13 +97,13 @@ COMMENT
|
|
97
97
|
|
98
98
|
schema_data = YAML.safe_load(File.read(SCHEMA_YAML_PATH), [Symbol])
|
99
99
|
|
100
|
-
ActiveGraph::
|
100
|
+
ActiveGraph::Base.subscribe_to_query(&method(:puts))
|
101
101
|
|
102
|
-
ActiveGraph::Base.
|
103
|
-
ActiveGraph::Migrations::Schema.synchronize_schema_data(
|
102
|
+
ActiveGraph::Base.transaction do
|
103
|
+
ActiveGraph::Migrations::Schema.synchronize_schema_data(schema_data, args[:remove_missing])
|
104
104
|
end
|
105
105
|
|
106
|
-
ActiveGraph::Base.
|
106
|
+
ActiveGraph::Base.transaction do
|
107
107
|
runner = ActiveGraph::Migrations::Runner.new
|
108
108
|
runner.mark_versions_as_complete(schema_data[:versions]) # Run in test mode?
|
109
109
|
end
|
@@ -1,139 +1,24 @@
|
|
1
|
-
require 'active_support/core_ext/module/delegation'
|
2
|
-
require 'active_support/core_ext/module/attribute_accessors_per_thread'
|
3
|
-
require 'active_graph/core/querable'
|
4
|
-
require 'active_graph/core/schema'
|
5
|
-
|
6
1
|
module ActiveGraph
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
thread_mattr_accessor :stack
|
12
|
-
attr_reader :root
|
13
|
-
attr_reader :driver_tx, :driver_session
|
14
|
-
|
15
|
-
class << self
|
16
|
-
# Runs the given block in a new transaction.
|
17
|
-
# @param [Boolean] run_in_tx if true a new transaction will not be created, instead if will simply yield to the given block
|
18
|
-
# @@yield [ActiveGraph::Transaction::Instance]
|
19
|
-
def run(_driver, run_in_tx)
|
20
|
-
return yield(nil) unless run_in_tx
|
21
|
-
|
22
|
-
tx = ActiveGraph::Transaction.new
|
23
|
-
yield tx
|
24
|
-
rescue Exception => e # rubocop:disable Lint/RescueException
|
25
|
-
tx.mark_failed unless tx.nil?
|
26
|
-
raise e
|
27
|
-
ensure
|
28
|
-
tx.close unless tx.nil?
|
29
|
-
end
|
30
|
-
|
31
|
-
def root
|
32
|
-
initialized_stack.first
|
33
|
-
end
|
34
|
-
|
35
|
-
def initialized_stack
|
36
|
-
self.stack ||= []
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def initialize(_options = {})
|
41
|
-
self.class.initialized_stack << self
|
42
|
-
@root = stack.first
|
43
|
-
return unless root?
|
44
|
-
@driver_session = Base.current_driver.driver.session(Neo4j::Driver::AccessMode::WRITE)
|
45
|
-
@driver_tx = @driver_session.begin_transaction
|
46
|
-
rescue StandardError => e
|
47
|
-
self.stack = []
|
48
|
-
@driver_tx.close if @driver_tx
|
49
|
-
@driver_session.close if @driver_session
|
50
|
-
raise e
|
51
|
-
end
|
52
|
-
|
53
|
-
# Commits or marks this transaction for rollback, depending on whether #mark_failed has been previously invoked.
|
54
|
-
def close
|
55
|
-
fail 'Tried closing when transaction stack is empty (maybe you closed too many?)' if stack.empty?
|
56
|
-
fail "Closed transaction which wasn't the most recent on the stack (maybe you forgot to close one?)" if stack.pop != self
|
57
|
-
|
58
|
-
post_close! if stack.empty?
|
59
|
-
end
|
60
|
-
|
61
|
-
# Marks this transaction as failed,
|
62
|
-
# which means that it will unconditionally be rolled back
|
63
|
-
# when #close is called.
|
64
|
-
# Aliased for legacy purposes.
|
65
|
-
def mark_failed
|
66
|
-
root.mark_failed if root && root != self
|
2
|
+
module Transaction
|
3
|
+
def failure
|
4
|
+
super
|
67
5
|
@failure = true
|
68
6
|
end
|
69
7
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
def failed?
|
75
|
-
!!@failure
|
76
|
-
end
|
77
|
-
|
78
|
-
alias failure? failed?
|
79
|
-
|
80
|
-
def root?
|
81
|
-
@root == self
|
82
|
-
end
|
83
|
-
|
84
|
-
def query(*args)
|
85
|
-
options = if args[0].is_a?(::ActiveGraph::Core::Query)
|
86
|
-
args[1] ||= {}
|
87
|
-
else
|
88
|
-
args[1] ||= {}
|
89
|
-
args[2] ||= {}
|
90
|
-
end
|
91
|
-
options[:transaction] ||= self
|
92
|
-
|
93
|
-
self.class.query(*args)
|
94
|
-
end
|
95
|
-
|
96
|
-
def queries(options = {}, &block)
|
97
|
-
self.class.queries({ transaction: self }.merge(options), &block)
|
98
|
-
end
|
99
|
-
|
100
|
-
def after_commit_registry
|
101
|
-
@after_commit_registry ||= []
|
8
|
+
def close
|
9
|
+
success
|
10
|
+
super
|
11
|
+
after_commit_registry.each(&:call) unless @failure
|
102
12
|
end
|
103
13
|
|
104
14
|
def after_commit(&block)
|
105
15
|
after_commit_registry << block
|
106
16
|
end
|
107
17
|
|
108
|
-
def commit
|
109
|
-
return unless root?
|
110
|
-
begin
|
111
|
-
@driver_tx.success
|
112
|
-
@driver_tx.close
|
113
|
-
ensure
|
114
|
-
@driver_session.close
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def delete
|
119
|
-
root.driver_tx.failure
|
120
|
-
root.driver_tx.close
|
121
|
-
root.driver_session.close
|
122
|
-
end
|
123
|
-
|
124
|
-
def root_tx
|
125
|
-
root.driver_tx
|
126
|
-
end
|
127
|
-
|
128
18
|
private
|
129
19
|
|
130
|
-
def
|
131
|
-
|
132
|
-
delete
|
133
|
-
else
|
134
|
-
commit
|
135
|
-
after_commit_registry.each(&:call)
|
136
|
-
end
|
20
|
+
def after_commit_registry
|
21
|
+
@after_commit_registry ||= []
|
137
22
|
end
|
138
23
|
end
|
139
24
|
end
|
@@ -5,50 +5,53 @@ module ActiveGraph
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
thread_mattr_accessor :
|
8
|
+
thread_mattr_accessor :explicit_session, :tx
|
9
9
|
end
|
10
10
|
|
11
11
|
class_methods do
|
12
12
|
def session(*args)
|
13
|
-
driver.session(*args) do |session|
|
14
|
-
self.
|
13
|
+
ActiveGraph::Base.driver.session(*args) do |session|
|
14
|
+
self.explicit_session = session
|
15
15
|
yield session
|
16
16
|
session.last_bookmark
|
17
|
-
ensure
|
18
|
-
self.ag_session = nil
|
19
17
|
end
|
20
18
|
end
|
21
19
|
|
22
|
-
def
|
23
|
-
send_transaction(:
|
20
|
+
def transaction(**config, &block)
|
21
|
+
send_transaction(:begin_transaction, **config, &block)
|
24
22
|
end
|
25
|
-
alias transaction write_transaction
|
26
23
|
|
27
|
-
def
|
28
|
-
send_transaction(:
|
24
|
+
def write_transaction(**config, &block)
|
25
|
+
send_transaction(:write_transaction, **config, &block)
|
29
26
|
end
|
30
27
|
|
31
|
-
def
|
32
|
-
send_transaction(:
|
28
|
+
def read_transaction(**config, &block)
|
29
|
+
send_transaction(:read_transaction, **config, &block)
|
33
30
|
end
|
34
31
|
|
35
32
|
private
|
36
33
|
|
37
|
-
def send_transaction(method, config
|
38
|
-
return
|
39
|
-
return
|
40
|
-
driver.session
|
34
|
+
def send_transaction(method, **config, &block)
|
35
|
+
return checked_yield(tx, &block) if tx&.open?
|
36
|
+
return run_transaction_work(explicit_session, method, **config, &block) if explicit_session&.open?
|
37
|
+
driver.session do |session|
|
38
|
+
run_transaction_work(session, method, **config, &block)
|
39
|
+
end
|
41
40
|
end
|
42
41
|
|
43
|
-
def
|
44
|
-
session.send(method, config) do |tx|
|
42
|
+
def run_transaction_work(session, method, **config, &block)
|
43
|
+
session.send(method, **config) do |tx|
|
45
44
|
self.tx = tx
|
46
|
-
|
47
|
-
tx.success
|
48
|
-
ensure
|
49
|
-
self.tx = nil
|
45
|
+
checked_yield(tx, &block)
|
50
46
|
end
|
51
47
|
end
|
48
|
+
|
49
|
+
def checked_yield(tx)
|
50
|
+
yield tx
|
51
|
+
rescue StandardError => e
|
52
|
+
tx.failure
|
53
|
+
raise e
|
54
|
+
end
|
52
55
|
end
|
53
56
|
end
|
54
57
|
end
|
data/lib/active_graph/version.rb
CHANGED
data/lib/active_graph.rb
CHANGED
@@ -4,6 +4,7 @@ require 'active_graph/version'
|
|
4
4
|
require 'active_graph/core'
|
5
5
|
require 'active_graph/core/query_ext' # From this gem
|
6
6
|
|
7
|
+
require 'active_support/core_ext/module/attribute_accessors_per_thread'
|
7
8
|
require 'active_graph/transactions'
|
8
9
|
require 'active_graph/base'
|
9
10
|
require 'active_graph/model_schema'
|
@@ -118,3 +119,5 @@ if defined?(Rails)
|
|
118
119
|
require 'rails/generators'
|
119
120
|
require 'rails/generators/neo4j_generator'
|
120
121
|
end
|
122
|
+
|
123
|
+
Neo4j::Driver::Transaction.prepend ActiveGraph::Transaction
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activegraph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 10.0.0.pre.beta.
|
4
|
+
version: 10.0.0.pre.beta.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Ronge, Brian Underwood, Chris Grigg, Heinrich Klobuczek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-04-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -128,14 +128,14 @@ dependencies:
|
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.3.
|
131
|
+
version: 0.3.5
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0.3.
|
138
|
+
version: 0.3.5
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: os
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -196,14 +196,14 @@ dependencies:
|
|
196
196
|
name: rubocop
|
197
197
|
requirement: !ruby/object:Gem::Requirement
|
198
198
|
requirements:
|
199
|
-
- - "
|
199
|
+
- - ">="
|
200
200
|
- !ruby/object:Gem::Version
|
201
201
|
version: 0.56.0
|
202
202
|
type: :development
|
203
203
|
prerelease: false
|
204
204
|
version_requirements: !ruby/object:Gem::Requirement
|
205
205
|
requirements:
|
206
|
-
- - "
|
206
|
+
- - ">="
|
207
207
|
- !ruby/object:Gem::Version
|
208
208
|
version: 0.56.0
|
209
209
|
- !ruby/object:Gem::Dependency
|
@@ -262,7 +262,6 @@ files:
|
|
262
262
|
- lib/active_graph/core.rb
|
263
263
|
- lib/active_graph/core/connection_failed_error.rb
|
264
264
|
- lib/active_graph/core/cypher_error.rb
|
265
|
-
- lib/active_graph/core/driver.rb
|
266
265
|
- lib/active_graph/core/instrumentable.rb
|
267
266
|
- lib/active_graph/core/label.rb
|
268
267
|
- lib/active_graph/core/logging.rb
|
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/module/attribute_accessors'
|
2
|
-
require 'active_graph/core/logging'
|
3
|
-
require 'active_graph/version'
|
4
|
-
|
5
|
-
module ActiveGraph
|
6
|
-
module Core
|
7
|
-
class Driver
|
8
|
-
USER_AGENT_STRING = "activegraph-gem/#{::ActiveGraph::VERSION} (https://github.com/neo4jrb/activegraph)"
|
9
|
-
|
10
|
-
attr_accessor :wrap_level
|
11
|
-
attr_reader :options, :driver, :url
|
12
|
-
delegate :close, to: :driver
|
13
|
-
|
14
|
-
class << self
|
15
|
-
def new_instance(url, auth_token, options = {})
|
16
|
-
Neo4j::Driver::GraphDatabase.driver(url, auth_token, options)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def initialize(url, auth_token = Neo4j::Driver::AuthTokens.none, options = {}, extended_options = {})
|
21
|
-
@url = url
|
22
|
-
@driver = self.class.new_instance(url, auth_token, options)
|
23
|
-
@options = extended_options
|
24
|
-
end
|
25
|
-
|
26
|
-
def logger
|
27
|
-
return @logger if @logger
|
28
|
-
|
29
|
-
@logger = if @options[:logger]
|
30
|
-
@options[:logger]
|
31
|
-
else
|
32
|
-
Logger.new(logger_location).tap do |logger|
|
33
|
-
logger.level = logger_level
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def logger_location
|
41
|
-
@options[:logger_location] || STDOUT
|
42
|
-
end
|
43
|
-
|
44
|
-
def logger_level
|
45
|
-
@options[:logger_level] || Logger::WARN
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|