mongoid_archival 0.1.0

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.
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Association
5
+ module Depending
6
+
7
+ STRATEGIES = STRATEGIES.dup + %i[archive archive_all]
8
+
9
+ def apply_archive_dependencies!
10
+ self.class._all_dependents.each do |association|
11
+ dependent = association.try(:dependent)
12
+ next if !dependent || dependent.in?(%i[delete_all destroy])
13
+ send(:"_dependent_#{dependent}!", association)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def _dependent_archive!(association)
20
+ return unless _warn_association_archivable?(association)
21
+ relation = send(association.name)
22
+ return unless relation
23
+ if relation.is_a?(Enumerable)
24
+ relation.entries
25
+ relation.each(&:archive)
26
+ elsif relation.try(:archivable?)
27
+ relation.archive
28
+ end
29
+ end
30
+
31
+ def _dependent_archive_all!(association)
32
+ return unless _warn_association_archivable?(association)
33
+ relation = send(association.name)
34
+ return unless relation
35
+ relation.set(archive_at: Time.zone.now)
36
+
37
+ # TODO: this code enables dependency recursion. Untested.
38
+ # dependents = relation.respond_to?(:dependents) && relation.dependents
39
+ # if dependents && dependents.reject {|dep| dep.try(:dependent).in?(%i[delete_all destroy]) }.blank?
40
+ # relation.set(archive_at: Time.zone.now)
41
+ # else
42
+ # ::Array.wrap(send(association.name)).each { |rel| rel.archive }
43
+ # end
44
+ end
45
+
46
+ def _warn_association_archivable?(association)
47
+ result = _association_archivable?(association)
48
+ Mongoid.logger.warn "Non-archivable association: #{association.name}" unless result
49
+ result
50
+ end
51
+
52
+ def _association_archivable?(association)
53
+ relations[association.name].class_name.constantize.try(:archivable?)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Archivable
5
+ module Protected
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include Mongoid::Persistable::Deletable
10
+
11
+ alias :delete! :delete
12
+
13
+ def delete
14
+ raise '#delete not permitted. Use #archive_without_callbacks or #delete! instead.'
15
+ end
16
+ alias :remove :delete
17
+
18
+ def destroy
19
+ raise '#destroy not permitted. Use #archive or #destroy! instead.'
20
+ end
21
+
22
+ def destroy!(options = {})
23
+ raise Errors::ReadonlyDocument.new(self.class) if readonly?
24
+ self.flagged_for_destroy = true
25
+ result = run_callbacks(:destroy) do
26
+ if catch(:abort) { apply_destroy_dependencies! }
27
+ delete!(options || {})
28
+ else
29
+ false
30
+ end
31
+ end
32
+ self.flagged_for_destroy = false
33
+ result
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Archivable
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mongoid/archivable'
data/perf/scope.rb ADDED
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'mongoid'
5
+ require 'mongoid/archivable'
6
+ require 'benchmark'
7
+
8
+ Mongoid.configure do |config|
9
+ config.connect_to('my_little_test')
10
+ end
11
+
12
+ class Model
13
+ include Mongoid::Document
14
+ field :text, type: String
15
+
16
+ index({ text: 'text' })
17
+ end
18
+
19
+ class ArchivableModel
20
+ include Mongoid::Document
21
+ include Mongoid::Archivable
22
+ field :text, type: String
23
+
24
+ index({ text: 'text' })
25
+ end
26
+
27
+ class MetaArchivableModel
28
+ include Mongoid::Document
29
+ field :text, type: String
30
+ field :archived_at, type: Time
31
+ default_scope -> { where(archived_at: nil) }
32
+
33
+ index({ text: 'text' })
34
+ end
35
+
36
+ if ENV['FORCE']
37
+ Mongoid.purge!
38
+ ::Mongoid::Tasks::Database.create_indexes
39
+
40
+ n = 50_000
41
+ n.times {|_i| Model.create(text: "text #{i}") }
42
+ n.times {|_i| ArchivableModel.create(text: "text #{i}") }
43
+ n.times {|_i| MetaArchivableModel.create(text: "text #{i}") }
44
+ end
45
+
46
+ n = 100
47
+
48
+ puts 'text_search benchmark ***'
49
+ Benchmark.bm(20) do |x|
50
+ x.report('without') { n.times { Model.text_search('text').execute } }
51
+ x.report('with') { n.times { ArchivableModel.text_search('text').execute } }
52
+ x.report('meta') { n.times { MetaArchivableModel.text_search('text').execute } }
53
+ x.report('unscoped meta') { n.times { MetaArchivableModel.unscoped.text_search('text').execute } }
54
+ x.report('unscoped archivable') { n.times { ArchivableModel.unscoped.text_search('text').execute } }
55
+ end
56
+
57
+ puts ''
58
+ puts 'Pluck all ids benchmark ***'
59
+ Benchmark.bm(20) do |x|
60
+ x.report('without') { n.times { Model.all.pluck(:id) } }
61
+ x.report('with') { n.times { ArchivableModel.all.pluck(:id) } }
62
+ x.report('meta') { n.times { MetaArchivableModel.all.pluck(:id) } }
63
+ x.report('unscoped meta') { n.times { MetaArchivableModel.unscoped.all.pluck(:id) } }
64
+ x.report('unscoped archivable') { n.times { ArchivableModel.unscoped.all.pluck(:id) } }
65
+ end
@@ -0,0 +1,71 @@
1
+ class Address
2
+ include Mongoid::Document
3
+
4
+ field :_id, type: String, default: ->{ street.try(:parameterize) }
5
+
6
+ attr_accessor :mode
7
+
8
+ field :address_type
9
+ field :number, type: Integer
10
+ field :street
11
+ field :city
12
+ field :state
13
+ field :post_code
14
+ field :parent_title
15
+ field :services, type: Array
16
+ field :latlng, type: Array
17
+ field :map, type: Hash
18
+ field :move_in, type: DateTime
19
+ field :s, type: String, as: :suite
20
+ field :name, localize: true
21
+
22
+ embeds_one :code, validate: false
23
+ embeds_one :target, as: :targetable, validate: false
24
+
25
+ embedded_in :addressable, polymorphic: true do
26
+ def extension
27
+ 'Testing'
28
+ end
29
+ def doctor?
30
+ title == 'Dr'
31
+ end
32
+ end
33
+
34
+ accepts_nested_attributes_for :code, :target
35
+
36
+ belongs_to :account
37
+
38
+ scope :without_postcode, -> {where(postcode: nil)}
39
+ scope :rodeo, -> {
40
+ where(street: 'Rodeo Dr') do
41
+ def mansion?
42
+ all? { |address| address.street == 'Rodeo Dr' }
43
+ end
44
+ end
45
+ }
46
+
47
+ validates_presence_of :street, on: :update
48
+ validates_format_of :street, with: /\D/, allow_nil: true
49
+
50
+ def set_parent=(set = false)
51
+ self.parent_title = addressable.title if set
52
+ end
53
+
54
+ def <=>(other)
55
+ street <=> other.street
56
+ end
57
+
58
+ class << self
59
+ def california
60
+ where(state: 'CA')
61
+ end
62
+
63
+ def homes
64
+ where(address_type: 'Home')
65
+ end
66
+
67
+ def streets
68
+ all.map(&:street)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,7 @@
1
+ class Appointment
2
+ include Mongoid::Document
3
+ field :active, type: Boolean, default: true
4
+ field :timed, type: Boolean, default: true
5
+ embedded_in :person
6
+ default_scope ->{where(active: true)}
7
+ end
@@ -0,0 +1,25 @@
1
+ class ArchivablePhone
2
+ include Mongoid::Document
3
+ include Mongoid::Archivable
4
+
5
+ attr_accessor :after_archive_called, :before_archive_called
6
+
7
+ field :number, type: String
8
+
9
+ embedded_in :person
10
+
11
+ before_archive :before_archive_stub, :halt_me
12
+ after_archive :after_archive_stub
13
+
14
+ def before_archive_stub
15
+ self.before_archive_called = true
16
+ end
17
+
18
+ def after_archive_stub
19
+ self.after_archive_called = true
20
+ end
21
+
22
+ def halt_me
23
+ throw :abort if person.age == 42
24
+ end
25
+ end
@@ -0,0 +1,66 @@
1
+ class ArchivablePost
2
+ include Mongoid::Document
3
+ include Mongoid::Archivable
4
+
5
+ field :title, type: String
6
+
7
+ attr_accessor :after_archive_called, :before_archive_called,
8
+ :after_restore_called, :before_restore_called,
9
+ :after_archive_called, :before_archive_called,
10
+ :around_before_restore_called, :around_after_restore_called
11
+
12
+ belongs_to :person
13
+
14
+ has_and_belongs_to_many :tags
15
+ has_many :authors, dependent: :delete_all, inverse_of: :post
16
+ has_many :titles, dependent: :restrict_with_error
17
+ has_one :fish, dependent: :archive
18
+
19
+ scope :recent, -> { where(created_at: { '$lt' => Time.now, '$gt' => 30.days.ago }) }
20
+
21
+ before_archive :before_archive_stub
22
+ after_archive :after_archive_stub
23
+
24
+ before_archive :before_archive_stub
25
+ after_archive :after_archive_stub
26
+
27
+ before_restore :before_restore_stub
28
+ after_restore :after_restore_stub
29
+ around_restore :around_restore_stub
30
+
31
+ def before_archive_stub
32
+ self.before_archive_called = true
33
+ end
34
+
35
+ def after_archive_stub
36
+ self.after_archive_called = true
37
+ end
38
+
39
+ def before_archive_stub
40
+ self.before_archive_called = true
41
+ end
42
+
43
+ def after_archive_stub
44
+ self.after_archive_called = true
45
+ end
46
+
47
+ def before_restore_stub
48
+ self.before_restore_called = true
49
+ end
50
+
51
+ def after_restore_stub
52
+ self.after_restore_called = true
53
+ end
54
+
55
+ def around_restore_stub
56
+ self.around_before_restore_called = true
57
+ yield
58
+ self.around_after_restore_called = true
59
+ end
60
+
61
+ class << self
62
+ def old
63
+ where(created_at: { '$lt' => 30.days.ago })
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,7 @@
1
+ class Author
2
+ include Mongoid::Document
3
+
4
+ field :name, type: String
5
+
6
+ belongs_to :post, class_name: 'ArchivablePost'
7
+ end
@@ -0,0 +1,10 @@
1
+ class Fish
2
+ include Mongoid::Document
3
+ include Mongoid::Archivable
4
+
5
+ def self.fresh
6
+ where(fresh: true)
7
+ end
8
+
9
+ belongs_to :post, class_name: 'ArchivablePost'
10
+ end
@@ -0,0 +1,21 @@
1
+ class Person
2
+ include Mongoid::Document
3
+
4
+ field :age, type: Integer, default: '100'
5
+ field :score, type: Integer
6
+
7
+ attr_reader :rescored
8
+
9
+ embeds_many :phone_numbers, class_name: 'Phone', validate: false
10
+ embeds_many :phones, store_as: :mobile_phones, validate: false
11
+ embeds_many :addresses, as: :addressable, validate: false
12
+
13
+ embeds_many :appointments, validate: false
14
+ embeds_many :archivable_phones, validate: false
15
+
16
+ has_many :archivable_posts, validate: false
17
+ belongs_to :archivable_post
18
+
19
+ accepts_nested_attributes_for :addresses
20
+ accepts_nested_attributes_for :archivable_phones
21
+ end
@@ -0,0 +1,11 @@
1
+ class Phone
2
+ include Mongoid::Document
3
+
4
+ attr_accessor :number_in_observer
5
+
6
+ field :_id, type: String, default: ->{ number }
7
+
8
+ field :number
9
+ embeds_one :country_code
10
+ embedded_in :person
11
+ end
@@ -0,0 +1,246 @@
1
+ class NormBase
2
+ include Mongoid::Document
3
+
4
+ has_one :norm_has_one, dependent: :destroy
5
+ has_one :arch_has_one, dependent: :archive
6
+
7
+ has_many :norm_has_many, dependent: :destroy
8
+ has_many :arch_has_many, dependent: :archive
9
+
10
+ has_many :norm_has_many_poly, dependent: :destroy
11
+ has_many :arch_has_many_poly, dependent: :archive
12
+
13
+ belongs_to :norm_belongs_to_one, dependent: :destroy
14
+ belongs_to :arch_belongs_to_one, dependent: :archive
15
+
16
+ belongs_to :norm_belongs_to, dependent: :destroy
17
+ belongs_to :arch_belongs_to, dependent: :archive
18
+
19
+ has_and_belongs_to_many :norm_habtm, dependent: :destroy
20
+ has_and_belongs_to_many :arch_habtm, dependent: :archive
21
+
22
+ embeds_one :norm_embeds_one
23
+ embeds_one :arch_embeds_one
24
+
25
+ embeds_many :norm_embeds_many
26
+ embeds_many :arch_embeds_many
27
+
28
+ embeds_many :norm_embeds_many_poly
29
+ embeds_many :arch_embeds_many_poly
30
+ end
31
+
32
+ class ArchBase
33
+ include Mongoid::Document
34
+ include Mongoid::Archivable
35
+
36
+ has_one :norm_has_one, dependent: :destroy
37
+ has_one :arch_has_one, dependent: :archive
38
+
39
+ has_many :norm_has_many, dependent: :destroy
40
+ has_many :arch_has_many, dependent: :archive
41
+
42
+ has_many :norm_has_many_poly, dependent: :destroy
43
+ has_many :arch_has_many_poly, dependent: :archive
44
+
45
+ belongs_to :norm_belongs_to_one, dependent: :destroy
46
+ belongs_to :arch_belongs_to_one, dependent: :archive
47
+
48
+ belongs_to :norm_belongs_to, dependent: :destroy
49
+ belongs_to :arch_belongs_to, dependent: :archive
50
+
51
+ has_and_belongs_to_many :norm_habtm, dependent: :destroy
52
+ has_and_belongs_to_many :arch_habtm, dependent: :archive
53
+
54
+ embeds_one :norm_embeds_one
55
+ embeds_one :arch_embeds_one
56
+
57
+ embeds_many :norm_embeds_many
58
+ embeds_many :arch_embeds_many
59
+
60
+ embeds_many :norm_embeds_many_poly
61
+ embeds_many :arch_embeds_many_poly
62
+ end
63
+
64
+ class NormHasOne
65
+ include Mongoid::Document
66
+
67
+ belongs_to :norm_base
68
+ belongs_to :arch_base
69
+
70
+ has_one :norm_belongs_to, dependent: :destroy
71
+ has_one :arch_belongs_to, dependent: :archive
72
+
73
+ has_one :norm_habtm, dependent: :destroy
74
+ has_one :norm_habtm, dependent: :destroy
75
+ end
76
+
77
+ class NormHasMany
78
+ include Mongoid::Document
79
+
80
+ belongs_to :norm_base
81
+ belongs_to :arch_base
82
+
83
+ has_many :norm_belongs_to, dependent: :destroy
84
+ has_many :arch_belongs_to, dependent: :archive
85
+
86
+ has_many :norm_habtm, dependent: :destroy
87
+ has_many :norm_habtm, dependent: :destroy
88
+ end
89
+
90
+ class NormHasManyPoly
91
+ include Mongoid::Document
92
+
93
+ belongs_to :base, polymorphic: true
94
+ end
95
+
96
+ class NormBelongsToOne
97
+ include Mongoid::Document
98
+
99
+ has_one :norm_base
100
+ has_one :arch_base
101
+ end
102
+
103
+ class NormBelongsTo
104
+ include Mongoid::Document
105
+
106
+ has_many :norm_base
107
+ has_many :arch_base
108
+
109
+ belongs_to :norm_has_one, dependent: :destroy
110
+ belongs_to :arch_has_one, dependent: :archive
111
+
112
+ belongs_to :norm_has_many, dependent: :destroy
113
+ belongs_to :arch_has_many, dependent: :archive
114
+ end
115
+
116
+ class NormHabtm
117
+ include Mongoid::Document
118
+
119
+ has_and_belongs_to_many :norm_base
120
+ has_and_belongs_to_many :arch_base
121
+
122
+ belongs_to :norm_has_one, dependent: :destroy
123
+ belongs_to :arch_has_one, dependent: :archive
124
+
125
+ belongs_to :norm_has_many, dependent: :destroy
126
+ belongs_to :arch_has_many, dependent: :archive
127
+
128
+ has_and_belongs_to_many :recursive, class_name: 'NormHabtm', inverse_of: :recursive, dependent: :archive
129
+ has_and_belongs_to_many :arch_habtm, dependent: :archive
130
+ end
131
+
132
+ class NormEmbedsOne
133
+ include Mongoid::Document
134
+
135
+ embedded_in :norm_base
136
+ embedded_in :arch_base
137
+ end
138
+
139
+ class NormEmbedsMany
140
+ include Mongoid::Document
141
+
142
+ embedded_in :norm_base
143
+ embedded_in :arch_base
144
+ end
145
+
146
+ class NormEmbedsManyPoly
147
+ include Mongoid::Document
148
+
149
+ embedded_in :base, polymorphic: true
150
+ end
151
+
152
+ class ArchHasOne
153
+ include Mongoid::Document
154
+ include Mongoid::Archivable
155
+
156
+ belongs_to :norm_base
157
+ belongs_to :arch_base
158
+
159
+ has_one :norm_belongs_to, dependent: :destroy
160
+ has_one :arch_belongs_to, dependent: :archive
161
+
162
+ has_one :norm_habtm, dependent: :destroy
163
+ has_one :norm_habtm, dependent: :destroy
164
+ end
165
+
166
+ class ArchHasMany
167
+ include Mongoid::Document
168
+ include Mongoid::Archivable
169
+
170
+ belongs_to :norm_base
171
+ belongs_to :arch_base
172
+
173
+ has_many :norm_belongs_to, dependent: :destroy
174
+ has_many :arch_belongs_to, dependent: :archive
175
+
176
+ has_many :norm_habtm, dependent: :destroy
177
+ has_many :norm_habtm, dependent: :destroy
178
+ end
179
+
180
+ class ArchHasManyPoly
181
+ include Mongoid::Document
182
+ include Mongoid::Archivable
183
+
184
+ belongs_to :base, polymorphic: true
185
+ end
186
+
187
+ class ArchBelongsToOne
188
+ include Mongoid::Document
189
+ include Mongoid::Archivable
190
+
191
+ has_one :norm_base
192
+ has_one :arch_base
193
+ end
194
+
195
+ class ArchBelongsTo
196
+ include Mongoid::Document
197
+ include Mongoid::Archivable
198
+
199
+ has_many :norm_base
200
+ has_many :arch_base
201
+
202
+ belongs_to :norm_has_one, dependent: :destroy
203
+ belongs_to :arch_has_one, dependent: :archive
204
+
205
+ belongs_to :norm_has_many, dependent: :destroy
206
+ belongs_to :arch_has_many, dependent: :archive
207
+ end
208
+
209
+ class ArchHabtm
210
+ include Mongoid::Document
211
+ include Mongoid::Archivable
212
+
213
+ has_and_belongs_to_many :norm_base
214
+ has_and_belongs_to_many :arch_base
215
+
216
+ belongs_to :norm_has_one, dependent: :destroy
217
+ belongs_to :arch_has_one, dependent: :archive
218
+
219
+ belongs_to :norm_has_many, dependent: :destroy
220
+ belongs_to :arch_has_many, dependent: :archive
221
+
222
+ has_and_belongs_to_many :norm_habtm, dependent: :destroy
223
+ end
224
+
225
+ class ArchEmbedsOne
226
+ include Mongoid::Document
227
+ include Mongoid::Archivable
228
+
229
+ embedded_in :norm_base
230
+ embedded_in :arch_base
231
+ end
232
+
233
+ class ArchEmbedsMany
234
+ include Mongoid::Document
235
+ include Mongoid::Archivable
236
+
237
+ embedded_in :norm_base
238
+ embedded_in :arch_base
239
+ end
240
+
241
+ class ArchEmbedsManyPoly
242
+ include Mongoid::Document
243
+ include Mongoid::Archivable
244
+
245
+ embedded_in :base, polymorphic: true
246
+ end