# encoding: utf-8 require "mongoid/persistence/command" require "mongoid/persistence/insert" require "mongoid/persistence/insert_embedded" require "mongoid/persistence/remove" require "mongoid/persistence/remove_all" require "mongoid/persistence/remove_embedded" require "mongoid/persistence/update" module Mongoid #:nodoc: # The persistence module is a mixin to provide database accessor methods for # the document. These correspond to the appropriate accessors on a # +Mongo::Collection+ and retain the same DSL. # # Examples: # # document.insert # document.update # document.upsert module Persistence extend ActiveSupport::Concern module InstanceMethods #:nodoc: # Remove the +Document+ from the datbase with callbacks. # # Example: # # document.destroy # # TODO: Will get rid of other #destroy once new persistence complete. def destroy run_callbacks(:before_destroy) if _remove self.destroyed = true run_callbacks(:after_destroy) end; true end # Insert a new +Document+ into the database. Will return the document # itself whether or not the save was successful. # # Example: # # document.insert def insert(validate = true) Insert.new(self, validate).persist end # Remove the +Document+ from the datbase. # # Example: # # document._remove # # TODO: Will get rid of other #remove once observable pattern killed. def _remove Remove.new(self).persist end alias :delete :_remove # Save the document - will perform an insert if the document is new, and # update if not. If a validation error occurs a # Mongoid::Errors::Validations error will get raised. # # Example: # # document.save! # # Returns: # # +true+ if validation passed, will raise error otherwise. def save! self.class.fail_validate!(self) unless upsert; true end # Update the +Document+ in the datbase. # # Example: # # document.update def update(validate = true) Update.new(self, validate).persist end # Update the +Document+ attributes in the datbase. # # Example: # # document.update_attributes(:title => "Sir") # # Returns: # # +true+ if validation passed, +false+ if not. def update_attributes(attributes = {}) write_attributes(attributes); update end # Update the +Document+ attributes in the datbase. # # Example: # # document.update_attributes(:title => "Sir") # # Returns: # # +true+ if validation passed, raises an error if not def update_attributes!(attributes = {}) write_attributes(attributes) result = update self.class.fail_validate!(self) unless result result end # Upsert the document - will perform an insert if the document is new, and # update if not. # # Example: # # document.upsert # # Returns: # # A +Boolean+ for updates. def upsert(validate = true) validate = parse_validate(validate) if new_record? insert(validate).errors.any? ? false : true else update(validate) end end # Save is aliased so that users familiar with active record can have some # semblance of a familiar API. # # Example: # # document.save alias :save :upsert protected # Alternative validation params. def parse_validate(validate) if validate.is_a?(Hash) && validate.has_key?(:validate) validate = validate[:validate] end validate end end module ClassMethods #:nodoc: # Create a new +Document+. This will instantiate a new document and # insert it in a single call. Will always return the document # whether save passed or not. # # Example: # # Person.create(:title => "Mr") # # Returns: the +Document+. def create(attributes = {}) document = new(attributes); document.insert end # Create a new +Document+. This will instantiate a new document and # insert it in a single call. Will always return the document # whether save passed or not, and if validation fails an error will be # raise. # # Example: # # Person.create!(:title => "Mr") # # Returns: the +Document+. def create!(attributes = {}) document = new(attributes) fail_validate!(document) if document.insert.errors.any? document end # Delete all documents given the supplied conditions. If no conditions # are passed, the entire collection will be dropped for performance # benefits. Does not fire any callbacks. # # Example: # # Person.delete_all(:conditions => { :title => "Sir" }) # Person.delete_all # # Returns: true or raises an error. def delete_all(conditions = {}) RemoveAll.new( self, false, conditions[:conditions] || {} ).persist end # Delete all documents given the supplied conditions. If no conditions # are passed, the entire collection will be dropped for performance # benefits. Fires the destroy callbacks if conditions were passed. # # Example: # # Person.destroy_all(:conditions => { :title => "Sir" }) # Person.destroy_all # # Returns: true or raises an error. def destroy_all(conditions = {}) documents = all(conditions) count = documents.count documents.each { |doc| doc.destroy }; count end # Raise an error if validation failed. def fail_validate!(document) raise Errors::Validations.new(document.errors) end end end end