memories 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|