lib/neo4j/rails/model.rb in neo4j-1.0.0.beta.9 vs lib/neo4j/rails/model.rb in neo4j-1.0.0.beta.10
- old
+ new
@@ -5,10 +5,12 @@
include ActiveModel::MassAssignmentSecurity
extend ActiveModel::Naming
extend ActiveModel::Callbacks
extend Neo4j::Validations::ClassMethods
+ extend Neo4j::TxMethods
+
define_model_callbacks :create, :save, :update, :destroy
UniquenessValidator = Neo4j::Validations::UniquenessValidator
@@ -37,11 +39,11 @@
# --------------------------------------
# Identity
# --------------------------------------
def id
- self.neo_id
+ neo_id.nil? ? nil : neo_id.to_s
end
def to_param
persisted? ? neo_id.to_s : nil
end
@@ -81,11 +83,11 @@
changed_attributes[attr] = value
end
def read_attribute_for_validation(key)
- respond_to?(key)? send(key) : self[key]
+ respond_to?(key) ? send(key) : self[key]
end
def attributes=(values)
sanitize_for_mass_assignment(values).each do |k, v|
if respond_to?("#{k}=")
@@ -96,67 +98,104 @@
end
end
# Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved.
- # If the saving fails because of a connection or remote service error, an exception will be raised.
# If saving fails because the resource is invalid then false will be returned.
def update_attributes(attributes)
- Neo4j::Rails::Transaction.running? ? update_attributes_in_tx(attributes) : Neo4j::Rails::Transaction.run { update_attributes_in_tx(attributes) }
- end
-
- def update_attributes_in_tx(attributes)
self.attributes=attributes
save
end
def update_attributes!(attributes)
- Neo4j::Rails::Transaction.running? ? update_attributes_in_tx!(attributes) : Neo4j::Rails::Transaction.run { update_attributes_in_tx!(attributes) }
- end
-
- def update_attributes_in_tx!(attributes)
self.attributes=attributes
save!
end
+ def update_nested_attributes(rel_type, clazz, has_one, attr, options)
+ allow_destroy,reject_if = [options[:allow_destroy], options[:reject_if]] if options
+
+ if new?
+ # We are updating a node that was created with the 'new' method.
+ # The relationship will only be kept in the Value object.
+ outgoing(rel_type)<<clazz.new(attr) unless reject_if?(reject_if,attr)
+ else
+ # We have a node that was created with the #create method - has real Neo4j relationships
+ # does it exist ?
+ found = if has_one
+ # id == nil that means we have a has_one relationship
+ outgoing(rel_type).first
+ else
+ # do we have an ID ?
+ id = attr[:id]
+ # this is a has_n relationship, find which one we want to update
+ id && outgoing(rel_type).find { |n| n.id == id }
+ end
+
+ # Check if we want to destroy not found nodes (e.g. {..., :_destroy => '1' } ?
+ destroy = attr[:_destroy]
+ if found
+ if destroy
+ found.destroy if allow_destroy
+ else
+ found.update_attributes_in_tx(attr) # it already exist, so update that one
+ end
+ elsif !destroy && !reject_if?(reject_if,attr)
+ new_node = clazz.new(attr)
+ saved = new_node.save
+ outgoing(rel_type) << new_node if saved
+ end
+ end
+ end
+
+ def reject_if?(proc_or_symbol, attr)
+ return false if proc_or_symbol.nil?
+ if proc_or_symbol.is_a?(Symbol)
+ meth = method(proc_or_symbol)
+ meth.arity == 0 ? meth.call : meth.call(attr)
+ else
+ proc_or_symbol.call(attr)
+ end
+ end
+
def delete
super
@_deleted = true
@_persisted = false
end
def save
- if valid?
+ valid = valid?
+ if valid
# if we are trying to save a value then we should create a real node
- if Neo4j::Rails::Transaction.running?
- _run_save_callbacks { save_in_tx }
- else
- Neo4j::Rails::Transaction.run { _run_save_callbacks { save_in_tx } }
- end
+ valid = _run_save_callbacks { create_or_update_node }
@_created_record = false
true
else
# if not valid we should rollback the transaction so that the changes does not take place.
# no point failing the transaction if we have created a model with 'new'
Neo4j::Rails::Transaction.fail if Neo4j::Rails::Transaction.running? && !_java_node.kind_of?(Neo4j::Value)
false
end
+ valid
end
- def save_in_tx
-# _run_save_callbacks do
+ def create_or_update_node
+ valid = true
if _java_node.kind_of?(Neo4j::Value)
node = Neo4j::Node.new(props)
+ valid = _java_node.save_nested(node)
init_on_load(node)
init_on_create
end
if new_record?
_run_create_callbacks { clear_changes }
else
_run_update_callbacks { clear_changes }
end
+ valid
end
def clear_changes
@previously_changed = changes
@changed_attributes.clear
@@ -176,42 +215,70 @@
end
# Returns true if this object hasn’t been saved yet — that is, a record for the object doesn’t exist yet; otherwise, returns false.
def new_record?()
# it is new if the model has been created with either the new or create method
- _java_node.kind_of?(Neo4j::Value) || @_created_record == true
+ new? || @_created_record == true
end
+ def new?
+ _java_node.kind_of?(Neo4j::Value)
+ end
+
def del
@_deleted = true
super
end
def destroy
- Neo4j::Rails::Transaction.running? ? _run_update_callbacks { del } : Neo4j::Rails::Transaction.run { _run_update_callbacks { del } }
+ _run_update_callbacks { del }
end
def destroyed?()
@_deleted
end
+ tx_methods :destroy, :create_or_update_node, :update_attributes, :update_attributes!
# --------------------------------------
# Class Methods
# --------------------------------------
class << self
+ extend Neo4j::TxMethods
+
# returns a value object instead of creating a new node
def new(*args)
value = Neo4j::Value.new
wrapped = self.orig_new
wrapped.init_on_load(value)
wrapped.attributes=args[0] if args[0].respond_to?(:each_pair)
+
+ wrapped.class._decl_rels.each_pair do |field, dsl|
+
+ meta = class << wrapped;
+ self;
+ end
+
+ wrapped.class._decl_rels.each_pair do |field, dsl|
+ meta.send(:define_method, field) do
+ if new?
+ value.outgoing(dsl.namespace_type)
+ else
+ self.outgoing(dsl.namespace_type)
+ end
+ end if dsl.direction == :outgoing
+
+ meta.send(:define_method, field) do
+ raise "NOT IMPLEMENTED #{field} (incoming relationship) FOR #new method, please create a new model with the create method instead"
+ end if dsl.direction == :incoming
+ end
+ end
+
wrapped
end
-
# Handle Model.find(params[:id])
def find(*args)
if args.length == 1 && String === args[0] && args[0].to_i != 0
load(*args)
else
@@ -234,28 +301,57 @@
alias_method :_orig_create, :create
def create(*)
- Neo4j::Rails::Transaction.running? ? create_in_tx(super) : Neo4j::Rails::Transaction.run { create_in_tx(super) }
- end
-
- def create!(*args)
- Neo4j::Rails::Transaction.running? ? create_in_tx!(_orig_create(*args)) : Neo4j::Rails::Transaction.run { create_in_tx!(_orig_create(*args)) }
- end
-
- def create_in_tx(model)
+ model = super
model.save
model
end
- def create_in_tx!(model)
+ def create!(*args)
+ model = _orig_create(*args)
model.save!
model
end
+ tx_methods :create, :create!
+
+
def transaction(&block)
Neo4j::Rails::Transaction.run &block
end
+
+ def accepts_nested_attributes_for(*attr_names)
+ options = attr_names.pop if attr_names[-1].is_a?(Hash)
+
+ attr_names.each do |association_name|
+ rel = self._decl_rels[association_name.to_sym]
+ raise "No relationship declared with has_n or has_one with type #{association_name}" unless rel
+ to_class = rel.to_class
+ raise "Can't use accepts_nested_attributes_for(#{association_name}) since it has not defined which class it has a relationship to, use has_n(#{association_name}).to(MyOtherClass)" unless to_class
+ type = rel.namespace_type
+ has_one = rel.has_one?
+
+ send(:define_method, "#{association_name}_attributes=") do |attributes|
+ if has_one
+ update_nested_attributes(type, to_class, true, attributes, options)
+ else
+ if attributes.is_a?(Array)
+ attributes.each do |attr|
+ update_nested_attributes(type, to_class, false, attr, options)
+ end
+ else
+ attributes.each_value do |attr|
+ update_nested_attributes(type, to_class, false, attr, options)
+ end
+ end
+ end
+ end
+ tx_methods("#{association_name}_attributes=")
+ end
+ end
+
end
end
+