rich-acts_as_revisable 0.9.8 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +11 -12
- data/Rakefile +3 -3
- data/lib/acts_as_revisable/acts/common.rb +13 -35
- data/lib/acts_as_revisable/acts/deletable.rb +9 -5
- data/lib/acts_as_revisable/acts/revisable.rb +29 -36
- data/lib/acts_as_revisable/acts/revision.rb +7 -9
- data/lib/acts_as_revisable/base.rb +3 -1
- data/lib/acts_as_revisable/gem_spec_options.rb +6 -6
- data/lib/acts_as_revisable/options.rb +1 -1
- data/lib/acts_as_revisable/quoted_columns.rb +3 -7
- data/lib/acts_as_revisable/version.rb +4 -4
- data/lib/acts_as_revisable.rb +1 -5
- data/spec/associations_spec.rb +1 -1
- data/spec/branch_spec.rb +1 -1
- data/spec/find_spec.rb +4 -12
- data/spec/general_spec.rb +7 -1
- data/spec/options_spec.rb +5 -5
- data/spec/quoted_columns_spec.rb +1 -1
- data/spec/revert_spec.rb +1 -1
- data/spec/spec_helper.rb +14 -0
- metadata +6 -6
- data/lib/acts_as_revisable/acts/scoped_model.rb +0 -75
- data/lib/acts_as_revisable/clone_associations.rb +0 -36
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= acts_as_revisable
|
2
2
|
|
3
|
-
http://github.com/
|
3
|
+
http://github.com/rich/acts_as_revisable
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
@@ -11,7 +11,9 @@ This plugin wouldn't exist without Rick Olsen's acts_as_versioned. AAV has been
|
|
11
11
|
== FEATURES:
|
12
12
|
|
13
13
|
* Both the revisable and revision models must be explicitly defined.
|
14
|
-
Yes, this is a feature. The less magic needed the better. This allows you to build up your revision models just as you would any other.
|
14
|
+
Yes, this is a feature. The less magic needed the better. This allows you to build up your revision models just as you would any other.
|
15
|
+
|
16
|
+
If you absolutely need a generated revision model, you may pass ":generate_revision_class => true" to acts_as_revisable and it will generate the class at runtime for you. Think of this like scaffolding and not to be kept around for a real application.
|
15
17
|
|
16
18
|
* Numerous custom callbacks for both revisable and revision models.
|
17
19
|
* revisable models
|
@@ -22,6 +24,8 @@ This plugin wouldn't exist without Rick Olsen's acts_as_versioned. AAV has been
|
|
22
24
|
* before_changeset
|
23
25
|
* after_changeset
|
24
26
|
* after_branch_created
|
27
|
+
* before_revise_on_destroy (when :on_destroy => :revise is set)
|
28
|
+
* after_revise_on_destroy (when :on_destroy => :revise is set)
|
25
29
|
* revision models
|
26
30
|
* before_restore
|
27
31
|
* after_restore
|
@@ -33,7 +37,6 @@ This plugin wouldn't exist without Rick Olsen's acts_as_versioned. AAV has been
|
|
33
37
|
* Provides migration generators to add the revisable columns.
|
34
38
|
* Grouping several revisable actions into a single revision (changeset).
|
35
39
|
* Monitor all or just specified columns to trigger a revision.
|
36
|
-
* Clone all or specified associations to the revision model.
|
37
40
|
* Uses ActiveRecord's dirty attribute tracking.
|
38
41
|
* Several ways to find revisions including:
|
39
42
|
* revision number
|
@@ -72,7 +75,7 @@ Create the revision class:
|
|
72
75
|
|
73
76
|
class Session < ActiveRecord::Base
|
74
77
|
# we can accept the more standard hash syntax
|
75
|
-
acts_as_revision :revisable_class_name => "Project"
|
78
|
+
acts_as_revision :revisable_class_name => "Project"
|
76
79
|
end
|
77
80
|
|
78
81
|
Some example usage:
|
@@ -154,10 +157,6 @@ Changesets:
|
|
154
157
|
# our revision number has only incremented by one
|
155
158
|
@project.revision_number # => 3
|
156
159
|
|
157
|
-
Associations have been cloned:
|
158
|
-
|
159
|
-
@project.owner === @previous.owner # => true
|
160
|
-
|
161
160
|
Maybe we don't want to be able to branch from revisions:
|
162
161
|
|
163
162
|
class Session < ActiveRecord::Base
|
@@ -184,23 +183,23 @@ If the owner isn't set let's prevent reverting:
|
|
184
183
|
|
185
184
|
== REQUIREMENTS:
|
186
185
|
|
187
|
-
This plugin
|
186
|
+
This plugin requires Rails 2.3. Use version 0.9.8 of this plugin for Rails 2.1 and 2.2.
|
188
187
|
|
189
188
|
== INSTALL:
|
190
189
|
|
191
190
|
acts_as_revisable uses Rails' new ability to use gems as plugins. Installing AAR is as simple as installing a gem:
|
192
191
|
|
193
|
-
sudo gem install
|
192
|
+
sudo gem install rich-acts_as_revisable --source=http://gems.github.com
|
194
193
|
|
195
194
|
Once the gem is installed you'll want to activate it in your Rails app by adding the following line to config/environment.rb:
|
196
195
|
|
197
|
-
config.gem "
|
196
|
+
config.gem "rich-acts_as_revisable", :lib => "acts_as_revisable", :source => "http://gems.github.com"
|
198
197
|
|
199
198
|
== LICENSE:
|
200
199
|
|
201
200
|
(The MIT License)
|
202
201
|
|
203
|
-
Copyright (c)
|
202
|
+
Copyright (c) 2009 Rich Cavanaugh
|
204
203
|
|
205
204
|
Permission is hereby granted, free of charge, to any person obtaining
|
206
205
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -14,7 +14,7 @@ Rake::RDocTask.new do |rdoc|
|
|
14
14
|
end
|
15
15
|
|
16
16
|
spec = Gem::Specification.new do |s|
|
17
|
-
|
17
|
+
WithoutScope::ActsAsRevisable::GemSpecOptions::HASH.each do |key, value|
|
18
18
|
s.send("#{key.to_s}=",value)
|
19
19
|
end
|
20
20
|
end
|
@@ -25,7 +25,7 @@ end
|
|
25
25
|
|
26
26
|
desc "Generate the static gemspec required for github."
|
27
27
|
task :generate_gemspec do
|
28
|
-
options =
|
28
|
+
options = WithoutScope::ActsAsRevisable::GemSpecOptions::HASH.clone
|
29
29
|
options[:name] = "acts_as_revisable"
|
30
30
|
|
31
31
|
spec = ["Gem::Specification.new do |s|"]
|
@@ -39,6 +39,6 @@ end
|
|
39
39
|
|
40
40
|
desc "Install acts_as_revisable"
|
41
41
|
task :install => :repackage do
|
42
|
-
options =
|
42
|
+
options = WithoutScope::ActsAsRevisable::GemSpecOptions::HASH.clone
|
43
43
|
sh %{sudo gem install pkg/#{options[:name]}-#{spec.version} --no-rdoc --no-ri}
|
44
44
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module
|
1
|
+
module WithoutScope
|
2
2
|
module ActsAsRevisable
|
3
3
|
# This module is mixed into the revision and revisable classes.
|
4
4
|
#
|
@@ -18,17 +18,12 @@ module FatJam
|
|
18
18
|
base.class_inheritable_hash :revisable_current_states
|
19
19
|
base.revisable_current_states = {}
|
20
20
|
|
21
|
-
class << base
|
22
|
-
alias_method_chain :instantiate, :revisable
|
23
|
-
end
|
24
|
-
|
25
21
|
base.instance_eval do
|
26
22
|
define_callbacks :before_branch, :after_branch
|
27
23
|
has_many :branches, (revisable_options.revision_association_options || {}).merge({:class_name => base.class_name, :foreign_key => :revisable_branched_from_id})
|
28
24
|
|
29
25
|
belongs_to :branch_source, :class_name => base.class_name, :foreign_key => :revisable_branched_from_id
|
30
26
|
after_save :execute_blocks_after_save
|
31
|
-
disable_revisable_scope :branch_source, :branches
|
32
27
|
end
|
33
28
|
end
|
34
29
|
|
@@ -151,7 +146,11 @@ module FatJam
|
|
151
146
|
|
152
147
|
# Accessor for revisable_number just to make external API more pleasant.
|
153
148
|
def revision_number
|
154
|
-
self[:revisable_number]
|
149
|
+
self[:revisable_number] ||= 0
|
150
|
+
end
|
151
|
+
|
152
|
+
def revision_number=(value)
|
153
|
+
self[:revisable_number] = value
|
155
154
|
end
|
156
155
|
|
157
156
|
def diffs(what)
|
@@ -163,32 +162,11 @@ module FatJam
|
|
163
162
|
end
|
164
163
|
end
|
165
164
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
assoc = self.class.reflect_on_association(#{a.inspect})
|
172
|
-
models = [self.class]
|
173
|
-
|
174
|
-
if [:has_many, :has_one].member? assoc.macro
|
175
|
-
models << (assoc.options[:class_name] ? assoc.options[:class_name] : #{a.inspect}.to_s.singularize.camelize).constantize
|
176
|
-
end
|
177
|
-
|
178
|
-
begin
|
179
|
-
models.each {|m| m.scoped_model_enabled = false}
|
180
|
-
if associated = #{a.to_s}_without_open_scope(*args, &block)
|
181
|
-
associated.reload
|
182
|
-
end
|
183
|
-
ensure
|
184
|
-
models.each {|m| m.scoped_model_enabled = true}
|
185
|
-
end
|
186
|
-
end
|
187
|
-
EOT
|
188
|
-
alias_method_chain a, :open_scope
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
165
|
+
def deleted?
|
166
|
+
self.revisable_deleted_at.present?
|
167
|
+
end
|
168
|
+
|
169
|
+
module ClassMethods
|
192
170
|
# Returns true if the revision should clone the given column.
|
193
171
|
def revisable_should_clone_column?(col) #:nodoc:
|
194
172
|
return false if (REVISABLE_SYSTEM_COLUMNS + REVISABLE_UNREVISABLE_COLUMNS).member? col
|
@@ -198,12 +176,12 @@ module FatJam
|
|
198
176
|
# acts_as_revisable's override for instantiate so we can
|
199
177
|
# return the appropriate type of model based on whether
|
200
178
|
# or not the record is the current record.
|
201
|
-
def
|
179
|
+
def instantiate(record) #:nodoc:
|
202
180
|
is_current = columns_hash["revisable_is_current"].type_cast(
|
203
181
|
record["revisable_is_current"])
|
204
182
|
|
205
183
|
if (is_current && self == self.revisable_class) || (!is_current && self == self.revision_class)
|
206
|
-
return
|
184
|
+
return super(record)
|
207
185
|
end
|
208
186
|
|
209
187
|
object = if is_current
|
@@ -1,19 +1,19 @@
|
|
1
|
-
module
|
1
|
+
module WithoutScope
|
2
2
|
module ActsAsRevisable
|
3
3
|
module Deletable
|
4
4
|
def self.included(base)
|
5
5
|
base.instance_eval do
|
6
|
-
|
6
|
+
define_callbacks :before_revise_on_destroy, :after_revise_on_destroy
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
10
|
+
def destroy
|
11
11
|
now = Time.now
|
12
12
|
|
13
13
|
prev = self.revisions.first
|
14
14
|
self.revisable_deleted_at = now
|
15
15
|
self.revisable_is_current = false
|
16
|
-
|
16
|
+
|
17
17
|
self.revisable_current_at = if prev
|
18
18
|
prev.update_attribute(:revisable_revised_at, now)
|
19
19
|
prev.revisable_revised_at + 1.second
|
@@ -22,7 +22,11 @@ module FatJam
|
|
22
22
|
end
|
23
23
|
|
24
24
|
self.revisable_revised_at = self.revisable_deleted_at
|
25
|
-
|
25
|
+
|
26
|
+
return false unless run_callbacks(:before_revise_on_destroy) { |r, o| r == false}
|
27
|
+
returning(self.save(:without_revision => true)) do
|
28
|
+
run_callbacks(:after_revise_on_destroy)
|
29
|
+
end
|
26
30
|
end
|
27
31
|
end
|
28
32
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module
|
1
|
+
module WithoutScope
|
2
2
|
module ActsAsRevisable
|
3
3
|
|
4
4
|
# This module is mixed into the revision classes.
|
@@ -18,8 +18,6 @@ module FatJam
|
|
18
18
|
base.send(:extend, ClassMethods)
|
19
19
|
|
20
20
|
class << base
|
21
|
-
alias_method_chain :find, :revisable
|
22
|
-
alias_method_chain :with_scope, :revisable
|
23
21
|
attr_accessor :revisable_revision_class, :revisable_columns
|
24
22
|
end
|
25
23
|
|
@@ -30,21 +28,24 @@ module FatJam
|
|
30
28
|
attr_accessor :revisable_new_params, :revisable_revision
|
31
29
|
|
32
30
|
define_callbacks :before_revise, :after_revise, :before_revert, :after_revert, :before_changeset, :after_changeset, :after_branch_created
|
33
|
-
|
34
|
-
alias_method_chain :save, :revisable
|
35
|
-
alias_method_chain :save!, :revisable
|
36
|
-
|
31
|
+
|
37
32
|
before_create :before_revisable_create
|
38
33
|
before_update :before_revisable_update
|
39
34
|
after_update :after_revisable_update
|
40
35
|
after_save :clear_revisable_shared_objects!, :unless => :is_reverting?
|
41
36
|
|
42
|
-
|
37
|
+
default_scope :conditions => {:revisable_is_current => true}
|
43
38
|
|
44
39
|
[:revisions, revisions_association_name.to_sym].each do |assoc|
|
45
40
|
has_many assoc, (revisable_options.revision_association_options || {}).merge({:class_name => revision_class_name, :foreign_key => :revisable_original_id, :order => "#{quoted_table_name}.#{connection.quote_column_name(:revisable_number)} DESC", :dependent => :destroy})
|
46
41
|
end
|
47
42
|
end
|
43
|
+
|
44
|
+
if !Object.const_defined?(base.revision_class_name) && base.revisable_options.generate_revision_class?
|
45
|
+
Object.const_set(base.revision_class_name, Class.new(ActiveRecord::Base)).class_eval do
|
46
|
+
acts_as_revision
|
47
|
+
end
|
48
|
+
end
|
48
49
|
end
|
49
50
|
|
50
51
|
# Finds a specific revision of self.
|
@@ -80,7 +81,7 @@ module FatJam
|
|
80
81
|
revisions.first
|
81
82
|
when Time
|
82
83
|
revisions.find(:first, :conditions => ["? >= ? and ? <= ?", :revisable_revised_at, by, :revisable_current_at, by])
|
83
|
-
when self
|
84
|
+
when self.revisable_number
|
84
85
|
self
|
85
86
|
else
|
86
87
|
revisions.find_by_revisable_number(by)
|
@@ -274,23 +275,23 @@ module FatJam
|
|
274
275
|
end
|
275
276
|
|
276
277
|
# acts_as_revisable's override for ActiveRecord::Base's #save!
|
277
|
-
def
|
278
|
+
def save!(*args) #:nodoc:
|
278
279
|
self.revisable_new_params ||= args.extract_options!
|
279
280
|
self.no_revision! if self.revisable_new_params.delete :without_revision
|
280
|
-
|
281
|
+
super
|
281
282
|
end
|
282
283
|
|
283
284
|
# acts_as_revisable's override for ActiveRecord::Base's #save
|
284
|
-
def
|
285
|
+
def save(*args) #:nodoc:
|
285
286
|
self.revisable_new_params ||= args.extract_options!
|
286
287
|
self.no_revision! if self.revisable_new_params.delete :without_revision
|
287
|
-
|
288
|
+
super(args)
|
288
289
|
end
|
289
290
|
|
290
291
|
# Set some defaults for a newly created +Revisable+ instance.
|
291
292
|
def before_revisable_create #:nodoc:
|
292
293
|
self[:revisable_is_current] = true
|
293
|
-
self
|
294
|
+
self.revision_number ||= 0
|
294
295
|
end
|
295
296
|
|
296
297
|
# Checks whether or not a +Revisable+ should be revised.
|
@@ -347,9 +348,9 @@ module FatJam
|
|
347
348
|
|
348
349
|
rev.revisable_original_id = self.id
|
349
350
|
|
350
|
-
new_revision_number = revisions.maximum(:revisable_number) + 1 rescue self.
|
351
|
-
rev.
|
352
|
-
self.
|
351
|
+
new_revision_number = revisions.maximum(:revisable_number) + 1 rescue self.revision_number
|
352
|
+
rev.revision_number = new_revision_number
|
353
|
+
self.revision_number = new_revision_number + 1
|
353
354
|
|
354
355
|
self.class.column_names.each do |col|
|
355
356
|
next unless self.class.revisable_should_clone_column? col
|
@@ -402,15 +403,15 @@ module FatJam
|
|
402
403
|
# with_scope(:with_revisions => true) do
|
403
404
|
# ...
|
404
405
|
# end
|
405
|
-
def
|
406
|
+
def with_scope(*args, &block) #:nodoc:
|
406
407
|
options = (args.grep(Hash).first || {})[:find]
|
407
408
|
|
408
409
|
if options && options.delete(:with_revisions)
|
409
|
-
|
410
|
-
|
410
|
+
with_exclusive_scope do
|
411
|
+
super(*args, &block)
|
411
412
|
end
|
412
413
|
else
|
413
|
-
|
414
|
+
super(*args, &block)
|
414
415
|
end
|
415
416
|
end
|
416
417
|
|
@@ -420,26 +421,18 @@ module FatJam
|
|
420
421
|
# ==== Example
|
421
422
|
#
|
422
423
|
# find(:all, :with_revisions => true)
|
423
|
-
def
|
424
|
+
def find(*args) #:nodoc:
|
424
425
|
options = args.grep(Hash).first
|
425
426
|
|
426
427
|
if options && options.delete(:with_revisions)
|
427
|
-
|
428
|
-
|
428
|
+
with_exclusive_scope do
|
429
|
+
super(*args)
|
429
430
|
end
|
430
431
|
else
|
431
|
-
|
432
|
+
super(*args)
|
432
433
|
end
|
433
434
|
end
|
434
|
-
|
435
|
-
# Equivalent to:
|
436
|
-
# find(..., :with_revisions => true)
|
437
|
-
def find_with_revisions(*args)
|
438
|
-
args << {} if args.grep(Hash).blank?
|
439
|
-
args.grep(Hash).first.update({:with_revisions => true})
|
440
|
-
find_with_revisable(*args)
|
441
|
-
end
|
442
|
-
|
435
|
+
|
443
436
|
# Returns the +revision_class_name+ as configured in
|
444
437
|
# +acts_as_revisable+.
|
445
438
|
def revision_class_name #:nodoc:
|
@@ -449,7 +442,7 @@ module FatJam
|
|
449
442
|
# Returns the actual +Revision+ class based on the
|
450
443
|
# #revision_class_name.
|
451
444
|
def revision_class #:nodoc:
|
452
|
-
self.revisable_revision_class ||= revision_class_name.constantize
|
445
|
+
self.revisable_revision_class ||= self.revision_class_name.constantize
|
453
446
|
end
|
454
447
|
|
455
448
|
# Returns the revisable_class which in this case is simply +self+.
|
@@ -460,7 +453,7 @@ module FatJam
|
|
460
453
|
# Returns the name of the association acts_as_revisable
|
461
454
|
# creates.
|
462
455
|
def revisions_association_name #:nodoc:
|
463
|
-
revision_class_name.pluralize.
|
456
|
+
revision_class_name.pluralize.underscore
|
464
457
|
end
|
465
458
|
|
466
459
|
# Returns an Array of the columns that are watched for changes.
|
@@ -1,6 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module FatJam
|
1
|
+
module WithoutScope
|
4
2
|
module ActsAsRevisable
|
5
3
|
# This module is mixed into the revision classes.
|
6
4
|
#
|
@@ -20,14 +18,14 @@ module FatJam
|
|
20
18
|
|
21
19
|
base.instance_eval do
|
22
20
|
set_table_name(revisable_class.table_name)
|
23
|
-
|
24
|
-
|
25
|
-
CloneAssociations.clone_associations(revisable_class, self)
|
26
|
-
|
21
|
+
default_scope :conditions => {:revisable_is_current => false}
|
22
|
+
|
27
23
|
define_callbacks :before_restore, :after_restore
|
28
24
|
before_create :revision_setup
|
29
25
|
after_create :grab_my_branches
|
30
26
|
|
27
|
+
named_scope :deleted, :conditions => ["? is not null", :revisable_deleted_at]
|
28
|
+
|
31
29
|
[:current_revision, revisable_association_name.to_sym].each do |a|
|
32
30
|
belongs_to a, :class_name => revisable_class_name, :foreign_key => :revisable_original_id
|
33
31
|
end
|
@@ -111,7 +109,7 @@ module FatJam
|
|
111
109
|
# Returns the actual +Revisable+ class based on the
|
112
110
|
# #revisable_class_name.
|
113
111
|
def revisable_class #:nodoc:
|
114
|
-
self.revisable_revisable_class ||= revisable_class_name.constantize
|
112
|
+
self.revisable_revisable_class ||= self.revisable_class_name.constantize
|
115
113
|
end
|
116
114
|
|
117
115
|
# Returns the revision_class which in this case is simply +self+.
|
@@ -126,7 +124,7 @@ module FatJam
|
|
126
124
|
# Returns the name of the association acts_as_revision
|
127
125
|
# creates.
|
128
126
|
def revisable_association_name #:nodoc:
|
129
|
-
revisable_class_name.
|
127
|
+
revisable_class_name.underscore
|
130
128
|
end
|
131
129
|
|
132
130
|
# Returns an array of the associations that should be cloned.
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'acts_as_revisable/options'
|
2
|
+
require 'acts_as_revisable/quoted_columns'
|
2
3
|
require 'acts_as_revisable/acts/common'
|
3
4
|
require 'acts_as_revisable/acts/revision'
|
4
5
|
require 'acts_as_revisable/acts/revisable'
|
5
6
|
require 'acts_as_revisable/acts/deletable'
|
6
7
|
|
7
|
-
module
|
8
|
+
module WithoutScope
|
8
9
|
# define the columns used internall by AAR
|
9
10
|
REVISABLE_SYSTEM_COLUMNS = %w(revisable_original_id revisable_branched_from_id revisable_number revisable_name revisable_type revisable_current_at revisable_revised_at revisable_deleted_at revisable_is_current)
|
10
11
|
|
@@ -44,6 +45,7 @@ module FatJam
|
|
44
45
|
self.revisable_options = Options.new(options, &block)
|
45
46
|
|
46
47
|
self.send(:include, Common)
|
48
|
+
self.send(:include, WithoutScope::QuotedColumnConditions)
|
47
49
|
end
|
48
50
|
end
|
49
51
|
end
|
@@ -1,14 +1,14 @@
|
|
1
|
-
module
|
1
|
+
module WithoutScope #:nodoc:
|
2
2
|
module ActsAsRevisable
|
3
3
|
class GemSpecOptions
|
4
4
|
HASH = {
|
5
|
-
:name => "
|
6
|
-
:version =>
|
5
|
+
:name => "rich-acts_as_revisable",
|
6
|
+
:version => WithoutScope::ActsAsRevisable::VERSION::STRING,
|
7
7
|
:summary => "acts_as_revisable enables revision tracking, querying, reverting and branching of ActiveRecord models. Inspired by acts_as_versioned.",
|
8
|
-
:email => "
|
9
|
-
:homepage => "http://github.com/
|
8
|
+
:email => "rich@withoutscope.com",
|
9
|
+
:homepage => "http://github.com/rich/acts_as_revisable",
|
10
10
|
:has_rdoc => true,
|
11
|
-
:authors => ["Rich Cavanaugh
|
11
|
+
:authors => ["Rich Cavanaugh", "Stephen Caudill"],
|
12
12
|
:files => %w( LICENSE README.rdoc Rakefile ) + Dir["{spec,lib,generators,rails}/**/*"],
|
13
13
|
:rdoc_options => ["--main", "README.rdoc"],
|
14
14
|
:extra_rdoc_files => ["README.rdoc", "LICENSE"]
|
@@ -10,17 +10,13 @@
|
|
10
10
|
# This is consistent with Rails and Ruby where symbols are used to
|
11
11
|
# represent methods. Only a symbol matching a column name will
|
12
12
|
# trigger this beavior.
|
13
|
-
module
|
13
|
+
module WithoutScope::QuotedColumnConditions
|
14
14
|
def self.included(base)
|
15
15
|
base.send(:extend, ClassMethods)
|
16
|
-
|
17
|
-
class << base
|
18
|
-
alias_method_chain :quote_bound_value, :quoted_column
|
19
|
-
end
|
20
16
|
end
|
21
17
|
|
22
18
|
module ClassMethods
|
23
|
-
def
|
19
|
+
def quote_bound_value(value)
|
24
20
|
if value.is_a?(Symbol) && column_names.member?(value.to_s)
|
25
21
|
# code borrowed from sanitize_sql_hash_for_conditions
|
26
22
|
attr = value.to_s
|
@@ -29,7 +25,7 @@ module FatJam::QuotedColumnConditions
|
|
29
25
|
return "#{table_name}.#{connection.quote_column_name(attr)}"
|
30
26
|
end
|
31
27
|
|
32
|
-
|
28
|
+
super(value)
|
33
29
|
end
|
34
30
|
end
|
35
31
|
end
|
data/lib/acts_as_revisable.rb
CHANGED
@@ -5,10 +5,6 @@ require 'activesupport' unless defined? ActiveSupport
|
|
5
5
|
require 'activerecord' unless defined? ActiveRecord
|
6
6
|
|
7
7
|
require 'acts_as_revisable/version.rb'
|
8
|
-
require 'acts_as_revisable/acts/scoped_model'
|
9
|
-
require 'acts_as_revisable/quoted_columns'
|
10
8
|
require 'acts_as_revisable/base'
|
11
9
|
|
12
|
-
ActiveRecord::Base.send(:include,
|
13
|
-
ActiveRecord::Base.send(:include, FatJam::QuotedColumnConditions)
|
14
|
-
ActiveRecord::Base.send(:include, FatJam::ActsAsRevisable)
|
10
|
+
ActiveRecord::Base.send(:include, WithoutScope::ActsAsRevisable)
|
data/spec/associations_spec.rb
CHANGED
data/spec/branch_spec.rb
CHANGED
data/spec/find_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe WithoutScope::ActsAsRevisable do
|
4
4
|
after(:each) do
|
5
5
|
cleanup_db
|
6
6
|
end
|
@@ -18,21 +18,13 @@ describe FatJam::ActsAsRevisable do
|
|
18
18
|
it "should accept the :with_revisions options" do
|
19
19
|
lambda { Project.find(:all, :with_revisions => true) }.should_not raise_error
|
20
20
|
end
|
21
|
-
|
22
|
-
it "should provide find_with_revisions" do
|
23
|
-
lambda { Project.find_with_revisions(:all) }.should_not raise_error
|
24
|
-
end
|
25
|
-
|
21
|
+
|
26
22
|
it "should find current and revisions with the :with_revisions option" do
|
27
23
|
Project.find(:all, :with_revisions => true).size.should == 2
|
28
24
|
end
|
29
|
-
|
30
|
-
it "should find current and revisions with the find_with_revisions method" do
|
31
|
-
Project.find_with_revisions(:all).size.should == 2
|
32
|
-
end
|
33
|
-
|
25
|
+
|
34
26
|
it "should find revisions with conditions" do
|
35
|
-
Project.
|
27
|
+
Project.find(:all, :conditions => {:name => "Rich"}, :with_revisions => true).should == [@project1.find_revision(:previous)]
|
36
28
|
end
|
37
29
|
end
|
38
30
|
end
|
data/spec/general_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe WithoutScope::ActsAsRevisable do
|
4
4
|
after(:each) do
|
5
5
|
cleanup_db
|
6
6
|
end
|
@@ -9,6 +9,12 @@ describe FatJam::ActsAsRevisable do
|
|
9
9
|
@project = Project.create(:name => "Rich", :notes => "this plugin's author")
|
10
10
|
end
|
11
11
|
|
12
|
+
describe "with auto-generated revision class" do
|
13
|
+
it "should have a revision class" do
|
14
|
+
Foo.revision_class.should == FooRevision
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
12
18
|
describe "without revisions" do
|
13
19
|
it "should have a revision_number of zero" do
|
14
20
|
@project.revision_number.should be_zero
|
data/spec/options_spec.rb
CHANGED
@@ -38,10 +38,10 @@ shared_examples_for "common Options usage" do
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
describe
|
41
|
+
describe WithoutScope::ActsAsRevisable::Options do
|
42
42
|
describe "with hash options" do
|
43
43
|
before(:each) do
|
44
|
-
@options =
|
44
|
+
@options = WithoutScope::ActsAsRevisable::Options.new :one => 1, :yes => true, :no => false, :arr => [1,2,3]
|
45
45
|
end
|
46
46
|
|
47
47
|
it_should_behave_like "common Options usage"
|
@@ -49,7 +49,7 @@ describe FatJam::ActsAsRevisable::Options do
|
|
49
49
|
|
50
50
|
describe "with block options" do
|
51
51
|
before(:each) do
|
52
|
-
@options =
|
52
|
+
@options = WithoutScope::ActsAsRevisable::Options.new do
|
53
53
|
one 1
|
54
54
|
yes true
|
55
55
|
arr [1,2,3]
|
@@ -61,7 +61,7 @@ describe FatJam::ActsAsRevisable::Options do
|
|
61
61
|
|
62
62
|
describe "with both block and hash options" do
|
63
63
|
before(:each) do
|
64
|
-
@options =
|
64
|
+
@options = WithoutScope::ActsAsRevisable::Options.new(:yes => true, :arr => [1,2,3]) do
|
65
65
|
one 1
|
66
66
|
end
|
67
67
|
end
|
@@ -70,7 +70,7 @@ describe FatJam::ActsAsRevisable::Options do
|
|
70
70
|
|
71
71
|
describe "the block should override the hash" do
|
72
72
|
before(:each) do
|
73
|
-
@options =
|
73
|
+
@options = WithoutScope::ActsAsRevisable::Options.new(:yes => false, :one => 10, :arr => [1,2,3,4,5]) do
|
74
74
|
one 1
|
75
75
|
yes true
|
76
76
|
arr [1,2,3]
|
data/spec/quoted_columns_spec.rb
CHANGED
@@ -14,6 +14,6 @@ describe "the quoted_columns extension" do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
it "should not quote strings any differently" do
|
17
|
-
Project.send(:quote_bound_value, "what").should ==
|
17
|
+
Project.send(:quote_bound_value, "what").should == ActiveRecord::Base.send(:quote_bound_value, "what")
|
18
18
|
end
|
19
19
|
end
|
data/spec/revert_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -36,6 +36,15 @@ def setup_db
|
|
36
36
|
t.datetime :revisable_current_at, :revisable_revised_at, :revisable_deleted_at
|
37
37
|
t.timestamps
|
38
38
|
end
|
39
|
+
|
40
|
+
create_table :foos do |t|
|
41
|
+
t.string :name, :revisable_name, :revisable_type
|
42
|
+
t.text :notes
|
43
|
+
t.boolean :revisable_is_current
|
44
|
+
t.integer :revisable_original_id, :revisable_branched_from_id, :revisable_number, :project_id
|
45
|
+
t.datetime :revisable_current_at, :revisable_revised_at, :revisable_deleted_at
|
46
|
+
t.timestamps
|
47
|
+
end
|
39
48
|
end
|
40
49
|
end
|
41
50
|
|
@@ -52,6 +61,7 @@ class Person < ActiveRecord::Base
|
|
52
61
|
|
53
62
|
acts_as_revisable do
|
54
63
|
revision_class_name "OldPerson"
|
64
|
+
on_delete :revise
|
55
65
|
end
|
56
66
|
end
|
57
67
|
|
@@ -76,4 +86,8 @@ class Session < ActiveRecord::Base
|
|
76
86
|
revisable_class_name "Project"
|
77
87
|
clone_associations :all
|
78
88
|
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Foo < ActiveRecord::Base
|
92
|
+
acts_as_revisable :generate_revision_class => true
|
79
93
|
end
|
metadata
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rich-acts_as_revisable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Rich Cavanaugh
|
8
|
-
- Stephen Caudill
|
7
|
+
- Rich Cavanaugh
|
8
|
+
- Stephen Caudill
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date:
|
13
|
+
date: 2009-04-01 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
17
17
|
description:
|
18
|
-
email:
|
18
|
+
email: rich@withoutscope.com
|
19
19
|
executables: []
|
20
20
|
|
21
21
|
extensions: []
|
@@ -56,7 +56,7 @@ files:
|
|
56
56
|
- generators/revisable_migration/templates/migration.rb
|
57
57
|
- rails/init.rb
|
58
58
|
has_rdoc: true
|
59
|
-
homepage: http://github.com/
|
59
|
+
homepage: http://github.com/rich/acts_as_revisable/tree/master
|
60
60
|
post_install_message:
|
61
61
|
rdoc_options:
|
62
62
|
- --main
|
@@ -1,75 +0,0 @@
|
|
1
|
-
module FatJam
|
2
|
-
module ActsAsScopedModel
|
3
|
-
def self.included(base)
|
4
|
-
base.send(:extend, ClassMethods)
|
5
|
-
end
|
6
|
-
|
7
|
-
module ClassMethods
|
8
|
-
SCOPED_METHODS = %w(construct_calculation_sql construct_finder_sql update_all delete_all destroy_all).freeze
|
9
|
-
|
10
|
-
def call_method_with_static_scope(meth, args)
|
11
|
-
return send(meth, *args) unless self.scoped_model_enabled?
|
12
|
-
|
13
|
-
with_scope(self.scoped_model_static_scope) do
|
14
|
-
send(meth, *args)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
SCOPED_METHODS.each do |m|
|
19
|
-
module_eval <<-EVAL
|
20
|
-
def #{m}_with_static_scope(*args)
|
21
|
-
call_method_with_static_scope(:#{m}_without_static_scope, args)
|
22
|
-
end
|
23
|
-
EVAL
|
24
|
-
end
|
25
|
-
|
26
|
-
def without_model_scope
|
27
|
-
return unless block_given?
|
28
|
-
|
29
|
-
begin
|
30
|
-
self.scoped_model_enabled = false
|
31
|
-
rv = yield
|
32
|
-
ensure
|
33
|
-
self.scoped_model_enabled = true
|
34
|
-
end
|
35
|
-
|
36
|
-
rv
|
37
|
-
end
|
38
|
-
|
39
|
-
def disable_model_scope!
|
40
|
-
self.scoped_model_disable_count += 1
|
41
|
-
end
|
42
|
-
|
43
|
-
def enable_model_scope!
|
44
|
-
self.scoped_model_disable_count -= 1
|
45
|
-
end
|
46
|
-
|
47
|
-
def scoped_model_enabled?
|
48
|
-
self.scoped_model_disable_count == 0
|
49
|
-
end
|
50
|
-
|
51
|
-
def scoped_model_enabled
|
52
|
-
self.scoped_model_enabled?
|
53
|
-
end
|
54
|
-
|
55
|
-
def scoped_model_enabled=(value)
|
56
|
-
if value == false
|
57
|
-
disable_model_scope!
|
58
|
-
else
|
59
|
-
enable_model_scope!
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def acts_as_scoped_model(*args)
|
64
|
-
class << self
|
65
|
-
attr_accessor :scoped_model_static_scope, :scoped_model_disable_count
|
66
|
-
SCOPED_METHODS.each do |m|
|
67
|
-
alias_method_chain m.to_sym, :static_scope
|
68
|
-
end
|
69
|
-
end
|
70
|
-
self.scoped_model_disable_count = 0
|
71
|
-
self.scoped_model_static_scope = args.extract_options!
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# This module encapsulates the methods used by ActsAsRevisable
|
2
|
-
# for cloning associations from one model to another.
|
3
|
-
module FatJam
|
4
|
-
module ActsAsRevisable
|
5
|
-
module CloneAssociations
|
6
|
-
class << self
|
7
|
-
def clone_associations(from, to)
|
8
|
-
return unless from.descends_from_active_record? && to.descends_from_active_record?
|
9
|
-
|
10
|
-
to.revision_cloned_associations.each do |key|
|
11
|
-
assoc = from.reflect_on_association(key)
|
12
|
-
meth = "clone_#{assoc.macro.to_s}_association"
|
13
|
-
meth = "clone_association" unless respond_to? meth
|
14
|
-
send(meth, assoc, to)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def clone_association(association, to)
|
19
|
-
options = association.options.clone
|
20
|
-
options[:foreign_key] ||= "revisable_original_id"
|
21
|
-
to.send(association.macro, association.name, options)
|
22
|
-
end
|
23
|
-
|
24
|
-
def clone_belongs_to_association(association, to)
|
25
|
-
to.send(association.macro, association.name, association.options.clone)
|
26
|
-
end
|
27
|
-
|
28
|
-
def clone_has_many_association(association, to)
|
29
|
-
options = association.options.clone
|
30
|
-
options[:association_foreign_key] ||= "revisable_original_id"
|
31
|
-
to.send(association.macro, association.name, options)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|