mongoid-history 0.1.7 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
data/Gemfile CHANGED
@@ -1,16 +1,13 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  gem "easy_diff"
4
- gem "mongoid", "~> 2.0"
4
+ gem "mongoid", ">= 2.0.0"
5
5
 
6
6
  group :development do
7
- gem "bson_ext", ">= 1.3.1"
8
- gem "rspec", "~> 2.3.0"
9
- gem "yard", "~> 0.6.0"
10
- gem "bundler", "~> 1.0.0"
11
- gem "jeweler", "~> 1.5.2"
12
- gem "rcov", ">= 0"
13
- gem "reek", "~> 1.2.8"
14
- gem "roodi", "~> 2.1.0"
7
+ gem "bson_ext"
8
+ gem "rspec"
9
+ gem "yard"
10
+ gem "bundler", ">= 1.0.0"
11
+ gem "jeweler"
15
12
  gem "database_cleaner"
16
13
  end
data/README.md ADDED
@@ -0,0 +1,202 @@
1
+ mongoid-history
2
+ ===============
3
+
4
+ [![Build Status](https://secure.travis-ci.org/aq1018/mongoid-history.png?branch=master)](http://travis-ci.org/aq1018/mongoid-history) [![Dependency Status](https://gemnasium.com/aq1018/mongoid-history.png?travis)](https://gemnasium.com/aq1018/mongoid-history)
5
+
6
+
7
+ In frustration of Mongoid::Versioning, I created this plugin for tracking historical changes for any document, including embedded ones. It achieves this by storing all history tracks in a single collection that you define. (See Usage for more details) Embedded documents are referenced by storing an association path, which is an array of document_name and document_id fields starting from the top most parent document and down to the embedded document that should track history.
8
+
9
+ This plugin implements multi-user undo, which allows users to undo any history change in any order. Undoing a document also creates a new history track. This is great for auditing and preventing vandalism, but it is probably not suitable for use cases such as a wiki.
10
+
11
+ Upgrading to mongoid-history-0.2.0
12
+ ----------------------------------
13
+
14
+ If you are upgrade from 0.1.x to version 0.2.x, you need to run the following code **before** you start to use the 0.2.x. This is due to changes in `Mongoid::History::Tracker`'s `association_chain` field.
15
+
16
+ ```ruby
17
+ Mongoid::History.tracker_class.all.each do |tracker|
18
+ tracker.association_chain[1..-1].each do |node|
19
+ node['name'] = node['name'].tableize
20
+ end
21
+ tracker.save!
22
+ end
23
+ ```
24
+
25
+ Install
26
+ -------
27
+
28
+ Currently this gem supports ruby 1.9.x only. ruby 1.8.7, ree and rubinus are not working right now.
29
+
30
+ ```
31
+ gem install mongoid-history
32
+ ```
33
+
34
+ Rails 3
35
+ -------
36
+
37
+ In your Gemfile:
38
+
39
+ ```ruby
40
+ gem 'mongoid-history'
41
+ ```
42
+
43
+ Usage
44
+ -----
45
+
46
+ Here is a quick example on how to use this plugin. For more details, please look at spec/integration/integration_spec.rb. It offers more detailed examples on how to use `Mongoid::History`.
47
+
48
+ **Create a History Tracker**
49
+
50
+ Create a new class to track histories. All histories are stored in this tracker. The name of the class can be anything you like. The only requirement is that it includes `Mongoid::History::Tracker`
51
+
52
+ ```ruby
53
+ # app/models/history_tracker.rb
54
+ class HistoryTracker
55
+ include Mongoid::History::Tracker
56
+ end
57
+ ```
58
+
59
+ **Set Tracker Class Name**
60
+
61
+
62
+ You should manually set the tracker class name to make sure your tracker can be found and loaded properly. You can skip this step if you manually require your tracker before using any trackables. If you don't know what I'm talking about, then you should just follow the example below.
63
+
64
+ Here is an example of setting the tracker class name using a rails initializer
65
+
66
+ ```ruby
67
+ # config/initializers/mongoid-history.rb
68
+ # initializer for mongoid-history
69
+ # assuming HistoryTracker is your tracker class
70
+ Mongoid::History.tracker_class_name = :history_tracker
71
+ ```
72
+
73
+ **Set `#current_user` method name**
74
+
75
+ You can set name of method which returns currently logged in user if you don't want to set modifier explicitly on every update.
76
+
77
+ Here is an example of setting the current_user_method using a rails initializer
78
+
79
+ ```ruby
80
+ # config/initializers/mongoid-history.rb
81
+ # initializer for mongoid-history
82
+ # assuming you're using devise/authlogic
83
+ Mongoid::History.current_user_method = :current_user
84
+ ```
85
+
86
+ When current_user_method is set mongoid-history call this method on each update and set it as modifier
87
+
88
+ ```ruby
89
+ # Assume that current_user return #<User _id: 1>
90
+ post = Post.first
91
+ post.update_attributes(:title => 'New title')
92
+
93
+ post.history_tracks.last.modifier #=> #<User _id: 1>
94
+ ```
95
+
96
+ ***Create Trackable classes and objects***
97
+
98
+ ```ruby
99
+ class Post
100
+ include Mongoid::Document
101
+ include Mongoid::Timestamps
102
+
103
+ # History tracking all Post Documents
104
+ # Note: Tracking will not work until #track_history is invoked
105
+ include Mongoid::History::Trackable
106
+
107
+ field :title
108
+ field :body
109
+ field :rating
110
+ embeds_many :comments
111
+
112
+ # Telling Mongoid::History how you want to track
113
+ track_history :on => [:title, :body], # I want to track title and body fields only. Default is :all
114
+ :modifier_field => :modifier, # Adds "referened_in :modifier" to track who made the change. Default is :modifier
115
+ :version_field => :version, # Adds "field :version, :type => Integer" to track current version. Default is :version
116
+ :track_create => false, # Do you want to track document creation? Default is false
117
+ :track_update => true, # Do you want to track document updates? Default is true
118
+ :track_destroy => false, # Do you want to track document destruction? Default is false
119
+ end
120
+
121
+ class Comment
122
+ include Mongoid::Document
123
+ include Mongoid::Timestamps
124
+
125
+ # Declare that we want to track comments
126
+ include Mongoid::History::Trackable
127
+
128
+ field :title
129
+ field :body
130
+ embedded_in :post, :inverse_of => :comments
131
+
132
+ # Track title and body for all comments, scope it to post (the parent)
133
+ # Also track creation and destruction
134
+ track_history :on => [:title, :body], :scope => :post, :track_create => true, :track_destroy => true
135
+ end
136
+
137
+ # The modifier can be specified as well
138
+ class User
139
+ include Mongoid::Document
140
+ include Mongoid::Timestamps
141
+
142
+ field :name
143
+ end
144
+
145
+ user = User.create(:name => "Aaron")
146
+ post = Post.create(:title => "Test", :body => "Post", :modifier => user)
147
+ comment = post.comments.create(:title => "test", :body => "comment", :modifier => user)
148
+ comment.history_tracks.count # should be 1
149
+
150
+ comment.update_attributes(:title => "Test 2")
151
+ comment.history_tracks.count # should be 2
152
+
153
+ track = comment.history_tracks.last
154
+
155
+ track.undo! user # comment title should be "Test"
156
+
157
+ track.redo! user # comment title should be "Test 2"
158
+
159
+ # undo last change
160
+ comment.undo! user
161
+
162
+ # undo versions 1 - 4
163
+ comment.undo! user, :from => 4, :to => 1
164
+
165
+ # undo last 3 versions
166
+ comment.undo! user, :last => 3
167
+
168
+ # redo versions 1 - 4
169
+ comment.redo! user, :from => 1, :to => 4
170
+
171
+ # redo last 3 versions
172
+ comment.redo! user, :last => 3
173
+
174
+ # delete post
175
+ post.destroy
176
+
177
+ # undelete post
178
+ post.undo! user
179
+
180
+ # disable tracking for comments within a block
181
+ Comment.disable_tracking do
182
+ comment.update_attributes(:title => "Test 3")
183
+ end
184
+ ```
185
+
186
+ Contributing to mongoid-history
187
+ -------------------------------
188
+
189
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
190
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
191
+ * Fork the project
192
+ * Start a feature/bugfix branch
193
+ * Commit and push until you are happy with your contribution
194
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
195
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
196
+
197
+ Copyright
198
+ ---------
199
+
200
+ Copyright (c) 2011 Aaron Qian. See LICENSE.txt for
201
+ further details.
202
+
data/Rakefile CHANGED
@@ -31,25 +31,6 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
31
31
  spec.rspec_opts = "--color --format progress"
32
32
  end
33
33
 
34
- RSpec::Core::RakeTask.new(:rcov) do |spec|
35
- spec.pattern = 'spec/**/*_spec.rb'
36
- spec.rcov = true
37
- spec.rcov_opts = "--exclude ~\/.rvm,spec"
38
- end
39
-
40
- require 'reek/rake/task'
41
- Reek::Rake::Task.new do |t|
42
- t.fail_on_error = true
43
- t.verbose = false
44
- t.source_files = 'lib/**/*.rb'
45
- end
46
-
47
- require 'roodi'
48
- require 'roodi_task'
49
- RoodiTask.new do |t|
50
- t.verbose = false
51
- end
52
-
53
34
  task :default => :spec
54
35
 
55
36
  require 'yard'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.7
1
+ 0.2.1
@@ -1,28 +1,44 @@
1
1
  module Mongoid::History
2
2
  class Sweeper < Mongoid::Observer
3
- attr_accessor :controller
3
+ def controller
4
+ Thread.current[:mongoid_history_sweeper_controller]
5
+ end
6
+
7
+ def controller=(value)
8
+ Thread.current[:mongoid_history_sweeper_controller] = value
9
+ end
4
10
 
5
11
  def self.observed_classes
6
12
  [Mongoid::History.tracker_class]
7
13
  end
8
14
 
15
+ # Hook to ActionController::Base#around_filter.
16
+ # Runs before a controller action is run.
17
+ # It should always return true so controller actions
18
+ # can continue.
9
19
  def before(controller)
10
20
  self.controller = controller
11
- true # before method from sweeper should always return true
21
+ true
12
22
  end
13
23
 
24
+ # Hook to ActionController::Base#around_filter.
25
+ # Runs after a controller action is run.
26
+ # Clean up so that the controller can
27
+ # be collected after this request
14
28
  def after(controller)
15
- self.controller = controller
16
- # Clean up, so that the controller can be collected after this request
17
29
  self.controller = nil
18
30
  end
19
31
 
20
32
  def before_create(track)
21
- track.modifier ||= current_user
33
+ modifier_field = track.trackable.history_trackable_options[:modifier_field]
34
+ modifier = track.send modifier_field
35
+ track.send "#{modifier_field}=", current_user unless modifier
22
36
  end
23
37
 
24
38
  def current_user
25
- controller.send Mongoid::History.current_user_method if controller.respond_to?(Mongoid::History.current_user_method, true)
39
+ if controller.respond_to?(Mongoid::History.current_user_method, true)
40
+ controller.send Mongoid::History.current_user_method
41
+ end
26
42
  end
27
43
  end
28
44
  end
@@ -36,7 +36,7 @@ module Mongoid::History
36
36
  field options[:version_field].to_sym, :type => Integer
37
37
  referenced_in options[:modifier_field].to_sym, :class_name => Mongoid::History.modifier_class_name
38
38
 
39
- include InstanceMethods
39
+ include MyInstanceMethods
40
40
  extend SingletonMethods
41
41
 
42
42
  delegate :history_trackable_options, :to => 'self.class'
@@ -69,11 +69,11 @@ module Mongoid::History
69
69
  end
70
70
  end
71
71
 
72
- module InstanceMethods
72
+ module MyInstanceMethods
73
73
  def history_tracks
74
- @history_tracks ||= Mongoid::History.tracker_class.where(:scope => history_trackable_options[:scope], :association_chain => traverse_association_chain)
74
+ @history_tracks ||= Mongoid::History.tracker_class.where(:scope => history_trackable_options[:scope], :association_chain => association_hash)
75
75
  end
76
-
76
+
77
77
  # undo :from => 1, :to => 5
78
78
  # undo 4
79
79
  # undo :last => 10
@@ -128,10 +128,27 @@ module Mongoid::History
128
128
 
129
129
  def traverse_association_chain(node=self)
130
130
  list = node._parent ? traverse_association_chain(node._parent) : []
131
- list << { 'name' => node.class.name, 'id' => node.id }
131
+ list << association_hash(node)
132
132
  list
133
133
  end
134
134
 
135
+ def association_hash(node=self)
136
+ # get all reflections of embedded_in association metadata
137
+ # and find the first association that matches _parent.
138
+ if node._parent
139
+ meta = node.reflect_on_all_associations(:embedded_in).find do |meta|
140
+ node._parent == node.send(meta.key)
141
+ end
142
+
143
+ inverse = node._parent.reflect_on_association(meta.inverse)
144
+ end
145
+
146
+ # if root node has no meta, and should use class name instead
147
+ name = meta ? meta.inverse.to_s : node.class.name
148
+
149
+ { 'name' => name, 'id' => node.id}
150
+ end
151
+
135
152
  def modified_attributes_for_update
136
153
  @modified_attributes_for_update ||= if history_trackable_options[:on] == :all
137
154
  changes.reject do |k, v|
@@ -187,7 +204,7 @@ module Mongoid::History
187
204
  return unless should_track_update?
188
205
  current_version = (self.send(history_trackable_options[:version_field]) || 0 ) + 1
189
206
  self.send("#{history_trackable_options[:version_field]}=", current_version)
190
- Mongoid::History.tracker_class.create!(history_tracker_attributes(:update).merge(:version => current_version, :action => "update"))
207
+ Mongoid::History.tracker_class.create!(history_tracker_attributes(:update).merge(:version => current_version, :action => "update", :trackable => self))
191
208
  clear_memoization
192
209
  end
193
210
 
@@ -195,14 +212,14 @@ module Mongoid::History
195
212
  return unless track_history?
196
213
  current_version = (self.send(history_trackable_options[:version_field]) || 0 ) + 1
197
214
  self.send("#{history_trackable_options[:version_field]}=", current_version)
198
- Mongoid::History.tracker_class.create!(history_tracker_attributes(:create).merge(:version => current_version, :action => "create"))
215
+ Mongoid::History.tracker_class.create!(history_tracker_attributes(:create).merge(:version => current_version, :action => "create", :trackable => self))
199
216
  clear_memoization
200
217
  end
201
218
 
202
219
  def track_destroy
203
220
  return unless track_history?
204
221
  current_version = (self.send(history_trackable_options[:version_field]) || 0 ) + 1
205
- Mongoid::History.tracker_class.create!(history_tracker_attributes(:destroy).merge(:version => current_version, :action => "destroy"))
222
+ Mongoid::History.tracker_class.create!(history_tracker_attributes(:destroy).merge(:version => current_version, :action => "destroy", :trackable => self))
206
223
  clear_memoization
207
224
  end
208
225
 
@@ -5,6 +5,7 @@ module Mongoid::History
5
5
  included do
6
6
  include Mongoid::Document
7
7
  include Mongoid::Timestamps
8
+ attr_writer :trackable
8
9
 
9
10
  field :association_chain, :type => Array, :default => []
10
11
  field :modified, :type => Hash
@@ -25,9 +26,9 @@ module Mongoid::History
25
26
 
26
27
  def undo!(modifier)
27
28
  if action.to_sym == :destroy
28
- class_name = association_chain[0]["name"]
29
- restored = class_name.constantize.new(modified)
30
- restored.save!
29
+ re_create
30
+ elsif action.to_sym == :create
31
+ re_destroy
31
32
  else
32
33
  trackable.update_attributes!(undo_attr(modifier))
33
34
  end
@@ -35,7 +36,9 @@ module Mongoid::History
35
36
 
36
37
  def redo!(modifier)
37
38
  if action.to_sym == :destroy
38
- trackable.destroy
39
+ re_destroy
40
+ elsif action.to_sym == :create
41
+ re_create
39
42
  else
40
43
  trackable.update_attributes!(redo_attr(modifier))
41
44
  end
@@ -69,25 +72,80 @@ module Mongoid::History
69
72
  @trackable_parents ||= trackable_parents_and_trackable[0, -1]
70
73
  end
71
74
 
75
+ def trackable_parent
76
+ @trackable_parent ||= trackable_parents_and_trackable[-2]
77
+ end
78
+
79
+
72
80
  def affected
73
- @affected ||= (modified.keys | original.keys).inject({}){ |h,k| h[k] =
81
+ @affected ||= (modified.keys | original.keys).inject({}){ |h,k| h[k] =
74
82
  trackable ? trackable.attributes[k] : modified[k]; h}
75
83
  end
76
84
 
77
85
  private
86
+
87
+ def re_create
88
+ association_chain.length > 1 ? create_on_parent : create_standalone
89
+ end
90
+
91
+ def re_destroy
92
+ trackable.destroy
93
+ end
94
+
95
+ def create_standalone
96
+ class_name = association_chain.first["name"]
97
+ restored = class_name.constantize.new(modified)
98
+ restored.save!
99
+ end
100
+
101
+ def create_on_parent
102
+ name = association_chain.last["name"]
103
+ if embeds_one?(trackable_parent, name)
104
+ trackable_parent.send("create_#{name}!", modified)
105
+ elsif embeds_many?(trackable_parent, name)
106
+ trackable_parent.send(name).create!(modified)
107
+ else
108
+ raise "This should never happen. Please report bug!"
109
+ end
110
+ end
111
+
78
112
  def trackable_parents_and_trackable
79
113
  @trackable_parents_and_trackable ||= traverse_association_chain
80
114
  end
81
115
 
116
+ def relation_of(doc, name)
117
+ meta = doc.reflect_on_association(name)
118
+ meta ? meta.relation : nil
119
+ end
120
+
121
+ def embeds_one?(doc, name)
122
+ relation_of(doc, name) == Mongoid::Relations::Embedded::One
123
+ end
124
+
125
+ def embeds_many?(doc, name)
126
+ relation_of(doc, name) == Mongoid::Relations::Embedded::Many
127
+ end
128
+
82
129
  def traverse_association_chain
83
130
  chain = association_chain.dup
84
131
  doc = nil
85
132
  documents = []
133
+
86
134
  begin
87
135
  node = chain.shift
88
136
  name = node['name']
89
- col = doc.nil? ? name.classify.constantize : doc.send(name.tableize)
90
- doc = col.where(:_id => node['id']).first
137
+
138
+ doc = if doc.nil?
139
+ # root association. First element of the association chain
140
+ klass = name.classify.constantize
141
+ klass.where(:_id => node['id']).first
142
+ elsif embeds_one?(doc, name)
143
+ doc.send(name)
144
+ elsif embeds_many?(doc, name)
145
+ doc.send(name).where(:_id => node['id']).first
146
+ else
147
+ raise "This should never happen. Please report bug."
148
+ end
91
149
  documents << doc
92
150
  end while( !chain.empty? )
93
151
  documents
@@ -4,27 +4,25 @@
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{mongoid-history}
8
- s.version = "0.1.7"
7
+ s.name = "mongoid-history"
8
+ s.version = "0.2.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = [%q{Aaron Qian}, %q{Justin Grimes}]
12
- s.date = %q{2011-12-09}
13
- s.description = %q{In frustration of Mongoid::Versioning, I created this plugin for tracking historical changes for any document, including embedded ones. It achieves this by storing all history tracks in a single collection that you define. (See Usage for more details) Embedded documents are referenced by storing an association path, which is an array of document_name and document_id fields starting from the top most parent document and down to the embedded document that should track history.
14
-
15
- This plugin implements multi-user undo, which allows users to undo any history change in any order. Undoing a document also creates a new history track. This is great for auditing and preventing vandalism, but it is probably not suitable for use cases such as a wiki.}
16
- s.email = [%q{aq1018@gmail.com}, %q{justin.mgrimes@gmail.com}]
11
+ s.authors = ["Aaron Qian", "Justin Grimes"]
12
+ s.date = "2012-03-19"
13
+ s.description = "In frustration of Mongoid::Versioning, I created this plugin for tracking historical changes for any document, including embedded ones. It achieves this by storing all history tracks in a single collection that you define. (See Usage for more details) Embedded documents are referenced by storing an association path, which is an array of document_name and document_id fields starting from the top most parent document and down to the embedded document that should track history.\n\n This plugin implements multi-user undo, which allows users to undo any history change in any order. Undoing a document also creates a new history track. This is great for auditing and preventing vandalism, but it is probably not suitable for use cases such as a wiki."
14
+ s.email = ["aq1018@gmail.com", "justin.mgrimes@gmail.com"]
17
15
  s.extra_rdoc_files = [
18
16
  "LICENSE.txt",
19
- "README.rdoc"
17
+ "README.md"
20
18
  ]
21
19
  s.files = [
22
20
  ".document",
23
21
  ".rspec",
24
- ".rvmrc",
22
+ ".travis.yml",
25
23
  "Gemfile",
26
24
  "LICENSE.txt",
27
- "README.rdoc",
25
+ "README.md",
28
26
  "Rakefile",
29
27
  "VERSION",
30
28
  "lib/mongoid-history.rb",
@@ -38,57 +36,42 @@ Gem::Specification.new do |s|
38
36
  "spec/trackable_spec.rb",
39
37
  "spec/tracker_spec.rb"
40
38
  ]
41
- s.homepage = %q{http://github.com/aq1018/mongoid-history}
42
- s.licenses = [%q{MIT}]
43
- s.require_paths = [%q{lib}]
44
- s.rubygems_version = %q{1.8.6}
45
- s.summary = %q{history tracking, auditing, undo, redo for mongoid}
46
- s.test_files = [
47
- "spec/integration/integration_spec.rb",
48
- "spec/spec_helper.rb",
49
- "spec/trackable_spec.rb",
50
- "spec/tracker_spec.rb"
51
- ]
39
+ s.homepage = "http://github.com/aq1018/mongoid-history"
40
+ s.licenses = ["MIT"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = "1.8.10"
43
+ s.summary = "history tracking, auditing, undo, redo for mongoid"
52
44
 
53
45
  if s.respond_to? :specification_version then
54
46
  s.specification_version = 3
55
47
 
56
48
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
49
  s.add_runtime_dependency(%q<easy_diff>, [">= 0"])
58
- s.add_runtime_dependency(%q<mongoid>, ["~> 2.0"])
59
- s.add_development_dependency(%q<bson_ext>, [">= 1.3.1"])
60
- s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
61
- s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
62
- s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
63
- s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
64
- s.add_development_dependency(%q<rcov>, [">= 0"])
65
- s.add_development_dependency(%q<reek>, ["~> 1.2.8"])
66
- s.add_development_dependency(%q<roodi>, ["~> 2.1.0"])
50
+ s.add_runtime_dependency(%q<mongoid>, [">= 2.0.0"])
51
+ s.add_development_dependency(%q<bson_ext>, [">= 0"])
52
+ s.add_development_dependency(%q<rspec>, [">= 0"])
53
+ s.add_development_dependency(%q<yard>, [">= 0"])
54
+ s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
55
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
67
56
  s.add_development_dependency(%q<database_cleaner>, [">= 0"])
68
57
  else
69
58
  s.add_dependency(%q<easy_diff>, [">= 0"])
70
- s.add_dependency(%q<mongoid>, ["~> 2.0"])
71
- s.add_dependency(%q<bson_ext>, [">= 1.3.1"])
72
- s.add_dependency(%q<rspec>, ["~> 2.3.0"])
73
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
74
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
75
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
76
- s.add_dependency(%q<rcov>, [">= 0"])
77
- s.add_dependency(%q<reek>, ["~> 1.2.8"])
78
- s.add_dependency(%q<roodi>, ["~> 2.1.0"])
59
+ s.add_dependency(%q<mongoid>, [">= 2.0.0"])
60
+ s.add_dependency(%q<bson_ext>, [">= 0"])
61
+ s.add_dependency(%q<rspec>, [">= 0"])
62
+ s.add_dependency(%q<yard>, [">= 0"])
63
+ s.add_dependency(%q<bundler>, [">= 1.0.0"])
64
+ s.add_dependency(%q<jeweler>, [">= 0"])
79
65
  s.add_dependency(%q<database_cleaner>, [">= 0"])
80
66
  end
81
67
  else
82
68
  s.add_dependency(%q<easy_diff>, [">= 0"])
83
- s.add_dependency(%q<mongoid>, ["~> 2.0"])
84
- s.add_dependency(%q<bson_ext>, [">= 1.3.1"])
85
- s.add_dependency(%q<rspec>, ["~> 2.3.0"])
86
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
87
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
88
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
89
- s.add_dependency(%q<rcov>, [">= 0"])
90
- s.add_dependency(%q<reek>, ["~> 1.2.8"])
91
- s.add_dependency(%q<roodi>, ["~> 2.1.0"])
69
+ s.add_dependency(%q<mongoid>, [">= 2.0.0"])
70
+ s.add_dependency(%q<bson_ext>, [">= 0"])
71
+ s.add_dependency(%q<rspec>, [">= 0"])
72
+ s.add_dependency(%q<yard>, [">= 0"])
73
+ s.add_dependency(%q<bundler>, [">= 1.0.0"])
74
+ s.add_dependency(%q<jeweler>, [">= 0"])
92
75
  s.add_dependency(%q<database_cleaner>, [">= 0"])
93
76
  end
94
77
  end
@@ -16,6 +16,7 @@ describe Mongoid::History do
16
16
  field :rating
17
17
 
18
18
  embeds_many :comments
19
+ embeds_one :section
19
20
  track_history :on => [:title, :body], :track_destroy => true
20
21
  end
21
22
 
@@ -26,8 +27,18 @@ describe Mongoid::History do
26
27
 
27
28
  field :title
28
29
  field :body
29
- embedded_in :post, :inverse_of => :comments
30
- track_history :on => [:title, :body], :scope => :post, :track_create => true
30
+ embedded_in :post
31
+ track_history :on => [:title, :body], :scope => :post, :track_create => true, :track_destroy => true
32
+ end
33
+
34
+ class Section
35
+ include Mongoid::Document
36
+ include Mongoid::Timestamps
37
+ include Mongoid::History::Trackable
38
+
39
+ field :title
40
+ embedded_in :post
41
+ track_history :on => [:title], :scope => :post, :track_create => true, :track_destroy => true
31
42
  end
32
43
 
33
44
  class User
@@ -79,7 +90,11 @@ describe Mongoid::History do
79
90
  end
80
91
 
81
92
  it "should assign association_chain" do
82
- @comment.history_tracks.first.association_chain.should == [{'id' => @post.id, 'name' => "Post"}, {'id' => @comment.id, 'name' => "Comment"}]
93
+ expected = [
94
+ {'id' => @post.id, 'name' => "Post"},
95
+ {'id' => @comment.id, 'name' => "comments"}
96
+ ]
97
+ @comment.history_tracks.first.association_chain.should == expected
83
98
  end
84
99
  end
85
100
 
@@ -117,19 +132,19 @@ describe Mongoid::History do
117
132
 
118
133
  it "should assign modified fields" do
119
134
  @post.update_attributes(:title => "Another Test")
120
- @post.history_tracks.first.modified.should == {
135
+ @post.history_tracks.last.modified.should == {
121
136
  "title" => "Another Test"
122
137
  }
123
138
  end
124
139
 
125
140
  it "should assign method field" do
126
141
  @post.update_attributes(:title => "Another Test")
127
- @post.history_tracks.first.action.should == "update"
142
+ @post.history_tracks.last.action.should == "update"
128
143
  end
129
144
 
130
145
  it "should assign original fields" do
131
146
  @post.update_attributes(:title => "Another Test")
132
- @post.history_tracks.first.original.should == {
147
+ @post.history_tracks.last.original.should == {
133
148
  "title" => "Test"
134
149
  }
135
150
  end
@@ -156,7 +171,7 @@ describe Mongoid::History do
156
171
 
157
172
  it "should assign association_chain" do
158
173
  @post.update_attributes(:title => "Another Test")
159
- @post.history_tracks.first.association_chain.should == [{'id' => @post.id, 'name' => "Post"}]
174
+ @post.history_tracks.last.association_chain.should == [{'id' => @post.id, 'name' => "Post"}]
160
175
  end
161
176
 
162
177
  it "should exclude defined options" do
@@ -206,11 +221,11 @@ describe Mongoid::History do
206
221
 
207
222
  it "should assign modifier" do
208
223
  @post.update_attributes(:title => "Another Test", :modifier => @another_user)
209
- @post.history_tracks.first.modifier.should == @another_user
224
+ @post.history_tracks.last.modifier.should == @another_user
210
225
  end
211
226
  end
212
227
 
213
- describe "on update embedded" do
228
+ describe "on update embedded 1..N (embeds_many)" do
214
229
  it "should assign version on comment" do
215
230
  @comment.update_attributes(:title => "Test2")
216
231
  @comment.version.should == 2 # first track generated on creation
@@ -235,43 +250,133 @@ describe Mongoid::History do
235
250
  }
236
251
  end
237
252
 
253
+ it "should be possible to undo from parent" do
254
+ @comment.update_attributes(:title => "Test 2")
255
+ @post.history_tracks.last.undo!(@user)
256
+ @comment.reload
257
+ @comment.title.should == "test"
258
+ end
259
+
238
260
  it "should assign modifier" do
239
261
  @post.update_attributes(:title => "Another Test", :modifier => @another_user)
240
- @post.history_tracks.first.modifier.should == @another_user
262
+ @post.history_tracks.last.modifier.should == @another_user
263
+ end
264
+ end
265
+
266
+ describe "on update embedded 1..1 (embeds_one)" do
267
+ before(:each) do
268
+ @section = Section.new(:title => 'Technology')
269
+ @post.section = @section
270
+ @post.save!
271
+ @post.reload
272
+ @section = @post.section
273
+ end
274
+
275
+ it "should assign version on create section" do
276
+ @section.version.should == 1
277
+ end
278
+
279
+ it "should assign version on section" do
280
+ @section.update_attributes(:title => 'Technology 2')
281
+ @section.version.should == 2 # first track generated on creation
282
+ end
283
+
284
+ it "should create a history track of version 2" do
285
+ @section.update_attributes(:title => 'Technology 2')
286
+ @section.history_tracks.where(:version => 2).first.should_not be_nil
287
+ end
288
+
289
+ it "should assign modified fields" do
290
+ @section.update_attributes(:title => 'Technology 2')
291
+ @section.history_tracks.where(:version => 2).first.modified.should == {
292
+ "title" => "Technology 2"
293
+ }
294
+ end
295
+
296
+ it "should assign original fields" do
297
+ @section.update_attributes(:title => 'Technology 2')
298
+ @section.history_tracks.where(:version => 2).first.original.should == {
299
+ "title" => "Technology"
300
+ }
301
+ end
302
+
303
+ it "should be possible to undo from parent" do
304
+ @section.update_attributes(:title => 'Technology 2')
305
+ @post.history_tracks.last.undo!(@user)
306
+ @section.reload
307
+ @section.title.should == "Technology"
308
+ end
309
+
310
+ it "should assign modifier" do
311
+ @section.update_attributes(:title => "Business", :modifier => @another_user)
312
+ @post.history_tracks.last.modifier.should == @another_user
313
+ end
314
+ end
315
+
316
+ describe "on destroy embedded" do
317
+ it "should be possible to re-create destroyed embedded" do
318
+ @comment.destroy
319
+ @comment.history_tracks.last.undo!(@user)
320
+ @post.reload
321
+ @post.comments.first.title.should == "test"
322
+ end
323
+
324
+ it "should be possible to re-create destroyed embedded from parent" do
325
+ @comment.destroy
326
+ @post.history_tracks.last.undo!(@user)
327
+ @post.reload
328
+ @post.comments.first.title.should == "test"
329
+ end
330
+
331
+ it "should be possible to destroy after re-create embedded from parent" do
332
+ @comment.destroy
333
+ @post.history_tracks.last.undo!(@user)
334
+ @post.history_tracks.last.undo!(@user)
335
+ @post.reload
336
+ @post.comments.count.should == 0
337
+ end
338
+
339
+ it "should be possible to create with redo after undo create embedded from parent" do
340
+ @post.comments.create!(:title => "The second one")
341
+ @track = @post.history_tracks.last
342
+ @track.undo!(@user)
343
+ @track.redo!(@user)
344
+ @post.reload
345
+ @post.comments.count.should == 2
241
346
  end
242
347
  end
243
348
 
244
349
  describe "non-embedded" do
245
350
  it "should undo changes" do
246
351
  @post.update_attributes(:title => "Test2")
247
- @post.history_tracks.where(:version => 1).first.undo!(@user)
352
+ @post.history_tracks.where(:version => 1).last.undo!(@user)
248
353
  @post.reload
249
354
  @post.title.should == "Test"
250
355
  end
251
356
 
252
357
  it "should undo destruction" do
253
358
  @post.destroy
254
- @post.history_tracks.where(:version => 1).first.undo!(@user)
359
+ @post.history_tracks.where(:version => 1).last.undo!(@user)
255
360
  Post.find(@post.id).title.should == "Test"
256
361
  end
257
362
 
258
363
  it "should create a new history track after undo" do
259
364
  @post.update_attributes(:title => "Test2")
260
- @post.history_tracks.where(:version => 1).first.undo!(@user)
365
+ @post.history_tracks.last.undo!(@user)
261
366
  @post.reload
262
- @post.history_tracks.count.should == 2
367
+ @post.history_tracks.count.should == 3
263
368
  end
264
369
 
265
370
  it "should assign @user as the modifier of the newly created history track" do
266
371
  @post.update_attributes(:title => "Test2")
267
- @post.history_tracks.where(:version => 1).first.undo!(@user)
372
+ @post.history_tracks.where(:version => 1).last.undo!(@user)
268
373
  @post.reload
269
- @post.history_tracks.where(:version => 2).first.modifier.should == @user
374
+ @post.history_tracks.where(:version => 2).last.modifier.should == @user
270
375
  end
271
376
 
272
377
  it "should stay the same after undo and redo" do
273
378
  @post.update_attributes(:title => "Test2")
274
- @track = @post.history_tracks.where(:version => 1).first
379
+ @track = @post.history_tracks.last
275
380
  @track.undo!(@user)
276
381
  @track.redo!(@user)
277
382
  @post2 = Post.where(:_id => @post.id).first
@@ -281,7 +386,7 @@ describe Mongoid::History do
281
386
 
282
387
  it "should be destroyed after undo and redo" do
283
388
  @post.destroy
284
- @track = @post.history_tracks.where(:version => 1).first
389
+ @track = @post.history_tracks.where(:version => 1).last
285
390
  @track.undo!(@user)
286
391
  @track.redo!(@user)
287
392
  Post.where(:_id => @post.id).first.should == nil
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid-history
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-12-09 00:00:00.000000000Z
13
+ date: 2012-03-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: easy_diff
17
- requirement: &19710360 !ruby/object:Gem::Requirement
17
+ requirement: &10925040 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,76 +22,65 @@ dependencies:
22
22
  version: '0'
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *19710360
25
+ version_requirements: *10925040
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: mongoid
28
- requirement: &19709840 !ruby/object:Gem::Requirement
28
+ requirement: &10924060 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
- - - ~>
31
+ - - ! '>='
32
32
  - !ruby/object:Gem::Version
33
- version: '2.0'
33
+ version: 2.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *19709840
36
+ version_requirements: *10924060
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: bson_ext
39
- requirement: &19709320 !ruby/object:Gem::Requirement
39
+ requirement: &10923100 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
43
43
  - !ruby/object:Gem::Version
44
- version: 1.3.1
44
+ version: '0'
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *19709320
47
+ version_requirements: *10923100
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: rspec
50
- requirement: &19708820 !ruby/object:Gem::Requirement
50
+ requirement: &10922320 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
- - - ~>
53
+ - - ! '>='
54
54
  - !ruby/object:Gem::Version
55
- version: 2.3.0
55
+ version: '0'
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *19708820
58
+ version_requirements: *10922320
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: yard
61
- requirement: &19708260 !ruby/object:Gem::Requirement
61
+ requirement: &10921560 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
- - - ~>
64
+ - - ! '>='
65
65
  - !ruby/object:Gem::Version
66
- version: 0.6.0
66
+ version: '0'
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *19708260
69
+ version_requirements: *10921560
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: bundler
72
- requirement: &19707720 !ruby/object:Gem::Requirement
72
+ requirement: &10920720 !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
- - - ~>
75
+ - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: 1.0.0
78
78
  type: :development
79
79
  prerelease: false
80
- version_requirements: *19707720
80
+ version_requirements: *10920720
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: jeweler
83
- requirement: &19707120 !ruby/object:Gem::Requirement
84
- none: false
85
- requirements:
86
- - - ~>
87
- - !ruby/object:Gem::Version
88
- version: 1.5.2
89
- type: :development
90
- prerelease: false
91
- version_requirements: *19707120
92
- - !ruby/object:Gem::Dependency
93
- name: rcov
94
- requirement: &19706560 !ruby/object:Gem::Requirement
83
+ requirement: &10935520 !ruby/object:Gem::Requirement
95
84
  none: false
96
85
  requirements:
97
86
  - - ! '>='
@@ -99,32 +88,10 @@ dependencies:
99
88
  version: '0'
100
89
  type: :development
101
90
  prerelease: false
102
- version_requirements: *19706560
103
- - !ruby/object:Gem::Dependency
104
- name: reek
105
- requirement: &19705940 !ruby/object:Gem::Requirement
106
- none: false
107
- requirements:
108
- - - ~>
109
- - !ruby/object:Gem::Version
110
- version: 1.2.8
111
- type: :development
112
- prerelease: false
113
- version_requirements: *19705940
114
- - !ruby/object:Gem::Dependency
115
- name: roodi
116
- requirement: &19705280 !ruby/object:Gem::Requirement
117
- none: false
118
- requirements:
119
- - - ~>
120
- - !ruby/object:Gem::Version
121
- version: 2.1.0
122
- type: :development
123
- prerelease: false
124
- version_requirements: *19705280
91
+ version_requirements: *10935520
125
92
  - !ruby/object:Gem::Dependency
126
93
  name: database_cleaner
127
- requirement: &19704680 !ruby/object:Gem::Requirement
94
+ requirement: &10934420 !ruby/object:Gem::Requirement
128
95
  none: false
129
96
  requirements:
130
97
  - - ! '>='
@@ -132,7 +99,7 @@ dependencies:
132
99
  version: '0'
133
100
  type: :development
134
101
  prerelease: false
135
- version_requirements: *19704680
102
+ version_requirements: *10934420
136
103
  description: ! "In frustration of Mongoid::Versioning, I created this plugin for tracking
137
104
  historical changes for any document, including embedded ones. It achieves this by
138
105
  storing all history tracks in a single collection that you define. (See Usage for
@@ -150,14 +117,14 @@ executables: []
150
117
  extensions: []
151
118
  extra_rdoc_files:
152
119
  - LICENSE.txt
153
- - README.rdoc
120
+ - README.md
154
121
  files:
155
122
  - .document
156
123
  - .rspec
157
- - .rvmrc
124
+ - .travis.yml
158
125
  - Gemfile
159
126
  - LICENSE.txt
160
- - README.rdoc
127
+ - README.md
161
128
  - Rakefile
162
129
  - VERSION
163
130
  - lib/mongoid-history.rb
@@ -185,7 +152,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
185
152
  version: '0'
186
153
  segments:
187
154
  - 0
188
- hash: -2112048329123492134
155
+ hash: -4147881344335982311
189
156
  required_rubygems_version: !ruby/object:Gem::Requirement
190
157
  none: false
191
158
  requirements:
@@ -194,12 +161,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
161
  version: '0'
195
162
  requirements: []
196
163
  rubyforge_project:
197
- rubygems_version: 1.8.6
164
+ rubygems_version: 1.8.10
198
165
  signing_key:
199
166
  specification_version: 3
200
167
  summary: history tracking, auditing, undo, redo for mongoid
201
- test_files:
202
- - spec/integration/integration_spec.rb
203
- - spec/spec_helper.rb
204
- - spec/trackable_spec.rb
205
- - spec/tracker_spec.rb
168
+ test_files: []
data/.rvmrc DELETED
@@ -1 +0,0 @@
1
- rvm 1.9.2@
data/README.rdoc DELETED
@@ -1,144 +0,0 @@
1
- = mongoid-history
2
-
3
- In frustration of Mongoid::Versioning, I created this plugin for tracking historical changes for any document, including embedded ones. It achieves this by storing all history tracks in a single collection that you define. (See Usage for more details) Embedded documents are referenced by storing an association path, which is an array of document_name and document_id fields starting from the top most parent document and down to the embedded document that should track history.
4
-
5
- This plugin implements multi-user undo, which allows users to undo any history change in any order. Undoing a document also creates a new history track. This is great for auditing and preventing vandalism, but it is probably not suitable for use cases such as a wiki.
6
-
7
- == Install
8
-
9
- gem install mongoid-history
10
-
11
- == Rails 3
12
-
13
- In your Gemfile:
14
-
15
- gem 'mongoid-history'
16
-
17
- == Usage
18
-
19
- Here is a quick example on how to use this plugin. For more details, please look at spec/integration/integration_spec.rb. It offers more detailed examples on how to use Mongoid::History.
20
-
21
- === Create a History Tracker
22
-
23
- Create a new class to track histories. All histories are stored in this tracker. The name of the class can be anything you like. The only requirement is that it includes `Mongoid::History::Tracker`
24
-
25
- # app/models/history_tracker.rb
26
- class HistoryTracker
27
- include Mongoid::History::Tracker
28
- end
29
-
30
- === Set Tracker Class Name
31
-
32
- You should manually set the tracker class name to make sure your tracker can be found and loaded properly. You can skip this step if you manually require your tracker before using any trackables. If you don't know what I'm talking about, then you should just follow the example below.
33
-
34
- Here is an example of setting the tracker class name using a rails initializer
35
-
36
- # config/initializers/mongoid-history.rb
37
- # initializer for mongoid-history
38
- # assuming HistoryTracker is your tracker class
39
- Mongoid::History.tracker_class_name = :history_tracker
40
-
41
-
42
- === Create Trackable classes and objects
43
-
44
- class Post
45
- include Mongoid::Document
46
- include Mongoid::Timestamps
47
-
48
- # History tracking all Post Documents
49
- # Note: Tracking will not work until #track_history is invoked
50
- include Mongoid::History::Trackable
51
-
52
- field :title
53
- field :body
54
- field :rating
55
- embeds_many :comments
56
-
57
- # Telling Mongoid::History how you want to track
58
- track_history :on => [:title, :body], # I want to track title and body fields only. Default is :all
59
- :modifier_field => :modifier, # Adds "referened_in :modifier" to track who made the change. Default is :modifier
60
- :version_field => :version, # Adds "field :version, :type => Integer" to track current version. Default is :version
61
- :track_create => false, # Do you want to track document creation? Default is false
62
- :track_update => true, # Do you want to track document updates? Default is true
63
- :track_destroy => false, # Do you want to track document destruction? Default is false
64
- end
65
-
66
- class Comment
67
- include Mongoid::Document
68
- include Mongoid::Timestamps
69
-
70
- # Declare that we want to track comments
71
- include Mongoid::History::Trackable
72
-
73
- field :title
74
- field :body
75
- embedded_in :post, :inverse_of => :comments
76
-
77
- # Track title and body for all comments, scope it to post (the parent)
78
- # Also track creation and destruction
79
- track_history :on => [:title, :body], :scope => :post, :track_create => true, :track_destroy => true
80
- end
81
-
82
- # The modifier can be specified as well
83
- class User
84
- include Mongoid::Document
85
- include Mongoid::Timestamps
86
-
87
- field :name
88
- end
89
-
90
- user = User.create(:name => "Aaron")
91
- post = Post.create(:title => "Test", :body => "Post", :modifier => user)
92
- comment = post.comments.create(:title => "test", :body => "comment", :modifier => user)
93
- comment.history_tracks.count # should be 1
94
-
95
- comment.update_attributes(:title => "Test 2")
96
- comment.history_tracks.count # should be 2
97
-
98
- track = comment.history_tracks.last
99
-
100
- track.undo! user # comment title should be "Test"
101
-
102
- track.redo! user # comment title should be "Test 2"
103
-
104
- # undo last change
105
- comment.undo! user
106
-
107
- # undo versions 1 - 4
108
- comment.undo! user, :from => 4, :to => 1
109
-
110
- # undo last 3 versions
111
- comment.undo! user, :last => 3
112
-
113
- # redo versions 1 - 4
114
- comment.redo! user, :from => 1, :to => 4
115
-
116
- # redo last 3 versions
117
- comment.redo! user, :last => 3
118
-
119
- # delete post
120
- post.destroy
121
-
122
- # undelete post
123
- post.undo! user
124
-
125
- # disable tracking for comments within a block
126
- Comment.disable_tracking do
127
- comment.update_attributes(:title => "Test 3")
128
- end
129
-
130
- == Contributing to mongoid-history
131
-
132
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
133
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
134
- * Fork the project
135
- * Start a feature/bugfix branch
136
- * Commit and push until you are happy with your contribution
137
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
138
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
139
-
140
- == Copyright
141
-
142
- Copyright (c) 2011 Aaron Qian. See LICENSE.txt for
143
- further details.
144
-