awesome_delete 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +11 -0
- data/README.md +39 -0
- data/awesome_delete.gemspec +25 -0
- data/lib/awesome_delete.rb +4 -0
- data/lib/awesome_delete/delete_extension.rb +153 -0
- data/lib/awesome_delete/version.rb +3 -0
- data/spec/db/database.yml +10 -0
- data/spec/db/schema.rb +55 -0
- data/spec/delete_extension_spec.rb +113 -0
- data/spec/fixtures/entries.yml +11 -0
- data/spec/fixtures/fields.yml +16 -0
- data/spec/fixtures/forms.yml +13 -0
- data/spec/fixtures/options.yml +14 -0
- data/spec/fixtures/projects.yml +3 -0
- data/spec/fixtures/responses.yml +17 -0
- data/spec/fixtures/users.yml +4 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/support/models.rb +92 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a16663db45580e4f1a4faf0e97fd4b323020f238
|
4
|
+
data.tar.gz: ebeb2c214cb0f54345838c922d409c582c27ffa2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 333c0ddd99204473a3c2081dfb59cf45cf9c0336aca9b6ea0eb16d2466145347dd464ef4193d3b4cfb09d99baf5f7dd4df7dd2c80e1d14154b35114e2f53d232
|
7
|
+
data.tar.gz: 7048b3cc09be57ecf2c157e1f1d13cf0a5f0adee7758d435cbe7dad9726b47b454d4d400430847087f52e84408907756d264c1cead60325b4b9e3d75cee4a58f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# awesome_delete
|
2
|
+
> Recursive delete appropriately
|
3
|
+
|
4
|
+
Recursively delete a collection and its all assoication with less sqls.
|
5
|
+
It thinks about the following
|
6
|
+
- STI (delete the associations of subclass)
|
7
|
+
- polymorphism
|
8
|
+
- counter_cache, touch (avoid to unnecessary handle)
|
9
|
+
- callbacks
|
10
|
+
|
11
|
+
## Example
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
class Form < ActiveRecord::Base
|
15
|
+
has_many :fields, dependent: :destroy
|
16
|
+
end
|
17
|
+
|
18
|
+
class Field < ActiveRecord::Base
|
19
|
+
end
|
20
|
+
|
21
|
+
Form.delete_collection [1,4,5]
|
22
|
+
```
|
23
|
+
The class method `execute_callbacks` will execute callbacks.
|
24
|
+
Overwriting it maybe a better choice.
|
25
|
+
eg:
|
26
|
+
```ruby
|
27
|
+
class CloudFile < ActiveRecord::Base
|
28
|
+
after_destroy :remove_file
|
29
|
+
|
30
|
+
def self.execute_callbacks ids
|
31
|
+
keys = where(id: ids).pluck(:key)
|
32
|
+
# do something with all keys
|
33
|
+
end
|
34
|
+
|
35
|
+
def remove_file key
|
36
|
+
HttpClient.send_request key
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path('../lib/awesome_delete/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.authors = ['hw93']
|
5
|
+
gem.email = ['676018683@qq.com']
|
6
|
+
gem.license = 'MIT'
|
7
|
+
gem.version = AwesomeDelete::VERSION
|
8
|
+
gem.summary = 'Recursively delete appropriately'
|
9
|
+
gem.description = 'Recursively delete a collection and its all assoication with less sqls'
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = 'awesome_delete'
|
15
|
+
gem.require_paths = ['lib']
|
16
|
+
gem.homepage = 'https://github.com/hw676018683/awesome_delete'
|
17
|
+
|
18
|
+
gem.required_ruby_version = ">= 1.9.3"
|
19
|
+
|
20
|
+
gem.add_runtime_dependency 'activerecord', '>= 4.0.0', '< 5'
|
21
|
+
|
22
|
+
gem.add_development_dependency 'rspec-rails', '~> 3.0'
|
23
|
+
gem.add_development_dependency 'combustion', '>= 0.5.2'
|
24
|
+
gem.add_development_dependency 'database_cleaner'
|
25
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module AwesomeDelete
|
2
|
+
module DeleteExtension
|
3
|
+
def delete_collection ids, all_associations_name = []
|
4
|
+
return true if ids.blank?
|
5
|
+
|
6
|
+
#Not handle counter_cache or touch association in all_associations_name
|
7
|
+
@@all_associations_name = all_associations_name
|
8
|
+
|
9
|
+
if @@all_associations_name.blank?
|
10
|
+
@@all_associations_name = get_associations_name << self.name
|
11
|
+
end
|
12
|
+
|
13
|
+
delete_assoicated_collection(ids, deleted_associations)
|
14
|
+
|
15
|
+
# STI
|
16
|
+
if column_names.include? inheritance_column
|
17
|
+
where(id: ids).pluck(inheritance_column).uniq.each do |type|
|
18
|
+
subklass = type.constantize
|
19
|
+
subklass.delete_self_collection(ids)
|
20
|
+
delete_assoicated_collection(ids, subklass.deleted_associations - deleted_associations)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
delete_self_collection(ids)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def deleted_associations
|
28
|
+
@deleted_associations ||= reflect_on_all_associations.select do |asso|
|
29
|
+
[:destroy, :delete_all].include? asso.options.deep_symbolize_keys[:dependent]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def touch_associations
|
34
|
+
@touch_associations ||= reflect_on_all_associations.select do |asso|
|
35
|
+
asso.options.deep_symbolize_keys[:touch]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def counter_cache_associations
|
40
|
+
@counter_cache_associations ||= reflect_on_all_associations.select do |asso|
|
41
|
+
asso.options.deep_symbolize_keys[:counter_cache]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def delete_self_collection ids
|
46
|
+
#touch
|
47
|
+
need_handle_touch_associations = touch_associations.select do |asso|
|
48
|
+
!@@all_associations_name.include?(asso.class_name)
|
49
|
+
end
|
50
|
+
need_handle_touch_associations.each do |asso|
|
51
|
+
if asso.options[:polymorphic]
|
52
|
+
types_ids = where(id: ids).pluck(asso.foreign_type, asso.foreign_key).uniq
|
53
|
+
types = types_ids.map(&:first).uniq
|
54
|
+
types.each do |type|
|
55
|
+
type_ids = types_ids.select { |type_id| type_id.first == type }.map(&:last).uniq.compact
|
56
|
+
type.constantize.where(id: type_ids).update_all updated_at: Time.now
|
57
|
+
end
|
58
|
+
else
|
59
|
+
asso.klass.where(id: where(id: ids).pluck(asso.foreign_key)).update_all updated_at: Time.now
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
#counter_cache
|
64
|
+
need_handle_counter_cache_associations = counter_cache_associations.select do |asso|
|
65
|
+
!@@all_associations_name.include?(asso.class_name)
|
66
|
+
end
|
67
|
+
cache_ids_with_possible_types = []
|
68
|
+
need_handle_counter_cache_associations.each do |asso|
|
69
|
+
if asso.options[:polymorphic]
|
70
|
+
cache_ids_with_possible_types << where(id: ids).pluck(asso.foreign_type, asso.foreign_key).uniq
|
71
|
+
else
|
72
|
+
cache_ids_with_possible_types << where(id: ids).pluck(asso.foreign_key).uniq
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
execute_callbacks(ids)
|
77
|
+
|
78
|
+
#counter_cache
|
79
|
+
need_handle_counter_cache_associations.each_with_index do |asso, index|
|
80
|
+
if asso.options[:polymorphic]
|
81
|
+
types_ids = cache_ids_with_possible_types[index]
|
82
|
+
types = types_ids.map(&:first).uniq
|
83
|
+
types.each do |type|
|
84
|
+
ids = types_ids.select { |type_id| type_id.first == type }.map(&:last).uniq.compact
|
85
|
+
ids.each do |id|
|
86
|
+
type.constantize.where(id: id).update_all asso.counter_cache_column => where(asso.foreign_key => id).count
|
87
|
+
end
|
88
|
+
end
|
89
|
+
else
|
90
|
+
asso_ids = cache_ids_with_possible_types[index]
|
91
|
+
asso_ids.each do |id|
|
92
|
+
asso.klass.where(id: id).update_all asso.counter_cache_column => where(asso.foreign_key => id).count
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def delete_assoicated_collection ids, associations
|
99
|
+
associations.each do |association|
|
100
|
+
association_class = association.klass
|
101
|
+
|
102
|
+
#polymorphic
|
103
|
+
if association.type
|
104
|
+
association_class.delete_collection association_class.where(association.foreign_key => ids, association.type => self.name).pluck(:id), @@all_associations_name
|
105
|
+
else
|
106
|
+
association_class.delete_collection association_class.where(association.foreign_key => ids).pluck(:id), @@all_associations_name
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def get_associations_name
|
112
|
+
return [] if deleted_associations.blank?
|
113
|
+
associations_name = []
|
114
|
+
|
115
|
+
deleted_associations.each do |association|
|
116
|
+
associations_name << association.class_name
|
117
|
+
associations_name += association.class_name.constantize.get_associations_name
|
118
|
+
end
|
119
|
+
associations_name
|
120
|
+
end
|
121
|
+
|
122
|
+
def execute_callbacks ids
|
123
|
+
#overwriting this method may be a better choice
|
124
|
+
collection = where(id: ids).to_a
|
125
|
+
befores = _destroy_callbacks.select { |callback| callback.kind == :before }
|
126
|
+
afters = _destroy_callbacks.select { |callback| callback.kind == :after }
|
127
|
+
commits = _commit_callbacks.select do |callback|
|
128
|
+
ifs = callback.instance_variable_get('@if')
|
129
|
+
ifs.empty? || ifs.include?("transaction_include_any_action?([:destroy])")
|
130
|
+
end
|
131
|
+
|
132
|
+
befores.each do |callback|
|
133
|
+
case callback.filter
|
134
|
+
when Symbol
|
135
|
+
collection.each { |item| item.send callback.filter }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
where(id: ids).delete_all
|
139
|
+
afters.each do |callback|
|
140
|
+
case callback.filter
|
141
|
+
when Symbol
|
142
|
+
collection.each { |item| item.send callback.filter }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
commits.each do |callback|
|
146
|
+
case callback.filter
|
147
|
+
when Symbol
|
148
|
+
collection.each { |item| item.send callback.filter }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/spec/db/schema.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
create_table :projects, :force => true do |t|
|
3
|
+
t.string :title
|
4
|
+
t.integer :forms_count
|
5
|
+
t.datetime :created_at
|
6
|
+
t.datetime :updated_at
|
7
|
+
end
|
8
|
+
|
9
|
+
create_table :users, :force => true do |t|
|
10
|
+
t.string :name
|
11
|
+
t.integer :forms_count
|
12
|
+
t.datetime :created_at
|
13
|
+
t.datetime :updated_at
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table :forms, :force => true do |t|
|
17
|
+
t.string :title
|
18
|
+
t.integer :user_id
|
19
|
+
t.integer :formable_id
|
20
|
+
t.string :formable_type
|
21
|
+
t.datetime :created_at
|
22
|
+
t.datetime :updated_at
|
23
|
+
end
|
24
|
+
|
25
|
+
create_table :responses, :force => true do |t|
|
26
|
+
t.integer :responseable_id
|
27
|
+
t.string :responseable_type
|
28
|
+
t.integer :entries_count
|
29
|
+
t.datetime :created_at
|
30
|
+
t.datetime :updated_at
|
31
|
+
end
|
32
|
+
|
33
|
+
create_table :fields, :force => true do |t|
|
34
|
+
t.string :title
|
35
|
+
t.integer :form_id
|
36
|
+
t.string :type
|
37
|
+
t.integer :options_count, default: 0
|
38
|
+
t.datetime :created_at
|
39
|
+
t.datetime :updated_at
|
40
|
+
end
|
41
|
+
|
42
|
+
create_table :options, :force => true do |t|
|
43
|
+
t.string :title
|
44
|
+
t.integer :field_id
|
45
|
+
t.datetime :created_at
|
46
|
+
t.datetime :updated_at
|
47
|
+
end
|
48
|
+
|
49
|
+
create_table :entries, :force => true do |t|
|
50
|
+
t.string :title
|
51
|
+
t.integer :response_id
|
52
|
+
t.datetime :created_at
|
53
|
+
t.datetime :updated_at
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'AwesomeDelete' do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
self.class.fixtures :projects, :forms, :fields, :options, :responses, :entries, :users
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'deleting forms' do
|
10
|
+
let!(:form_a) { forms(:form_a) }
|
11
|
+
let!(:form_b) { forms(:form_b) }
|
12
|
+
|
13
|
+
it 'decrements the forms count' do
|
14
|
+
expect {
|
15
|
+
Form.delete_collection [form_a.id, form_b.id]
|
16
|
+
}.to change { Form.count }.by -2
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'touchs the project' do
|
20
|
+
expect(Logger).to receive(:project_touch).with("Touching")
|
21
|
+
expect {
|
22
|
+
Form.delete_collection [form_a.id, form_b.id]
|
23
|
+
}.to change { projects(:project_a).reload.updated_at }
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'updates forms_count of project' do
|
27
|
+
expect(Logger).to receive(:project_update_counter).with("Updating counter")
|
28
|
+
expect {
|
29
|
+
Form.delete_collection [form_a.id, form_b.id]
|
30
|
+
}.to change { projects(:project_a).reload.forms_count }.by -2
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'touchs the user' do
|
34
|
+
expect {
|
35
|
+
Form.delete_collection [form_a.id, form_b.id]
|
36
|
+
}.to change { users(:user_a).reload.updated_at }
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'updates forms_count of user' do
|
40
|
+
expect {
|
41
|
+
Form.delete_collection [form_a.id, form_b.id]
|
42
|
+
}.to change { users(:user_a).reload.forms_count }.by -2
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'decrements the fields count' do
|
46
|
+
expect {
|
47
|
+
Form.delete_collection [form_a.id, form_b.id]
|
48
|
+
}.to change { Field.count }.by -3
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'decrements the options count' do
|
52
|
+
expect {
|
53
|
+
Form.delete_collection [form_a.id, form_b.id]
|
54
|
+
}.to change { Option.count }.by -3
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'decrements the responses count' do
|
58
|
+
expect {
|
59
|
+
Form.delete_collection [form_a.id, form_b.id]
|
60
|
+
}.to change { Response.count }.by -3
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'decrements the entries count' do
|
64
|
+
expect {
|
65
|
+
Form.delete_collection [form_a.id, form_b.id]
|
66
|
+
}.to change { Entry.count }.by -3
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'doesnot touch forms' do
|
70
|
+
expect(Logger).not_to receive(:form_touch).with("Touching")
|
71
|
+
Form.delete_collection [form_a.id, form_b.id]
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'doesnot updates entries of responses' do
|
75
|
+
expect(Logger).not_to receive(:response_update_counter).with("Updating counter")
|
76
|
+
Form.delete_collection [form_a.id, form_b.id]
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'executes destroy callback' do
|
80
|
+
expect(Logger).to receive(:entry_before_destroy).with("Doing other things.").exactly(3).times
|
81
|
+
expect(Logger).to receive(:entry_after_destroy).with("Doing other things.").exactly(3).times
|
82
|
+
Form.delete_collection [form_a.id, form_b.id]
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'executes commit callback' do
|
86
|
+
expect(Logger).to receive(:option_after_commit).with("Doing other things.").exactly(3).times
|
87
|
+
Form.delete_collection [form_a.id, form_b.id]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'destroying fields' do
|
92
|
+
it 'touch the form' do
|
93
|
+
field_a = fields(:field_a)
|
94
|
+
field_b = fields(:field_b)
|
95
|
+
form_a = forms(:form_a)
|
96
|
+
expect {
|
97
|
+
Field.delete_collection [field_a.id, field_b.id]
|
98
|
+
}.to change { form_a.reload.updated_at }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'destroying options' do
|
103
|
+
it 'update the options_count of the field' do
|
104
|
+
option_a = options(:option_a)
|
105
|
+
option_b = options(:option_b)
|
106
|
+
field_a = fields(:field_a)
|
107
|
+
field_b = fields(:field_b)
|
108
|
+
expect {
|
109
|
+
Option.delete_collection [option_a.id, option_b.id]
|
110
|
+
}.to change { field_a.reload.options.count }.by -1
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
response_a:
|
2
|
+
id: 1
|
3
|
+
responseable_type: Form
|
4
|
+
responseable_id: 1
|
5
|
+
entries_count: 1
|
6
|
+
|
7
|
+
response_b:
|
8
|
+
id: 2
|
9
|
+
responseable_type: Form
|
10
|
+
responseable_id: 1
|
11
|
+
entries_count: 1
|
12
|
+
|
13
|
+
response_c:
|
14
|
+
id: 3
|
15
|
+
responseable_type: Form
|
16
|
+
responseable_id: 1
|
17
|
+
entries_count: 1
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
plugin_test_dir = File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'pry'
|
6
|
+
|
7
|
+
require 'logger'
|
8
|
+
require 'active_record'
|
9
|
+
ActiveRecord::Base.logger = Logger.new(plugin_test_dir + "/debug.log")
|
10
|
+
|
11
|
+
require 'yaml'
|
12
|
+
require 'erb'
|
13
|
+
|
14
|
+
ENV["DB"] = 'postgresql'
|
15
|
+
ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(plugin_test_dir + "/db/database.yml")).result)
|
16
|
+
ActiveRecord::Base.establish_connection((ENV["DB"] ||= "sqlite3mem").to_sym)
|
17
|
+
ActiveRecord::Migration.verbose = false
|
18
|
+
|
19
|
+
require 'combustion/database'
|
20
|
+
Combustion::Database.create_database(ActiveRecord::Base.configurations[ENV["DB"]])
|
21
|
+
load(File.join(plugin_test_dir, "db", "schema.rb"))
|
22
|
+
|
23
|
+
begin
|
24
|
+
require 'action_view'
|
25
|
+
rescue LoadError; end # action_view doesn't exist in Rails 4.0, but we need this for the tests to run with Rails 4.1
|
26
|
+
|
27
|
+
require 'awesome_delete'
|
28
|
+
require 'support/models'
|
29
|
+
|
30
|
+
require 'action_controller'
|
31
|
+
require 'rspec/rails'
|
32
|
+
require 'database_cleaner'
|
33
|
+
RSpec.configure do |config|
|
34
|
+
config.fixture_path = "#{plugin_test_dir}/fixtures"
|
35
|
+
config.use_transactional_fixtures = true
|
36
|
+
config.after(:suite) do
|
37
|
+
unless /sqlite/ === ENV['DB']
|
38
|
+
Combustion::Database.drop_database(ActiveRecord::Base.configurations[ENV['DB']])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
class Project < ActiveRecord::Base
|
2
|
+
end
|
3
|
+
|
4
|
+
class User < ActiveRecord::Base
|
5
|
+
end
|
6
|
+
|
7
|
+
class Form < ActiveRecord::Base
|
8
|
+
has_many :fields, dependent: :destroy
|
9
|
+
has_many :responses, as: :responseable, dependent: :destroy
|
10
|
+
belongs_to :formable, polymorphic: true, touch: true, counter_cache: true
|
11
|
+
belongs_to :user, touch: true, counter_cache: true
|
12
|
+
end
|
13
|
+
|
14
|
+
class Field < ActiveRecord::Base
|
15
|
+
belongs_to :form, touch: true
|
16
|
+
end
|
17
|
+
|
18
|
+
class Field::A < Field
|
19
|
+
has_many :options, dependent: :destroy, foreign_key: 'field_id'
|
20
|
+
end
|
21
|
+
|
22
|
+
class Field::B < Field
|
23
|
+
has_many :options, dependent: :destroy, foreign_key: 'field_id'
|
24
|
+
end
|
25
|
+
|
26
|
+
class Field::C < Field
|
27
|
+
end
|
28
|
+
|
29
|
+
class Option < ActiveRecord::Base
|
30
|
+
after_commit :test
|
31
|
+
|
32
|
+
def test
|
33
|
+
Logger.send "option_after_commit", 'Doing other things.'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Response < ActiveRecord::Base
|
38
|
+
has_many :entries, dependent: :destroy
|
39
|
+
belongs_to :responseable, polymorphic: true
|
40
|
+
end
|
41
|
+
|
42
|
+
class Entry < ActiveRecord::Base
|
43
|
+
before_destroy :handle1
|
44
|
+
after_destroy :handle2
|
45
|
+
belongs_to :response, counter_cache: true
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def handle1
|
50
|
+
Logger.send "entry_before_destroy", 'Doing other things.'
|
51
|
+
end
|
52
|
+
|
53
|
+
def handle2
|
54
|
+
Logger.send "entry_after_destroy", 'Doing other things.'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class ActiveRecord::Relation
|
59
|
+
def update_all updates
|
60
|
+
#for test
|
61
|
+
if updates[:updated_at]
|
62
|
+
Logger.send "#{model.name.downcase}_touch", 'Touching'
|
63
|
+
elsif updates.keys.find { |key| key =~ /.*_count$/ }
|
64
|
+
Logger.send "#{model.name.downcase}_update_counter", 'Updating counter'
|
65
|
+
end
|
66
|
+
|
67
|
+
raise ArgumentError, "Empty list of attributes to change" if updates.blank?
|
68
|
+
|
69
|
+
stmt = Arel::UpdateManager.new(arel.engine)
|
70
|
+
|
71
|
+
stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
|
72
|
+
stmt.table(table)
|
73
|
+
stmt.key = table[primary_key]
|
74
|
+
|
75
|
+
if joins_values.any?
|
76
|
+
@klass.connection.join_to_update(stmt, arel)
|
77
|
+
else
|
78
|
+
stmt.take(arel.limit)
|
79
|
+
stmt.order(*arel.orders)
|
80
|
+
stmt.wheres = arel.constraints
|
81
|
+
end
|
82
|
+
|
83
|
+
bvs = arel.bind_values + bind_values
|
84
|
+
@klass.connection.update stmt, 'SQL', bvs
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Logger
|
89
|
+
def self.method_missing(method, *args, &block)
|
90
|
+
args
|
91
|
+
end
|
92
|
+
end
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: awesome_delete
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- hw93
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-12-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.0.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '5'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 4.0.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '5'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rspec-rails
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '3.0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: combustion
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.5.2
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.5.2
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: database_cleaner
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
description: Recursively delete a collection and its all assoication with less sqls
|
76
|
+
email:
|
77
|
+
- 676018683@qq.com
|
78
|
+
executables: []
|
79
|
+
extensions: []
|
80
|
+
extra_rdoc_files: []
|
81
|
+
files:
|
82
|
+
- ".gitignore"
|
83
|
+
- Gemfile
|
84
|
+
- README.md
|
85
|
+
- awesome_delete.gemspec
|
86
|
+
- lib/awesome_delete.rb
|
87
|
+
- lib/awesome_delete/delete_extension.rb
|
88
|
+
- lib/awesome_delete/version.rb
|
89
|
+
- spec/db/database.yml
|
90
|
+
- spec/db/schema.rb
|
91
|
+
- spec/delete_extension_spec.rb
|
92
|
+
- spec/fixtures/entries.yml
|
93
|
+
- spec/fixtures/fields.yml
|
94
|
+
- spec/fixtures/forms.yml
|
95
|
+
- spec/fixtures/options.yml
|
96
|
+
- spec/fixtures/projects.yml
|
97
|
+
- spec/fixtures/responses.yml
|
98
|
+
- spec/fixtures/users.yml
|
99
|
+
- spec/spec_helper.rb
|
100
|
+
- spec/support/models.rb
|
101
|
+
homepage: https://github.com/hw676018683/awesome_delete
|
102
|
+
licenses:
|
103
|
+
- MIT
|
104
|
+
metadata: {}
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: 1.9.3
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 2.4.3
|
122
|
+
signing_key:
|
123
|
+
specification_version: 4
|
124
|
+
summary: Recursively delete appropriately
|
125
|
+
test_files: []
|