lib/neo4j/rails/model.rb in neo4j-1.0.0.beta.19 vs lib/neo4j/rails/model.rb in neo4j-1.0.0.beta.20
- old
+ new
@@ -1,25 +1,31 @@
module Neo4j
module Rails
class Model
include Neo4j::NodeMixin
+
include ActiveModel::Serializers::Xml
include ActiveModel::Validations
include ActiveModel::Dirty
include ActiveModel::MassAssignmentSecurity
extend ActiveModel::Naming
extend ActiveModel::Callbacks
- extend Neo4j::Validations::ClassMethods
+
+ include Neo4j::Rails::Validations
+ extend Neo4j::Rails::Validations::ClassMethods
+
+ include Finders # ActiveRecord style find
+ include Mapping::Property # allows some additional options on the #property class method
+
extend TxMethods
+ class_inheritable_hash :attribute_defaults
+ self.attribute_defaults = {}
+
define_model_callbacks :create, :save, :update, :destroy
- rule :all
-
- UniquenessValidator = Neo4j::Validations::UniquenessValidator
-
class RecordInvalidError < RuntimeError
attr_reader :record
def initialize(record)
@record = record
@@ -61,35 +67,19 @@
def method_missing(method_id, *args, &block)
if !self.class.attribute_methods_generated?
self.class.define_attribute_methods(self.class._decl_props.keys)
# try again
send(method_id, *args, &block)
+ elsif property?(method_id)
+ send(:[], method_id)
+ else
+ super
end
end
- # redefine this methods so that ActiveModel::Dirty will work
- def []=(key, new_value)
- key = key.to_s
- unless key[0] == ?_
- old_value = self.send(:[], key)
- attribute_will_change!(key) unless old_value == new_value
- end
- Neo4j::Rails::Transaction.running? ? super : Neo4j::Rails::Transaction.run { super }
- end
-
- def attribute_will_change!(attr)
- begin
- value = __send__(:[], attr)
- value = value.duplicable? ? value.clone : value
- rescue TypeError, NoMethodError
- end
- changed_attributes[attr] = value
- end
-
-
def read_attribute_for_validation(key)
- respond_to?(key) ? send(key) : self[key]
+ send(key)
end
def attributes=(values)
sanitize_for_mass_assignment(values).each do |k, v|
if respond_to?("#{k}=")
@@ -157,28 +147,30 @@
else
proc_or_symbol.call(attr)
end
end
- def save
+ def save(*args)
_run_save_callbacks do
- if create_or_update_node
+ if create_or_update_node(*args)
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::Rails::Value)
false
end
end
end
- def create_or_update_node
- if valid?(:save)
+ def create_or_update_node(options = {})
+ options.reverse_merge!({ :validate => true })
+
+ if options[:validate] == false || valid?(:save)
if new_record?
_run_create_callbacks do
- if valid?(:create)
+ if options[:validate] == false || valid?(:create)
node = Neo4j::Node.new(props)
return false unless _java_node.save_nested(node)
init_on_load(node)
init_on_create
self.created_at = DateTime.now if Neo4j::Config[:timestamps] && respond_to?(:created_at)
@@ -186,11 +178,11 @@
true
end
end
else
_run_update_callbacks do
- if valid?(:update)
+ if options[:validate] == false || valid?(:update)
clear_changes
self.updated_at = DateTime.now if Neo4j::Config[:timestamps] && respond_to?(:updated_at)
true
end
end
@@ -206,12 +198,12 @@
def reload(options = nil)
clear_changes
reload_from_database or set_deleted_properties and return self
end
- def save!
- raise RecordInvalidError.new(self) unless save
+ def save!(*args)
+ raise RecordInvalidError.new(self) unless save(*args)
end
# Returns if the record is persisted, i.e. it’s not a new record and it was not destroyed
def persisted?
!new_record? && !destroyed?
@@ -243,11 +235,12 @@
def destroyed?()
@_deleted
end
- tx_methods :destroy, :create_or_update_node, :update_attributes, :update_attributes!
+ # TODO: []= shouldn't need to be in a transaction because it shouldn't update the DB. Need to refactor the @_java_node handling stuff if we want that to be the case though
+ tx_methods :destroy, :create_or_update_node, :[]=, :update_attributes, :update_attributes!, :update_nested_attributes
# --------------------------------------
# Class Methods
# --------------------------------------
@@ -257,92 +250,61 @@
# returns a value object instead of creating a new node
def new(*args)
wrapped = self.orig_new
value = Neo4j::Rails::Value.new(wrapped)
wrapped.init_on_load(value)
- wrapped.attributes=args[0] if args[0].respond_to?(:each_pair)
+ wrapped.attributes = initial_attributes(*args)
wrapped
end
- # Behave like ActiveModel
- def all_with_args(*args)
- if args.empty?
- all_without_args
- else
- hits = find_without_checking_for_id(*args)
- # We need to save this so that the Rack Neo4j::Rails:LuceneConnection::Closer can close it
- Thread.current[:neo4j_lucene_connection] ||= []
- Thread.current[:neo4j_lucene_connection] << hits
- hits
- end
- end
-
- alias_method_chain :all, :args
-
- # Handle Model.find(params[:id])
- def find_with_checking_for_id(*args)
- if args.length == 1 && String === args[0] && args[0].to_i != 0
- load(*args)
- else
- all_with_args(*args).first
- end
- end
-
- alias_method_chain :find, :checking_for_id
-
- def load(*ids)
- result = ids.map { |id| Neo4j::Node.load(id) }
- if ids.length == 1
- result.first
- else
- result
- end
- end
-
alias_method :_orig_create, :create
def create(*args)
new(*args).tap { |o| o.save }
end
def create!(*args)
new(*args).tap { |o| o.save! }
end
- tx_methods :create, :create!
-
def transaction(&block)
- Neo4j::Rails::Transaction.run &block
+ Neo4j::Rails::Transaction.run do |tx|
+ block.call(tx)
+ end
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
+ target_class = rel.target_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 target_class
+ type = rel.rel_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)
+ update_nested_attributes(type, target_class, true, attributes, options)
else
if attributes.is_a?(Array)
attributes.each do |attr|
- update_nested_attributes(type, to_class, false, attr, options)
+ update_nested_attributes(type, target_class, false, attr, options)
end
else
attributes.each_value do |attr|
- update_nested_attributes(type, to_class, false, attr, options)
+ update_nested_attributes(type, target_class, false, attr, options)
end
end
end
end
- tx_methods("#{association_name}_attributes=")
end
+ end
+
+ protected
+ def initial_attributes(*args)
+ args.first.is_a?(Hash) ? args.first.reverse_merge(attribute_defaults) : attribute_defaults
end
end
private
def reload_from_database