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 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: []