Introduced acts_as_paranoid. Avoid deleting of suppliers and articles. (for consistency of order-results)
This commit is contained in:
parent
b820577382
commit
fc45345e0d
30 changed files with 1238 additions and 151 deletions
14
vendor/plugins/acts_as_paranoid/lib/caboose/acts/belongs_to_with_deleted_association.rb
vendored
Normal file
14
vendor/plugins/acts_as_paranoid/lib/caboose/acts/belongs_to_with_deleted_association.rb
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
module Caboose # :nodoc:
|
||||
module Acts # :nodoc:
|
||||
class BelongsToWithDeletedAssociation < ActiveRecord::Associations::BelongsToAssociation
|
||||
private
|
||||
def find_target
|
||||
@reflection.klass.find_with_deleted(
|
||||
@owner[@reflection.primary_key_name],
|
||||
:conditions => conditions,
|
||||
:include => @reflection.options[:include]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
27
vendor/plugins/acts_as_paranoid/lib/caboose/acts/has_many_through_without_deleted_association.rb
vendored
Normal file
27
vendor/plugins/acts_as_paranoid/lib/caboose/acts/has_many_through_without_deleted_association.rb
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
module Caboose # :nodoc:
|
||||
module Acts # :nodoc:
|
||||
class HasManyThroughWithoutDeletedAssociation < ActiveRecord::Associations::HasManyThroughAssociation
|
||||
protected
|
||||
def current_time
|
||||
ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
|
||||
end
|
||||
|
||||
def construct_conditions
|
||||
return super unless @reflection.through_reflection.klass.paranoid?
|
||||
table_name = @reflection.through_reflection.table_name
|
||||
conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value|
|
||||
"#{table_name}.#{attr} = #{value}"
|
||||
end
|
||||
|
||||
deleted_attribute = @reflection.through_reflection.klass.deleted_attribute
|
||||
quoted_current_time = @reflection.through_reflection.klass.quote_value(
|
||||
current_time,
|
||||
@reflection.through_reflection.klass.columns_hash[deleted_attribute.to_s])
|
||||
conditions << "#{table_name}.#{deleted_attribute} IS NULL OR #{table_name}.#{deleted_attribute} > #{quoted_current_time}"
|
||||
|
||||
conditions << sql_conditions if sql_conditions
|
||||
"(" + conditions.join(') AND (') + ")"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
196
vendor/plugins/acts_as_paranoid/lib/caboose/acts/paranoid.rb
vendored
Normal file
196
vendor/plugins/acts_as_paranoid/lib/caboose/acts/paranoid.rb
vendored
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
module Caboose #:nodoc:
|
||||
module Acts #:nodoc:
|
||||
# Overrides some basic methods for the current model so that calling #destroy sets a 'deleted_at' field to the current timestamp.
|
||||
# This assumes the table has a deleted_at date/time field. Most normal model operations will work, but there will be some oddities.
|
||||
#
|
||||
# class Widget < ActiveRecord::Base
|
||||
# acts_as_paranoid
|
||||
# end
|
||||
#
|
||||
# Widget.find(:all)
|
||||
# # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL
|
||||
#
|
||||
# Widget.find(:first, :conditions => ['title = ?', 'test'], :order => 'title')
|
||||
# # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL AND title = 'test' ORDER BY title LIMIT 1
|
||||
#
|
||||
# Widget.find_with_deleted(:all)
|
||||
# # SELECT * FROM widgets
|
||||
#
|
||||
# Widget.find_only_deleted(:all)
|
||||
# # SELECT * FROM widgets WHERE widgets.deleted_at IS NOT NULL
|
||||
#
|
||||
# Widget.find_with_deleted(1).deleted?
|
||||
# # Returns true if the record was previously destroyed, false if not
|
||||
#
|
||||
# Widget.count
|
||||
# # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NULL
|
||||
#
|
||||
# Widget.count ['title = ?', 'test']
|
||||
# # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NULL AND title = 'test'
|
||||
#
|
||||
# Widget.count_with_deleted
|
||||
# # SELECT COUNT(*) FROM widgets
|
||||
#
|
||||
# Widget.count_only_deleted
|
||||
# # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NOT NULL
|
||||
#
|
||||
# Widget.delete_all
|
||||
# # UPDATE widgets SET deleted_at = '2005-09-17 17:46:36'
|
||||
#
|
||||
# Widget.delete_all!
|
||||
# # DELETE FROM widgets
|
||||
#
|
||||
# @widget.destroy
|
||||
# # UPDATE widgets SET deleted_at = '2005-09-17 17:46:36' WHERE id = 1
|
||||
#
|
||||
# @widget.destroy!
|
||||
# # DELETE FROM widgets WHERE id = 1
|
||||
#
|
||||
module Paranoid
|
||||
def self.included(base) # :nodoc:
|
||||
base.extend ClassMethods
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def acts_as_paranoid(options = {})
|
||||
unless paranoid? # don't let AR call this twice
|
||||
cattr_accessor :deleted_attribute
|
||||
self.deleted_attribute = options[:with] || :deleted_at
|
||||
alias_method :destroy_without_callbacks!, :destroy_without_callbacks
|
||||
class << self
|
||||
alias_method :find_every_with_deleted, :find_every
|
||||
alias_method :calculate_with_deleted, :calculate
|
||||
alias_method :delete_all!, :delete_all
|
||||
end
|
||||
end
|
||||
include InstanceMethods
|
||||
end
|
||||
|
||||
def paranoid?
|
||||
self.included_modules.include?(InstanceMethods)
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods #:nodoc:
|
||||
def self.included(base) # :nodoc:
|
||||
base.extend ClassMethods
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def find_with_deleted(*args)
|
||||
options = args.extract_options!
|
||||
validate_find_options(options)
|
||||
set_readonly_option!(options)
|
||||
options[:with_deleted] = true # yuck!
|
||||
|
||||
case args.first
|
||||
when :first then find_initial(options)
|
||||
when :all then find_every(options)
|
||||
else find_from_ids(args, options)
|
||||
end
|
||||
end
|
||||
|
||||
def find_only_deleted(*args)
|
||||
options = args.extract_options!
|
||||
validate_find_options(options)
|
||||
set_readonly_option!(options)
|
||||
options[:only_deleted] = true # yuck!
|
||||
|
||||
case args.first
|
||||
when :first then find_initial(options)
|
||||
when :all then find_every(options)
|
||||
else find_from_ids(args, options)
|
||||
end
|
||||
end
|
||||
|
||||
def exists?(*args)
|
||||
with_deleted_scope { exists_with_deleted?(*args) }
|
||||
end
|
||||
|
||||
def exists_only_deleted?(*args)
|
||||
with_only_deleted_scope { exists_with_deleted?(*args) }
|
||||
end
|
||||
|
||||
def count_with_deleted(*args)
|
||||
calculate_with_deleted(:count, *construct_count_options_from_args(*args))
|
||||
end
|
||||
|
||||
def count_only_deleted(*args)
|
||||
with_only_deleted_scope { count_with_deleted(*args) }
|
||||
end
|
||||
|
||||
def count(*args)
|
||||
with_deleted_scope { count_with_deleted(*args) }
|
||||
end
|
||||
|
||||
def calculate(*args)
|
||||
with_deleted_scope { calculate_with_deleted(*args) }
|
||||
end
|
||||
|
||||
def delete_all(conditions = nil)
|
||||
self.update_all ["#{self.deleted_attribute} = ?", current_time], conditions
|
||||
end
|
||||
|
||||
protected
|
||||
def current_time
|
||||
default_timezone == :utc ? Time.now.utc : Time.now
|
||||
end
|
||||
|
||||
def with_deleted_scope(&block)
|
||||
with_scope({:find => { :conditions => ["#{table_name}.#{deleted_attribute} IS NULL OR #{table_name}.#{deleted_attribute} > ?", current_time] } }, :merge, &block)
|
||||
end
|
||||
|
||||
def with_only_deleted_scope(&block)
|
||||
with_scope({:find => { :conditions => ["#{table_name}.#{deleted_attribute} IS NOT NULL AND #{table_name}.#{deleted_attribute} <= ?", current_time] } }, :merge, &block)
|
||||
end
|
||||
|
||||
private
|
||||
# all find calls lead here
|
||||
def find_every(options)
|
||||
options.delete(:with_deleted) ?
|
||||
find_every_with_deleted(options) :
|
||||
options.delete(:only_deleted) ?
|
||||
with_only_deleted_scope { find_every_with_deleted(options) } :
|
||||
with_deleted_scope { find_every_with_deleted(options) }
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_without_callbacks
|
||||
unless new_record?
|
||||
self.class.update_all self.class.send(:sanitize_sql, ["#{self.class.deleted_attribute} = ?", (self.deleted_at = self.class.send(:current_time))]), ["#{self.class.primary_key} = ?", id]
|
||||
end
|
||||
freeze
|
||||
end
|
||||
|
||||
def destroy_with_callbacks!
|
||||
return false if callback(:before_destroy) == false
|
||||
result = destroy_without_callbacks!
|
||||
callback(:after_destroy)
|
||||
result
|
||||
end
|
||||
|
||||
def destroy!
|
||||
transaction { destroy_with_callbacks! }
|
||||
end
|
||||
|
||||
def deleted?
|
||||
!!read_attribute(:deleted_at)
|
||||
end
|
||||
|
||||
def recover!
|
||||
self.deleted_at = nil
|
||||
save!
|
||||
end
|
||||
|
||||
def recover_with_associations!(*associations)
|
||||
self.recover!
|
||||
associations.to_a.each do |assoc|
|
||||
self.send(assoc).find_with_deleted(:all).each do |a|
|
||||
a.recover! if a.class.paranoid?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
94
vendor/plugins/acts_as_paranoid/lib/caboose/acts/paranoid_find_wrapper.rb
vendored
Normal file
94
vendor/plugins/acts_as_paranoid/lib/caboose/acts/paranoid_find_wrapper.rb
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
module Caboose #:nodoc:
|
||||
module Acts #:nodoc:
|
||||
# Adds a wrapper find method which can identify :with_deleted or :only_deleted options
|
||||
# and would call the corresponding acts_as_paranoid finders find_with_deleted or
|
||||
# find_only_deleted methods.
|
||||
#
|
||||
# With this wrapper you can easily change from using this pattern:
|
||||
#
|
||||
# if some_condition_enabling_access_to_deleted_records?
|
||||
# @post = Post.find_with_deleted(params[:id])
|
||||
# else
|
||||
# @post = Post.find(params[:id])
|
||||
# end
|
||||
#
|
||||
# to this:
|
||||
#
|
||||
# @post = Post.find(params[:id], :with_deleted => some_condition_enabling_access_to_deleted_records?)
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# class Widget < ActiveRecord::Base
|
||||
# acts_as_paranoid
|
||||
# end
|
||||
#
|
||||
# Widget.find(:all)
|
||||
# # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL
|
||||
#
|
||||
# Widget.find(:all, :with_deleted => false)
|
||||
# # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL
|
||||
#
|
||||
# Widget.find_with_deleted(:all)
|
||||
# # SELECT * FROM widgets
|
||||
#
|
||||
# Widget.find(:all, :with_deleted => true)
|
||||
# # SELECT * FROM widgets
|
||||
#
|
||||
# Widget.find_only_deleted(:all)
|
||||
# # SELECT * FROM widgets WHERE widgets.deleted_at IS NOT NULL
|
||||
#
|
||||
# Widget.find(:all, :only_deleted => true)
|
||||
# # SELECT * FROM widgets WHERE widgets.deleted_at IS NOT NULL
|
||||
#
|
||||
# Widget.find(:all, :only_deleted => false)
|
||||
# # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL
|
||||
#
|
||||
module ParanoidFindWrapper
|
||||
def self.included(base) # :nodoc:
|
||||
base.extend ClassMethods
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def acts_as_paranoid_with_find_wrapper(options = {})
|
||||
unless paranoid? # don't let AR call this twice
|
||||
acts_as_paranoid_without_find_wrapper(options)
|
||||
class << self
|
||||
alias_method :find_without_find_wrapper, :find
|
||||
alias_method :validate_find_options_without_find_wrapper, :validate_find_options
|
||||
end
|
||||
end
|
||||
include InstanceMethods
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods #:nodoc:
|
||||
def self.included(base) # :nodoc:
|
||||
base.extend ClassMethods
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# This is a wrapper for the regular "find" so you can pass acts_as_paranoid related
|
||||
# options and determine which finder to call.
|
||||
def find(*args)
|
||||
options = args.extract_options!
|
||||
# Determine who to call.
|
||||
finder_option = VALID_PARANOID_FIND_OPTIONS.detect { |key| options.delete(key) } || :without_find_wrapper
|
||||
finder_method = "find_#{finder_option}".to_sym
|
||||
# Put back the options in the args now that they don't include the extended keys.
|
||||
args << options
|
||||
send(finder_method, *args)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
VALID_PARANOID_FIND_OPTIONS = [:with_deleted, :only_deleted]
|
||||
|
||||
def validate_find_options(options) #:nodoc:
|
||||
cleaned_options = options.reject { |k, v| VALID_PARANOID_FIND_OPTIONS.include?(k) }
|
||||
validate_find_options_without_find_wrapper(cleaned_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue