mongoid-versioning 0.2.0 → 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +2 -1
- data/README.md +2 -1
- data/Rakefile +3 -5
- data/gemfiles/mongoid_master.gemfile +8 -0
- data/lib/mongoid/config/locales/en.yml +11 -0
- data/lib/mongoid/core_ext/errors/versioning_not_on_root.rb +23 -0
- data/lib/mongoid/core_ext/fields/standard.rb +17 -0
- data/lib/mongoid/core_ext/fields/validators/macro.rb +11 -0
- data/lib/mongoid/core_ext/hierarchy.rb +25 -0
- data/lib/mongoid/core_ext/relations/bindings/embedded/many.rb +31 -0
- data/lib/mongoid/core_ext/relations/cascading.rb +23 -0
- data/lib/mongoid/core_ext/relations/embedded/batchable.rb +36 -0
- data/lib/mongoid/core_ext/relations/embedded/many.rb +24 -0
- data/lib/mongoid/core_ext/relations/macros.rb +39 -0
- data/lib/mongoid/core_ext/relations/metadata.rb +63 -0
- data/lib/mongoid/core_ext/relations/options.rb +9 -0
- data/lib/mongoid/core_ext/threaded/lifecycle.rb +37 -0
- data/lib/mongoid/core_ext/versioning.rb +218 -0
- data/lib/mongoid/versioning/version.rb +1 -1
- data/lib/mongoid/versioning.rb +16 -213
- data/mongoid-versioning.gemspec +2 -2
- data/spec/app/models/account.rb +0 -4
- data/spec/app/models/acolyte.rb +1 -1
- data/spec/app/models/address.rb +2 -2
- data/spec/app/models/appointment.rb +1 -1
- data/spec/app/models/article.rb +0 -3
- data/spec/app/models/building.rb +0 -2
- data/spec/app/models/building_address.rb +0 -2
- data/spec/app/models/contractor.rb +0 -2
- data/spec/app/models/dog.rb +1 -1
- data/spec/app/models/drug.rb +0 -2
- data/spec/app/models/event.rb +1 -1
- data/spec/app/models/game.rb +0 -2
- data/spec/app/models/house.rb +1 -2
- data/spec/app/models/item.rb +0 -4
- data/spec/app/models/name.rb +0 -2
- data/spec/app/models/paranoid_phone.rb +2 -0
- data/spec/app/models/paranoid_post.rb +3 -1
- data/spec/app/models/person.rb +3 -5
- data/spec/app/models/player.rb +2 -2
- data/spec/app/models/post.rb +2 -2
- data/spec/app/models/preference.rb +1 -1
- data/spec/app/models/quiz.rb +0 -3
- data/spec/app/models/registry.rb +1 -1
- data/spec/app/models/symptom.rb +1 -1
- data/spec/app/models/tree.rb +1 -1
- data/spec/app/models/video.rb +1 -5
- data/spec/app/models/wiki_page.rb +0 -2
- data/spec/config/mongoid.yml +0 -5
- data/spec/mongoid/errors/versioning_not_on_root_spec.rb +29 -0
- data/spec/mongoid/relations/macros_spec.rb +17 -0
- data/spec/mongoid/relations/metadata_spec.rb +50 -0
- data/spec/mongoid/versioning_spec.rb +2 -36
- data/spec/spec_helper.rb +4 -3
- metadata +44 -30
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e909070135c192cc4bbdd0c62416daed6d33d837
|
4
|
+
data.tar.gz: 2481913b87525fa8e554283d395ef52f189319af
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1528b23826c4709c66b7ea37649408040d2eb2a3f0748fb03094459a3f2f1ed887d7234822bab9310c6677122b0581913c3b17b4de04f57fe5e6cd29634e3981
|
7
|
+
data.tar.gz: fffbba7feefc13c2f2298cc23b7e935c9bbca28059705507d908449c1eb0ebcd5b3e69f79f210b571851ac7526ba38e4a3022659df9f348c76a82dea4431f1e3
|
data/.travis.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
language: ruby
|
2
|
+
|
3
|
+
rvm:
|
4
|
+
- 1.9.3
|
5
|
+
- 2.0.0
|
6
|
+
- 2.1.0
|
7
|
+
- ruby-head
|
8
|
+
|
9
|
+
gemfile:
|
10
|
+
- Gemfile
|
11
|
+
- gemfiles/mongoid_master.gemfile
|
12
|
+
|
13
|
+
branches:
|
14
|
+
only:
|
15
|
+
- master
|
16
|
+
|
17
|
+
matrix:
|
18
|
+
allow_failures:
|
19
|
+
- rvm: ruby-head
|
20
|
+
- gemfile: gemfiles/mongoid_master.gemfile
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
# mongoid-versioning
|
1
|
+
# mongoid-versioning [![Build Status](https://travis-ci.org/haihappen/mongoid-versioning.png)](https://travis-ci.org/haihappen/mongoid-versioning)
|
2
|
+
|
2
3
|
**Important:** This gem is an extraction of [Mongoid::Versioning](http://mongoid.org/en/mongoid/docs/extras.html#versioning) from the official [mongoid](http://mongoid.org) gem. Since Mongoid::Versioning will be removed in the upcoming `4.0.0` release of mongoid, this gem re-enables the functionality of versioned documents.
|
3
4
|
|
4
5
|
**Please submit only bug and security fixes**. Neighter I will accept new features nor changes to exiting APIs. Please consider forking the project if you want new features to appear! :)
|
data/Rakefile
CHANGED
@@ -0,0 +1,11 @@
|
|
1
|
+
en:
|
2
|
+
mongoid:
|
3
|
+
errors:
|
4
|
+
messages:
|
5
|
+
versioning_not_on_root:
|
6
|
+
message: "Versioning not allowed on embedded document: %{klass}."
|
7
|
+
summary: "Mongoid::Versioning behaviour is only allowed on documents
|
8
|
+
that are the root document in the hierarchy."
|
9
|
+
resolution: "Remove the versioning from the embedded %{klass} or
|
10
|
+
consider moving it to a root location in the hierarchy if
|
11
|
+
versioning is needed."
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid
|
3
|
+
module Errors
|
4
|
+
|
5
|
+
# This error is raised when attempting to version an embedded document.
|
6
|
+
class VersioningNotOnRoot < MongoidError
|
7
|
+
|
8
|
+
# Create the new error.
|
9
|
+
#
|
10
|
+
# @example Create the new error.
|
11
|
+
# VersioningNotOnRoot.new(Address)
|
12
|
+
#
|
13
|
+
# @param [ Class ] klass The embedded class.
|
14
|
+
#
|
15
|
+
# @since 3.0.0
|
16
|
+
def initialize(klass)
|
17
|
+
super(
|
18
|
+
compose_message("versioning_not_on_root", { klass: klass })
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Fields
|
3
|
+
class Standard
|
4
|
+
# Is this field included in versioned attributes?
|
5
|
+
#
|
6
|
+
# @example Is the field versioned?
|
7
|
+
# field.versioned?
|
8
|
+
#
|
9
|
+
# @return [ true, false ] If the field is included in versioning.
|
10
|
+
#
|
11
|
+
# @since 2.1.0
|
12
|
+
def versioned?
|
13
|
+
@versioned ||= (options[:versioned].nil? ? true : options[:versioned])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Hierarchy
|
3
|
+
# Collect all the children of this document.
|
4
|
+
#
|
5
|
+
# @example Collect all the children.
|
6
|
+
# document.collect_children
|
7
|
+
#
|
8
|
+
# @return [ Array<Document> ] The children.
|
9
|
+
#
|
10
|
+
# @since 2.4.0
|
11
|
+
def collect_children
|
12
|
+
children = []
|
13
|
+
embedded_relations.each_pair do |name, metadata|
|
14
|
+
without_autobuild do
|
15
|
+
child = send(name)
|
16
|
+
Array.wrap(child).each do |doc|
|
17
|
+
children.push(doc)
|
18
|
+
children.concat(doc._children) unless metadata.versioned?
|
19
|
+
end if child
|
20
|
+
end
|
21
|
+
end
|
22
|
+
children
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Relations
|
3
|
+
module Bindings
|
4
|
+
module Embedded
|
5
|
+
class Many < Binding
|
6
|
+
# Binds a single document with the inverse relation. Used
|
7
|
+
# specifically when appending to the proxy.
|
8
|
+
#
|
9
|
+
# @example Bind one document.
|
10
|
+
# person.addresses.bind_one(address)
|
11
|
+
#
|
12
|
+
# @param [ Document ] doc The single document to bind.
|
13
|
+
# @param [ Hash ] options The binding options.
|
14
|
+
#
|
15
|
+
# @option options [ true, false ] :continue Continue binding the inverse.
|
16
|
+
# @option options [ true, false ] :binding Are we in build mode?
|
17
|
+
#
|
18
|
+
# @since 2.0.0.rc.1
|
19
|
+
def bind_one(doc)
|
20
|
+
doc.parentize(base)
|
21
|
+
binding do
|
22
|
+
unless metadata.versioned?
|
23
|
+
doc.do_or_do_not(metadata.inverse_setter(target), base)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Relations
|
3
|
+
module Cascading
|
4
|
+
# Perform all cascading deletes, destroys, or nullifies. Will delegate to
|
5
|
+
# the appropriate strategy to perform the operation.
|
6
|
+
#
|
7
|
+
# @example Execute cascades.
|
8
|
+
# document.cascade!
|
9
|
+
#
|
10
|
+
# @since 2.0.0.rc.1
|
11
|
+
def cascade!
|
12
|
+
cascades.each do |name|
|
13
|
+
if !relation_metadata || !relation_metadata.versioned?
|
14
|
+
if meta = relations[name]
|
15
|
+
strategy = meta.cascade_strategy
|
16
|
+
strategy.new(self, meta).cascade if strategy
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Relations
|
3
|
+
module Embedded
|
4
|
+
module Batchable
|
5
|
+
# Pre process the batch removal.
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
#
|
9
|
+
# @example Pre process the documents.
|
10
|
+
# batchable.pre_process_batch_remove(docs, :delete)
|
11
|
+
#
|
12
|
+
# @param [ Array<Document> ] docs The documents.
|
13
|
+
# @param [ Symbol ] method Delete or destroy.
|
14
|
+
#
|
15
|
+
# @return [ Array<Hash> ] The documents as hashes.
|
16
|
+
#
|
17
|
+
# @since 3.0.0
|
18
|
+
def pre_process_batch_remove(docs, method)
|
19
|
+
docs.map do |doc|
|
20
|
+
self.path = doc.atomic_path unless path
|
21
|
+
execute_callback :before_remove, doc
|
22
|
+
if !_assigning? && !metadata.versioned?
|
23
|
+
doc.cascade!
|
24
|
+
doc.run_before_callbacks(:destroy) if method == :destroy
|
25
|
+
end
|
26
|
+
target.delete_one(doc)
|
27
|
+
_unscoped.delete_one(doc)
|
28
|
+
unbind_one(doc)
|
29
|
+
execute_callback :after_remove, doc
|
30
|
+
doc.as_document
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Relations
|
3
|
+
module Embedded
|
4
|
+
class Many < Relations::Many
|
5
|
+
class << self
|
6
|
+
# Get the valid options allowed with this relation.
|
7
|
+
#
|
8
|
+
# @example Get the valid options.
|
9
|
+
# Relation.valid_options
|
10
|
+
#
|
11
|
+
# @return [ Array<Symbol> ] The valid options.
|
12
|
+
#
|
13
|
+
# @since 2.1.0
|
14
|
+
def valid_options
|
15
|
+
[
|
16
|
+
:as, :cascade_callbacks, :cyclic, :order, :versioned, :store_as,
|
17
|
+
:before_add, :after_add, :before_remove, :after_remove
|
18
|
+
]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Relations
|
3
|
+
module Macros
|
4
|
+
module ClassMethods
|
5
|
+
# Adds the relation back to the parent document. This macro is
|
6
|
+
# necessary to set the references from the child back to the parent
|
7
|
+
# document. If a child does not define this relation calling
|
8
|
+
# persistence methods on the child object will cause a save to fail.
|
9
|
+
#
|
10
|
+
# @example Define the relation.
|
11
|
+
#
|
12
|
+
# class Person
|
13
|
+
# include Mongoid::Document
|
14
|
+
# embeds_many :addresses
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# class Address
|
18
|
+
# include Mongoid::Document
|
19
|
+
# embedded_in :person
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# @param [ Symbol ] name The name of the relation.
|
23
|
+
# @param [ Hash ] options The relation options.
|
24
|
+
# @param [ Proc ] block Optional block for defining extensions.
|
25
|
+
def embedded_in(name, options = {}, &block)
|
26
|
+
if ancestors.include?(Mongoid::Versioning)
|
27
|
+
raise Errors::VersioningNotOnRoot.new(self)
|
28
|
+
end
|
29
|
+
meta = characterize(name, Embedded::In, options, &block)
|
30
|
+
self.embedded = true
|
31
|
+
relate(name, meta)
|
32
|
+
builder(name, meta).creator(name, meta)
|
33
|
+
add_counter_cache_callbacks(meta) if meta.counter_cached?
|
34
|
+
meta
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Relations
|
3
|
+
class Metadata < Hash
|
4
|
+
# Since a lot of the information from the metadata is inferred and not
|
5
|
+
# explicitly stored in the hash, the inspection needs to be much more
|
6
|
+
# detailed.
|
7
|
+
#
|
8
|
+
# @example Inspect the metadata.
|
9
|
+
# metadata.inspect
|
10
|
+
#
|
11
|
+
# @return [ String ] Oodles of information in a nice format.
|
12
|
+
#
|
13
|
+
# @since 2.0.0.rc.1
|
14
|
+
def inspect
|
15
|
+
%Q{#<Mongoid::Relations::Metadata
|
16
|
+
autobuild: #{autobuilding?}
|
17
|
+
class_name: #{class_name}
|
18
|
+
cyclic: #{cyclic.inspect}
|
19
|
+
counter_cache:#{counter_cached?}
|
20
|
+
dependent: #{dependent.inspect}
|
21
|
+
inverse_of: #{inverse_of.inspect}
|
22
|
+
key: #{key}
|
23
|
+
macro: #{macro}
|
24
|
+
name: #{name}
|
25
|
+
order: #{order.inspect}
|
26
|
+
polymorphic: #{polymorphic?}
|
27
|
+
relation: #{relation}
|
28
|
+
setter: #{setter}
|
29
|
+
versioned: #{versioned?}>
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Is this relation using Mongoid's internal versioning system?
|
34
|
+
#
|
35
|
+
# @example Is this relation versioned?
|
36
|
+
# metadata.versioned?
|
37
|
+
#
|
38
|
+
# @return [ true, false ] If the relation uses Mongoid versioning.
|
39
|
+
#
|
40
|
+
# @since 2.1.0
|
41
|
+
def versioned?
|
42
|
+
!!self[:versioned]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Get the inverse relation candidates.
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
#
|
49
|
+
# @example Get the inverse relation candidates.
|
50
|
+
# metadata.inverse_relation_candidates
|
51
|
+
#
|
52
|
+
# @return [ Array<Metdata> ] The candidates.
|
53
|
+
#
|
54
|
+
# @since 3.0.0
|
55
|
+
def inverse_relation_candidates
|
56
|
+
relations_metadata.select do |meta|
|
57
|
+
next if meta.versioned? || meta.name == name
|
58
|
+
meta.class_name == inverse_class_name
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Threaded
|
3
|
+
module Lifecycle
|
4
|
+
private
|
5
|
+
# Execute a block in loading revision mode.
|
6
|
+
#
|
7
|
+
# @example Execute in loading revision mode.
|
8
|
+
# _loading_revision do
|
9
|
+
# load_revision
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# @return [ Object ] The return value of the block.
|
13
|
+
#
|
14
|
+
# @since 2.3.4
|
15
|
+
def _loading_revision
|
16
|
+
Threaded.begin_execution("load_revision")
|
17
|
+
yield
|
18
|
+
ensure
|
19
|
+
Threaded.exit_execution("load_revision")
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
# Is the current thread in loading revision mode?
|
24
|
+
#
|
25
|
+
# @example Is the current thread in loading revision mode?
|
26
|
+
# proxy._loading_revision?
|
27
|
+
#
|
28
|
+
# @return [ true, false ] If the thread is loading a revision.
|
29
|
+
#
|
30
|
+
# @since 2.3.4
|
31
|
+
def _loading_revision?
|
32
|
+
Threaded.executing?("load_revision")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid
|
3
|
+
|
4
|
+
# Include this module to get automatic versioning of root level documents.
|
5
|
+
# This will add a version field to the +Document+ and a has_many association
|
6
|
+
# with all the versions contained in it.
|
7
|
+
module Versioning
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
field :version, type: Integer, default: 1
|
12
|
+
|
13
|
+
embeds_many \
|
14
|
+
:versions,
|
15
|
+
class_name: self.name,
|
16
|
+
validate: false,
|
17
|
+
cyclic: true,
|
18
|
+
inverse_of: nil,
|
19
|
+
versioned: true
|
20
|
+
|
21
|
+
set_callback :save, :before, :revise, if: :revisable?
|
22
|
+
|
23
|
+
class_attribute :version_max
|
24
|
+
self.cyclic = true
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create a new version of the +Document+. This will load the previous
|
28
|
+
# document from the database and set it as the next version before saving
|
29
|
+
# the current document. It then increments the version number. If a #max_versions
|
30
|
+
# limit is set in the model and it's exceeded, the oldest version gets discarded.
|
31
|
+
#
|
32
|
+
# @example Revise the document.
|
33
|
+
# person.revise
|
34
|
+
#
|
35
|
+
# @since 1.0.0
|
36
|
+
def revise
|
37
|
+
previous = previous_revision
|
38
|
+
if previous && versioned_attributes_changed?
|
39
|
+
new_version = versions.build(
|
40
|
+
previous.versioned_attributes
|
41
|
+
)
|
42
|
+
new_version._id = nil
|
43
|
+
if version_max.present? && versions.length > version_max
|
44
|
+
deleted = versions.first
|
45
|
+
if deleted.respond_to?(:paranoid?) && deleted.paranoid?
|
46
|
+
versions.delete_one(deleted)
|
47
|
+
collection.find(atomic_selector).
|
48
|
+
update({ "$pull" => { "versions" => { "version" => deleted.version }}})
|
49
|
+
else
|
50
|
+
versions.delete(deleted)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
self.version = (version || 1 ) + 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Forces the creation of a new version of the +Document+, regardless of
|
58
|
+
# whether a change was actually made.
|
59
|
+
#
|
60
|
+
# @example Revise the document.
|
61
|
+
# person.revise!
|
62
|
+
#
|
63
|
+
# @since 2.2.1
|
64
|
+
def revise!
|
65
|
+
versions.build(
|
66
|
+
(previous_revision || self).versioned_attributes
|
67
|
+
)
|
68
|
+
versions.shift if version_max.present? && versions.length > version_max
|
69
|
+
self.version = (version || 1 ) + 1
|
70
|
+
save
|
71
|
+
end
|
72
|
+
|
73
|
+
# Filters the results of +changes+ by removing any fields that should
|
74
|
+
# not be versioned.
|
75
|
+
#
|
76
|
+
# @return [ Hash ] A hash of versioned changed attributes.
|
77
|
+
#
|
78
|
+
# @since 2.1.0
|
79
|
+
def versioned_changes
|
80
|
+
only_versioned_attributes(changes.except("updated_at"))
|
81
|
+
end
|
82
|
+
|
83
|
+
# Filters the results of +attributes+ by removing any fields that should
|
84
|
+
# not be versioned.
|
85
|
+
#
|
86
|
+
# @return [ Hash ] A hash of versioned attributes.
|
87
|
+
#
|
88
|
+
# @since 2.1.0
|
89
|
+
def versioned_attributes
|
90
|
+
only_versioned_attributes(attributes)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Check if any versioned fields have been modified. This is similar
|
94
|
+
# to +changed?+, except this method also ignores fields set to be
|
95
|
+
# ignored by versioning.
|
96
|
+
#
|
97
|
+
# @return [ Boolean ] Whether fields that will be versioned have changed.
|
98
|
+
#
|
99
|
+
# @since 2.1.0
|
100
|
+
def versioned_attributes_changed?
|
101
|
+
!versioned_changes.empty?
|
102
|
+
end
|
103
|
+
|
104
|
+
# Executes a block that temporarily disables versioning. This is for cases
|
105
|
+
# where you do not want to version on every save.
|
106
|
+
#
|
107
|
+
# @example Execute a save without versioning.
|
108
|
+
# person.versionless(&:save)
|
109
|
+
#
|
110
|
+
# @return [ Object ] The document or result of the block execution.
|
111
|
+
#
|
112
|
+
# @since 2.0.0
|
113
|
+
def versionless
|
114
|
+
@versionless = true
|
115
|
+
result = yield(self) if block_given?
|
116
|
+
@versionless = false
|
117
|
+
result || self
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
# Find the previous version of this document in the database, or if the
|
123
|
+
# document had been saved without versioning return the persisted one.
|
124
|
+
#
|
125
|
+
# @example Find the last version.
|
126
|
+
# document.find_last_version
|
127
|
+
#
|
128
|
+
# @return [ Document, nil ] The previously saved document.
|
129
|
+
#
|
130
|
+
# @since 2.0.0
|
131
|
+
def previous_revision
|
132
|
+
_loading_revision do
|
133
|
+
self.class.unscoped.
|
134
|
+
with(self.mongo_session.options).
|
135
|
+
where(_id: id).
|
136
|
+
any_of({ version: version }, { version: nil }).first
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Is the document able to be revised? This is true if the document has
|
141
|
+
# changed and we have not explicitly told it not to version.
|
142
|
+
#
|
143
|
+
# @example Is the document revisable?
|
144
|
+
# document.revisable?
|
145
|
+
#
|
146
|
+
# @return [ true, false ] If the document is revisable.
|
147
|
+
#
|
148
|
+
# @since 2.0.0
|
149
|
+
def revisable?
|
150
|
+
versioned_attributes_changed? && !versionless?
|
151
|
+
end
|
152
|
+
|
153
|
+
# Are we in versionless mode? This is true if in a versionless block on the
|
154
|
+
# document.
|
155
|
+
#
|
156
|
+
# @example Is the document in versionless mode?
|
157
|
+
# document.versionless?
|
158
|
+
#
|
159
|
+
# @return [ true, false ] Is the document not currently versioning.
|
160
|
+
#
|
161
|
+
# @since 2.0.0
|
162
|
+
def versionless?
|
163
|
+
@versionless ||= false
|
164
|
+
end
|
165
|
+
|
166
|
+
# Filters fields that should not be versioned out of an attributes hash.
|
167
|
+
# Dynamic attributes are always versioned.
|
168
|
+
#
|
169
|
+
# @param [ Hash ] A hash with field names as keys.
|
170
|
+
#
|
171
|
+
# @return [ Hash ] The hash without non-versioned columns.
|
172
|
+
#
|
173
|
+
# @since 2.1.0
|
174
|
+
def only_versioned_attributes(hash)
|
175
|
+
versioned = {}
|
176
|
+
hash.except("versions").each_pair do |name, value|
|
177
|
+
add_versioned_attribute(versioned, name, value)
|
178
|
+
end
|
179
|
+
versioned
|
180
|
+
end
|
181
|
+
|
182
|
+
# Add the versioned attribute. Will work now for localized fields.
|
183
|
+
#
|
184
|
+
# @api private
|
185
|
+
#
|
186
|
+
# @example Add the versioned attribute.
|
187
|
+
# model.add_versioned_attribute({}, "name", "test")
|
188
|
+
#
|
189
|
+
# @param [ Hash ] versioned The versioned attributes.
|
190
|
+
# @param [ String ] name The name of the field.
|
191
|
+
# @param [ Object ] value The value for the field.
|
192
|
+
#
|
193
|
+
# @since 3.0.10
|
194
|
+
def add_versioned_attribute(versioned, name, value)
|
195
|
+
field = fields[name]
|
196
|
+
if field && field.localized?
|
197
|
+
versioned["#{name}_translations"] = value
|
198
|
+
else
|
199
|
+
versioned[name] = value if !field || field.versioned?
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
module ClassMethods
|
204
|
+
|
205
|
+
# Sets the maximum number of versions to store.
|
206
|
+
#
|
207
|
+
# @example Set the maximum.
|
208
|
+
# Person.max_versions(5)
|
209
|
+
#
|
210
|
+
# @param [ Integer ] number The maximum number to store.
|
211
|
+
#
|
212
|
+
# @return [ Integer ] The max number of versions.
|
213
|
+
def max_versions(number)
|
214
|
+
self.version_max = number.to_i
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|