-
Notifications
You must be signed in to change notification settings - Fork 276
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lazy association creation #871
Changes from 8 commits
e08a83c
47955ad
78eda06
97e181e
51d4adf
a77da97
7120439
6a834d5
5551b14
718460d
98cc0e4
3735715
1030b95
9e2e588
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,8 +24,10 @@ def initialize(record) | |
# If any of the before_* callbacks return false the action is cancelled and save returns false. | ||
def save(*) | ||
update_magic_properties | ||
association_proxy_cache.clear | ||
create_or_update | ||
cascade_save do | ||
association_proxy_cache.clear | ||
create_or_update | ||
end | ||
end | ||
|
||
# Persist the object to the database. Validations and Callbacks are included | ||
|
@@ -53,7 +55,7 @@ def create_model(*) | |
node = _create_node(properties) | ||
init_on_load(node, node.props) | ||
send_props(@relationship_props) if @relationship_props | ||
@relationship_props = nil | ||
@relationship_props = @deferred_nodes = nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is Rubocop really OK about this?! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess so. It's really sensitive about the length of things, so I tend to try pushing out before down. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yup, it's just that I've had an error on something similar once, but actually it was on a parallel assignment. |
||
true | ||
end | ||
|
||
|
@@ -66,12 +68,25 @@ def _create_node(*args) | |
session.create_node(props, labels) | ||
end | ||
|
||
private | ||
|
||
# The pending associations are cleared during the save process, so it's necessary to | ||
# build the processable hash before it begins. If there are nodes and associations that | ||
# need to be created after the node is saved, a new transaction is started. | ||
def cascade_save | ||
deferred_nodes = pending_associations_with_nodes | ||
Neo4j::Transaction.run(!deferred_nodes.blank?) do | ||
result = yield | ||
process_unpersisted_nodes!(deferred_nodes) if deferred_nodes | ||
result | ||
end | ||
end | ||
|
||
module ClassMethods | ||
# Creates and saves a new node | ||
# @param [Hash] props the properties the new node should have | ||
def create(props = {}) | ||
association_props = extract_association_attributes!(props) || {} | ||
|
||
new(props).tap do |obj| | ||
yield obj if block_given? | ||
obj.save | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
module Neo4j | ||
module ActiveNode | ||
module Query | ||
module QueryProxyUnpersisted | ||
def defer_create(other_nodes, _properties, operator) | ||
key = [@association.name, [nil, nil, nil]].hash | ||
@start_object.pending_associations[key] = [@association.name, operator] | ||
if @start_object.association_proxy_cache[key] | ||
@start_object.association_proxy_cache[key] << other_nodes | ||
else | ||
@start_object.association_proxy_cache[key] = [other_nodes] | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
module Neo4j | ||
module ActiveNode | ||
module Unpersisted | ||
def pending_associations | ||
@pending_associations ||= {} | ||
end | ||
|
||
def pending_associations? | ||
!@pending_associations.blank? | ||
end | ||
|
||
private | ||
|
||
# TODO: Change this method's name. | ||
# Takes the pending_associations hash, which is in the format { cache_key => [:association_name, :association_operator]}, | ||
# and returns them as { association_name => [[nodes_for_persistence], :operator] } | ||
def pending_associations_with_nodes | ||
return unless pending_associations? | ||
{}.tap do |deferred_nodes| | ||
pending_associations.each_pair do |cache_key, (association_name, operator)| | ||
nodes_for_creation = self.persisted? ? association_proxy_cache[cache_key].select { |n| !n.persisted? } : association_proxy_cache[cache_key] | ||
deferred_nodes[association_name] = [nodes_for_creation, operator] | ||
end | ||
end | ||
end | ||
|
||
# @param [Hash] deferred_nodes A hash created by :pending_associations_with_nodes | ||
def process_unpersisted_nodes!(deferred_nodes) | ||
deferred_nodes.each_pair do |k, (v, o)| | ||
save_and_associate_queue(k, v, o) | ||
end | ||
end | ||
|
||
|
||
def save_and_associate_queue(association_name, node_queue, operator) | ||
association_proc = proc { |node| save_and_associate_node(association_name, node, operator) } | ||
node_queue.each do |element| | ||
element.is_a?(Array) ? element.each { |node| association_proc.call(node) } : association_proc.call(element) | ||
end | ||
end | ||
|
||
def save_and_associate_node(association_name, node, operator) | ||
node.save if node.changed? || !node.persisted? | ||
fail "Unable to defer node persistence, could not save #{node.inspect}" unless node.persisted? | ||
operator == :<< ? send(association_name).send(operator, node) : send(:"#{association_name}=", node) | ||
end | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tsk tsk... ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was done before I split things out into modules, it can be removed
now. ;-)
On Friday, July 17, 2015, Brian Underwood [email protected] wrote: