memories 0.2.1 → 0.2.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.
- data/README.markdown +28 -0
- data/README.rdoc +111 -0
- data/lib/memories/annotation.rb +1 -1
- data/lib/memories/attachment.rb +1 -1
- data/lib/memories/base.rb +70 -5
- metadata +7 -5
data/README.markdown
CHANGED
@@ -34,6 +34,34 @@ Here's how basic versioning works. Every time you save your document, you get a
|
|
34
34
|
b.name #==> "2001"
|
35
35
|
b.current_version #==> 3
|
36
36
|
|
37
|
+
If you'd like to exclude certain properties from versioning, use the #forget class method:
|
38
|
+
|
39
|
+
class Book < CouchRest::Model::Base
|
40
|
+
include Memories
|
41
|
+
use_database SOME_DATABASE
|
42
|
+
|
43
|
+
forget :notes
|
44
|
+
|
45
|
+
property :name
|
46
|
+
property :notes
|
47
|
+
view_by :name
|
48
|
+
end
|
49
|
+
|
50
|
+
b = Book.create :name => "2001", :notes => "creating the book."
|
51
|
+
b.current_version #==> 1
|
52
|
+
b.name = "2001: A Space Odyssey"
|
53
|
+
b.notes += "updating the title. might ship today. 9/2/2010. MKP"
|
54
|
+
b.save
|
55
|
+
b.current_version #==> 2
|
56
|
+
b.previous_version #==> 1
|
57
|
+
p b.name #==> "2001: A Space Odyssey"
|
58
|
+
p b.notes # ==> "creating the book. updating the title. might ship today. 9/2/2010. MKP"
|
59
|
+
b.revert_to! 1
|
60
|
+
p b.name #==> "2001"
|
61
|
+
p b.notes # ==> "creating the book. updating the title. might ship today. 9/2/2010. MKP"
|
62
|
+
b.current_version #==> 3
|
63
|
+
|
64
|
+
|
37
65
|
###Milestones
|
38
66
|
|
39
67
|
As of version 0.2.0, Memories also supports milestones. Milestones are special versions that you want to flag in some way.
|
data/README.rdoc
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
= Introduction
|
2
|
+
|
3
|
+
A simple gem for adding versioning to your CouchRest::Model::Base documents. When you update a document, the previous version gets
|
4
|
+
stored as an attachment on the document. This versioning strategy was originally created here: http://blog.couch.io/post/632718824/simple-document-versioning-with-couchdb
|
5
|
+
|
6
|
+
== Installation
|
7
|
+
|
8
|
+
$ gem install memories
|
9
|
+
|
10
|
+
== How does it work?
|
11
|
+
|
12
|
+
Just "include Memories" in your "CouchRest::Model::Base" classes and let the auto-versioning begin.
|
13
|
+
|
14
|
+
=== Basic Versioning
|
15
|
+
|
16
|
+
Here's how basic versioning works. Every time you save your document, you get a new version. You have the ability to roll back to a previous version.
|
17
|
+
|
18
|
+
class Book < CouchRest::Model::Base
|
19
|
+
include Memories
|
20
|
+
use_database SOME_DATABASE
|
21
|
+
|
22
|
+
property :name
|
23
|
+
view_by :name
|
24
|
+
end
|
25
|
+
|
26
|
+
b = Book.create :name => "2001"
|
27
|
+
b.current_version #==> 1
|
28
|
+
b.name = "2001: A Space Odyssey"
|
29
|
+
b.save
|
30
|
+
b.current_version #==> 2
|
31
|
+
b.previous_version #==> 1
|
32
|
+
b.name #==> "2001: A Space Odyssey"
|
33
|
+
b.revert_to! 1
|
34
|
+
b.name #==> "2001"
|
35
|
+
b.current_version #==> 3
|
36
|
+
|
37
|
+
If you'd like to exclude certain properties from versioning, use the #forget class method:
|
38
|
+
|
39
|
+
class Book < CouchRest::Model::Base
|
40
|
+
include Memories
|
41
|
+
use_database SOME_DATABASE
|
42
|
+
|
43
|
+
forget :notes
|
44
|
+
|
45
|
+
property :name
|
46
|
+
property :notes
|
47
|
+
view_by :name
|
48
|
+
end
|
49
|
+
|
50
|
+
b = Book.create :name => "2001", :notes => "creating the book."
|
51
|
+
b.current_version #==> 1
|
52
|
+
b.name = "2001: A Space Odyssey"
|
53
|
+
b.notes += "updating the title. might ship today. 9/2/2010. MKP"
|
54
|
+
b.save
|
55
|
+
b.current_version #==> 2
|
56
|
+
b.previous_version #==> 1
|
57
|
+
p b.name #==> "2001: A Space Odyssey"
|
58
|
+
p b.notes # ==> "creating the book. updating the title. might ship today. 9/2/2010. MKP"
|
59
|
+
b.revert_to! 1
|
60
|
+
p b.name #==> "2001"
|
61
|
+
p b.notes # ==> "creating the book. updating the title. might ship today. 9/2/2010. MKP"
|
62
|
+
b.current_version #==> 3
|
63
|
+
|
64
|
+
=== Milestones
|
65
|
+
|
66
|
+
As of version 0.2.0, Memories also supports milestones. Milestones are special versions that you want to flag in some way.
|
67
|
+
For example, suppose you were creating a content management system, and every time someone publishes an article to the website, you want to flag the version
|
68
|
+
they published as a milestone.
|
69
|
+
|
70
|
+
class Article < CouchRest::Model::Base
|
71
|
+
include Memories
|
72
|
+
use_database SOME_DATABASE
|
73
|
+
|
74
|
+
property :title
|
75
|
+
property :author
|
76
|
+
property :body
|
77
|
+
|
78
|
+
def publish!
|
79
|
+
# .... publishing logic
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
a = Article.create(
|
84
|
+
:title => "Memories gem makes versioning simple",
|
85
|
+
:author => "moonmaster9000",
|
86
|
+
:body => <<-ARTICLE
|
87
|
+
Check it out at http://github.com/moonmaster9000/memories
|
88
|
+
ARTICLE
|
89
|
+
)
|
90
|
+
a.save
|
91
|
+
a.publish!
|
92
|
+
a.current_version #==> 1
|
93
|
+
a.milestone! do
|
94
|
+
name "First publish."
|
95
|
+
notes "Passed all relevant editing. Signed off by moonmaster10000"
|
96
|
+
end
|
97
|
+
|
98
|
+
Notice that we annotated our milestone; we gave it a name, and some notes. You can annotate with whatever properties you desire. The annotation do block is entirely optional.
|
99
|
+
Now that we've created a milestone, let's inspect it:
|
100
|
+
|
101
|
+
a.milestones.count #==> 1
|
102
|
+
a.latest_milestone.version # ==> 1
|
103
|
+
a.latest_milestone.annotations.name ==> "First publish."
|
104
|
+
a.latest_milestone.annotations.notes ==> "Passed all relevant editing. Signed off by moonmaster 10000"
|
105
|
+
|
106
|
+
Now, let's imagine that we've made some more edits / saves to the document, but they don't get approved. Now we want to revert to the version the document was
|
107
|
+
at at the first milestone. How do we do that? Simple!
|
108
|
+
|
109
|
+
a.revert_to_milestone! 1
|
110
|
+
|
111
|
+
And now our document properties are back to the where they were when we first published the document.
|
data/lib/memories/annotation.rb
CHANGED
data/lib/memories/attachment.rb
CHANGED
data/lib/memories/base.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# Simply "include Memories" in your CouchRest::Model::Base derived classes to add versioning to your document.
|
1
2
|
module Memories
|
2
3
|
def self.included(base)
|
3
4
|
base.property(:milestone_memories) do |milestone_memory|
|
@@ -7,60 +8,120 @@ module Memories
|
|
7
8
|
|
8
9
|
base.before_update :add_version_attachment
|
9
10
|
base.after_update :decode_attachments
|
11
|
+
base.send :extend, ClassMethods
|
10
12
|
end
|
11
13
|
|
12
|
-
|
14
|
+
module ClassMethods
|
15
|
+
# If you'd like to exclude certain properties from versioning, simply pass those properties
|
16
|
+
# to this method:
|
17
|
+
#
|
18
|
+
# class MyDocument < CouchRest::Model::Base
|
19
|
+
# use_database MY_DATABASE
|
20
|
+
# forget :prop1, :prop2
|
21
|
+
#
|
22
|
+
# property :prop1 #not versioned
|
23
|
+
# property :prop2 #not versioned
|
24
|
+
# property :prop3 #versioned
|
25
|
+
# end
|
26
|
+
def forget(*props)
|
27
|
+
self.forget_properties += props.map {|p| p.to_s}
|
28
|
+
end
|
29
|
+
|
30
|
+
def forget_properties #:nodoc:
|
31
|
+
@forget_properties ||= ["couchrest-type", "_id", "_rev", "_attachments", "milestone_memories"]
|
32
|
+
end
|
13
33
|
|
34
|
+
def forget_properties=(props) #:nodoc:
|
35
|
+
@forget_properties = props
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
VERSION_REGEX = /(?:rev-)?(\d+)-[a-zA-Z0-9]+/
|
40
|
+
|
41
|
+
# Revert the document to a specific version and save.
|
42
|
+
# You can provide either a complete revision number ("1-u54abz3948302sjjej3jej300rj", or "rev-1-u54abz3948302sjjej3jej300rj")
|
43
|
+
# or simply a number (e.g, 1, 4, 100, etc.).
|
44
|
+
# my_doc.revert_to! 3 # ==> would revert your document "my_doc" to version 3.
|
14
45
|
def revert_to!(version)
|
15
46
|
revert version, :hard
|
16
47
|
end
|
17
48
|
|
49
|
+
# Same as #revert_to!, except that it doesn't save.
|
18
50
|
def revert_to(version)
|
19
51
|
revert version
|
20
52
|
end
|
21
53
|
|
54
|
+
# Same as #rollback!, but doesn't save.
|
22
55
|
def rollback
|
23
56
|
self.revert_to self.previous_version
|
24
57
|
end
|
25
58
|
|
59
|
+
# Revert to the previous version, and resave the document. Shortcut for:
|
60
|
+
# my_doc.revert_to! my_doc.previous_version
|
26
61
|
def rollback!
|
27
62
|
self.revert_to! self.previous_version
|
28
63
|
end
|
29
64
|
|
65
|
+
# Revert to a given milestone and save. Milestones are stored in the .milestones array.
|
66
|
+
# Reverting to milestone "n" reverts to milestone represented in the nth element in the
|
67
|
+
# milestones array.
|
30
68
|
def revert_to_milestone!(n)
|
31
69
|
verify_milestone_exists n
|
32
70
|
self.revert_to! self.milestones[n.to_i-1].version
|
33
71
|
end
|
34
|
-
|
72
|
+
|
73
|
+
# Same as #revert_to_milestone!, except it doesn't save.
|
35
74
|
def revert_to_milestone(n)
|
36
75
|
verify_milestone_exists n
|
37
76
|
self.revert_to self.milestones[n.to_i-1].version
|
38
77
|
end
|
39
|
-
|
78
|
+
|
79
|
+
# Same as #rollback_to_latest_milestone, but doesn't save.
|
40
80
|
def rollback_to_latest_milestone
|
41
81
|
self.revert_to_milestone self.milestones.count
|
42
82
|
end
|
43
83
|
|
84
|
+
# Reverts to the latest milestone. Shortcut for:
|
85
|
+
# my_doc.revert_to_milestone! my_doc.milestones.count
|
44
86
|
def rollback_to_latest_milestone!
|
45
87
|
self.revert_to_milestone! self.milestones.count
|
46
88
|
end
|
47
|
-
|
89
|
+
|
90
|
+
# Retrieve the entire revision number, given an integer.
|
91
|
+
# For example, suppose my doc has 5 versions.
|
92
|
+
# my_doc.version_id 4 #==> "rev-4-74fj838r838fhjkdfklasdjrieu4839493"
|
48
93
|
def version_id(version_num)
|
49
94
|
self["_attachments"].keys.sort {|a,b| version_number(a) <=> version_number(b)}[version_num - 1] if self["_attachments"]
|
50
95
|
end
|
51
96
|
|
97
|
+
# Retrieve the version number, given a revision id.
|
98
|
+
# For example,
|
99
|
+
# my_doc.version_number "rev-5-kjfldsjaiu932489023rewar" #==> 5
|
100
|
+
# my_doc.version_number "4-jkfldsjli3290843029irelajfldsa" # ==> 4
|
52
101
|
def version_number(version_id)
|
53
102
|
version_id.gsub(VERSION_REGEX, '\1').to_i
|
54
103
|
end
|
55
104
|
|
105
|
+
# Shortcut for:
|
106
|
+
# my_doc.current_version - 1
|
56
107
|
def previous_version
|
57
108
|
current_version - 1
|
58
109
|
end
|
59
110
|
|
111
|
+
# Returns a simple version number (integer) corresponding to the current revision.
|
112
|
+
# For example, suppose the current revision (_rev) is: "4-jkfdlsi9432943wklrejwalr94302".
|
113
|
+
# my_doc.current_version #==> 4
|
60
114
|
def current_version
|
61
115
|
version_number rev
|
62
116
|
end
|
63
117
|
|
118
|
+
# Flag the current version as a milestone. You can optionally annotate the milestone by passing a do block to the method.
|
119
|
+
# some_article.milestone! do
|
120
|
+
# notes "Passed first round of editing."
|
121
|
+
# approved_by "Joe the editor."
|
122
|
+
# end
|
123
|
+
#
|
124
|
+
# You may annotate with whatever properties you desire. "notes" and "approved_by" were simply examples.
|
64
125
|
def milestone!(&block)
|
65
126
|
annotations = Memories::Annotation.new
|
66
127
|
annotations.instance_eval(&block) if block
|
@@ -68,10 +129,14 @@ module Memories
|
|
68
129
|
self.save
|
69
130
|
end
|
70
131
|
|
132
|
+
# returns an array of all milestones. Each milestone contains a "version" property (pointing to a specific revision)
|
133
|
+
# and an "annotations" property, containing a (possible empty) hash of key/value pairs corresponding to any annotations
|
134
|
+
# the creator of the milestone decided to write.
|
71
135
|
def milestones
|
72
136
|
self.milestone_memories
|
73
137
|
end
|
74
138
|
|
139
|
+
# Returns the metadata (version, annotations) for the latest milestone created.
|
75
140
|
def latest_milestone
|
76
141
|
self.milestone_memories.last
|
77
142
|
end
|
@@ -109,7 +174,7 @@ module Memories
|
|
109
174
|
end
|
110
175
|
|
111
176
|
def prep_for_versioning(doc)
|
112
|
-
doc.dup.delete_if {|k,v|
|
177
|
+
doc.dup.delete_if {|k,v| self.class.forget_properties.include? k}
|
113
178
|
end
|
114
179
|
|
115
180
|
def decode_attachments
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memories
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 2
|
10
|
+
version: 0.2.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Matt Parker
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-09-04 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -35,7 +35,7 @@ dependencies:
|
|
35
35
|
version: 1.0.0.beta7
|
36
36
|
type: :runtime
|
37
37
|
version_requirements: *id001
|
38
|
-
description: CouchDB has built in document versioning, but you can't rely on it for version control. This is an implementation of a version-as-attachments approach
|
38
|
+
description: CouchDB has built in document versioning, but you can't rely on it for version control. This is an implementation of a version-as-attachments approach created by @jchris.moonmaster9000@gmail.com
|
39
39
|
email: moonmaster9000@gmail.com
|
40
40
|
executables: []
|
41
41
|
|
@@ -43,7 +43,9 @@ extensions: []
|
|
43
43
|
|
44
44
|
extra_rdoc_files:
|
45
45
|
- README.markdown
|
46
|
+
- README.rdoc
|
46
47
|
files:
|
48
|
+
- README.rdoc
|
47
49
|
- lib/memories.rb
|
48
50
|
- lib/memories/annotation.rb
|
49
51
|
- lib/memories/attachment.rb
|