awesome_delete 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,2 @@
1
+ /spec/debug.log
2
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'pry'
6
+ gem 'pry-nav'
7
+ gem 'pg'
8
+
9
+ gem 'nokogiri', '1.6.6.4'
10
+
11
+ # gem "appraisal"
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,4 @@
1
+ require 'active_record'
2
+ require 'awesome_delete/delete_extension'
3
+
4
+ ActiveRecord::Base.send :extend, AwesomeDelete::DeleteExtension
@@ -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
@@ -0,0 +1,3 @@
1
+ module AwesomeDelete
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,10 @@
1
+ postgresql:
2
+ adapter: postgresql
3
+ database: awesome_delete_test
4
+ encoding: unicode
5
+ pool: 10
6
+ username: skylark
7
+ password: skylark
8
+ host: localhost
9
+ port: 5432
10
+ min_messages: warning
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,11 @@
1
+ entry_a:
2
+ id: 1
3
+ response_id: 1
4
+
5
+ entry_b:
6
+ id: 2
7
+ response_id: 2
8
+
9
+ entry_c:
10
+ id: 3
11
+ response_id: 3
@@ -0,0 +1,16 @@
1
+ field_a:
2
+ id: 1
3
+ form_id: 1
4
+ type: 'Field::A'
5
+ options_count: 1
6
+
7
+ field_b:
8
+ id: 2
9
+ form_id: 1
10
+ type: 'Field::B'
11
+ options_count: 2
12
+
13
+ field_c:
14
+ id: 3
15
+ form_id: 1
16
+ type: 'Field::C'
@@ -0,0 +1,13 @@
1
+ form_a:
2
+ id: 1
3
+ title: form_a
4
+ formable_id: 1
5
+ formable_type: Project
6
+ user_id: 1
7
+
8
+ form_b:
9
+ id: 2
10
+ title: form_b
11
+ formable_id: 1
12
+ formable_type: Project
13
+ user_id: 1
@@ -0,0 +1,14 @@
1
+ option_a:
2
+ id: 1
3
+ title: option_a
4
+ field_id: 1
5
+
6
+ option_b:
7
+ id: 2
8
+ title: option_b
9
+ field_id: 2
10
+
11
+ option_c:
12
+ id: 3
13
+ title: option_c
14
+ field_id: 2
@@ -0,0 +1,3 @@
1
+ project_a:
2
+ id: 1
3
+ forms_count: 2
@@ -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
@@ -0,0 +1,4 @@
1
+ user_a:
2
+ id: 1
3
+ name: 'user_a'
4
+ forms_count: 2
@@ -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: []