acts_as_edition 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README +114 -0
- data/Rakefile +24 -0
- data/acts_as_edition.gemspec +24 -0
- data/init.rb +3 -0
- data/lib/acts_as_edition/version.rb +3 -0
- data/lib/acts_as_edition.rb +95 -0
- data/test/acts_as_edition_test.rb +219 -0
- data/test/database.yml +3 -0
- data/test/fixtures/abbreviation.rb +6 -0
- data/test/fixtures/abbreviations.yml +11 -0
- data/test/fixtures/alphabet.rb +2 -0
- data/test/fixtures/alphabets.yml +2 -0
- data/test/fixtures/author.rb +3 -0
- data/test/fixtures/authors.yml +8 -0
- data/test/fixtures/countries.yml +3 -0
- data/test/fixtures/country.rb +3 -0
- data/test/fixtures/guide.rb +35 -0
- data/test/fixtures/guides.yml +20 -0
- data/test/fixtures/imprint.rb +4 -0
- data/test/fixtures/imprints.yml +2 -0
- data/test/fixtures/law.rb +6 -0
- data/test/fixtures/laws.yml +12 -0
- data/test/fixtures/map.rb +4 -0
- data/test/fixtures/maps.yml +3 -0
- data/test/fixtures/place.rb +7 -0
- data/test/fixtures/places.yml +12 -0
- data/test/fixtures/retailer.rb +3 -0
- data/test/fixtures/retailers.yml +7 -0
- data/test/schema.rb +69 -0
- data/test/test_helper.rb +38 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 388edb3ce642edfbaf91712cc920565da99eeea0
|
4
|
+
data.tar.gz: 5e0c5d7465099a15ddd8da0af55acd76c43d391e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e5a69c47e155918e75720f5ad38017d3f0fb1046df4089cdb248c5615f95bfbb0425cf1353b7c86857600a100bb3ed1d1cceafa8a7524e7a4a38f8dcf1edcb89
|
7
|
+
data.tar.gz: e7a587b15fe382e379e0e6f66f72d6592d7232ca4d25f524ab63f61c23b9cd64e097537d549a247028289d9f994f2b6a48389bca5a811a223c953ac6bdbd8535
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Virginia Department of Education
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
ActsAsEdition
|
2
|
+
=============
|
3
|
+
|
4
|
+
acts_as_edition is a ruby gem for creating new editions of a tree (think
|
5
|
+
document) of ActiveRecord models.
|
6
|
+
|
7
|
+
Each new edition tree is a deep clone of the previous tree. The edition tree is
|
8
|
+
defined using an acts_as_edition declaration on each model in the tree. Using
|
9
|
+
the resources option, acts_as_edition supports management of relationships
|
10
|
+
between models in the edition tree and those outside it. A selection of hooks
|
11
|
+
provide additional control over the cloning process. Conditions can be
|
12
|
+
specified on models in the tree to determine when/if they are cloned. Finally,
|
13
|
+
each object in the tree maintains a simple belongs_to relationship with its
|
14
|
+
ancestor in the previous edition.
|
15
|
+
|
16
|
+
If you are considering acts_as_edition, you should also evaluate
|
17
|
+
acts_as_versioned and deep_cloning:
|
18
|
+
|
19
|
+
http://github.com/technoweenie/acts_as_versioned
|
20
|
+
http://github.com/DefV/deep_cloning
|
21
|
+
|
22
|
+
Install
|
23
|
+
=======
|
24
|
+
|
25
|
+
gem install acts_as_versioned
|
26
|
+
|
27
|
+
Example
|
28
|
+
=======
|
29
|
+
|
30
|
+
First, add ancestor_id column to every table in the tree, using a migration
|
31
|
+
thusly:
|
32
|
+
|
33
|
+
class AddEditionMigration < ActiveRecord::Migration
|
34
|
+
def self.up
|
35
|
+
add_column :mymodel, :ancestor_id, :integer
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.down
|
39
|
+
remove_column :mymodel, :ancestor_id
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
rake db:migrate
|
44
|
+
|
45
|
+
Then declare acts_as_edition in the model class:
|
46
|
+
|
47
|
+
class Guide < ActiveRecord::Base
|
48
|
+
acts_as_edition :edition_chain => [:abbreviation, :imprint, :places],
|
49
|
+
:resources => [:country, :retailers, :authors],
|
50
|
+
:pre_hook => :unpublish_self,
|
51
|
+
:after_clone => :increment_descendant_year,
|
52
|
+
:post_hook => :publish_descendant
|
53
|
+
:conditions => { :returns_true => true }
|
54
|
+
has_one :abbreviation
|
55
|
+
belongs_to :imprint
|
56
|
+
has_many :places
|
57
|
+
has_one :country
|
58
|
+
has_many :retailers
|
59
|
+
has_and_belongs_to_many :authors
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def unpublish_self
|
64
|
+
self.published = false
|
65
|
+
self.save!
|
66
|
+
end
|
67
|
+
|
68
|
+
def increment_descendant_year
|
69
|
+
self.year = (self.year.to_i + 1).to_s
|
70
|
+
self.save!
|
71
|
+
end
|
72
|
+
|
73
|
+
def publish_descendant
|
74
|
+
self.descendant.published = true
|
75
|
+
self.descendant.save!
|
76
|
+
end
|
77
|
+
|
78
|
+
def returns_true
|
79
|
+
name != 'Noclonelandia'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Abbreviation < ActiveRecord::Base
|
84
|
+
. . .
|
85
|
+
end
|
86
|
+
|
87
|
+
And so on -- these models are taken from the tests if you need more context.
|
88
|
+
|
89
|
+
To clone:
|
90
|
+
|
91
|
+
new_edition = Guide.first.clone_edition!
|
92
|
+
new_edition.ancestor # == Guide.first
|
93
|
+
Guide.first.descendant # == new_edition
|
94
|
+
|
95
|
+
Attributes listed in edition_chain are cloned. Note that their model classes
|
96
|
+
must also have acts_as_edition declarations.
|
97
|
+
|
98
|
+
Atributes listed in resources are not cloned. The relationship between these
|
99
|
+
resource objects and objects in the previous edition tree are reestablished in
|
100
|
+
the new edition tree. NOTE: the relatishionship of the resource to the original
|
101
|
+
object may be broken. The exception is resources in a habtm relationship with
|
102
|
+
edition objects. In these cases, the relationship to the new edition is
|
103
|
+
additive.
|
104
|
+
|
105
|
+
pre_hook and post_hook are called at the beginning and end of the process on
|
106
|
+
the original object. The after_clone hook is called *on the newly cloned
|
107
|
+
object* before it is saved to the database.
|
108
|
+
|
109
|
+
If an object does not meet all conditions specified in the conditions
|
110
|
+
options hash, clone_edition! on that object will return nil and the object will
|
111
|
+
not be copied into the new tree.
|
112
|
+
|
113
|
+
Copyright (c) 2010, 2013 Virginia Department of Education, released under the MIT
|
114
|
+
license
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rdoc/task'
|
5
|
+
|
6
|
+
desc 'Default: run unit tests.'
|
7
|
+
task :default => :test
|
8
|
+
|
9
|
+
desc 'Test the acts_as_edition plugin.'
|
10
|
+
Rake::TestTask.new(:test) do |t|
|
11
|
+
t.libs << 'lib'
|
12
|
+
t.libs << 'test'
|
13
|
+
t.pattern = 'test/**/*_test.rb'
|
14
|
+
t.verbose = true
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Generate documentation for the acts_as_edition plugin.'
|
18
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
19
|
+
rdoc.rdoc_dir = 'rdoc'
|
20
|
+
rdoc.title = 'ActsAsEdition'
|
21
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
22
|
+
rdoc.rdoc_files.include('README')
|
23
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'acts_as_edition/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "acts_as_edition"
|
8
|
+
spec.version = ActsAsEdition::VERSION
|
9
|
+
spec.authors = ["Lee Capps"]
|
10
|
+
spec.email = ["himself@leecapps.com"]
|
11
|
+
spec.description = %q{Editions for ActiveRecord models}
|
12
|
+
spec.summary = %q{acts_as_edition is a ruby gem for creating new editions of trees of ActiveRecord objects.}
|
13
|
+
spec.homepage = "https://github.com/jlcapps/acts_as_edition"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency('activerecord', ["~> 3.2.13"])
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "sqlite3"
|
24
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# ActsAsEdition
|
2
|
+
module ActsAsEdition
|
3
|
+
# :pre_hook and :post_hook run on the original object. :after_clone
|
4
|
+
# runs on the cloned object before it is saved.
|
5
|
+
# Attributes listed in :edition_chain are cloned and related to
|
6
|
+
# self.descendant. Those listed in :resources are related to the
|
7
|
+
# self.descendant and are otherwise left as is.
|
8
|
+
# NOTE: habtm realtionships are handled differently from other
|
9
|
+
# resources. For the resource, the change is additive. The resource
|
10
|
+
# will have a relationship with original *and* cloned edition object.
|
11
|
+
def acts_as_edition(options = {})
|
12
|
+
belongs_to :ancestor, :class_name => name, :foreign_key => :ancestor_id
|
13
|
+
has_one :descendant, :class_name => name, :foreign_key => :ancestor_id
|
14
|
+
|
15
|
+
cattr_accessor :edition_chain, :resources, :pre_hook, :after_clone,
|
16
|
+
:post_hook, :conditions
|
17
|
+
self.edition_chain = Array((options[:edition_chain] || []))
|
18
|
+
self.resources = Array((options[:resources] || []))
|
19
|
+
self.pre_hook = options[:pre_hook]
|
20
|
+
self.after_clone = options[:after_clone]
|
21
|
+
self.post_hook = options[:post_hook]
|
22
|
+
self.conditions = (options[:conditions] || {})
|
23
|
+
|
24
|
+
include InstanceMethods
|
25
|
+
end
|
26
|
+
|
27
|
+
module InstanceMethods
|
28
|
+
def clone_edition!
|
29
|
+
self.class.transaction do
|
30
|
+
self.send("#{self.pre_hook}") if self.pre_hook
|
31
|
+
cloned = ActiveRecord::VERSION::MAJOR==3 ? self.dup : self.clone
|
32
|
+
cloned.send("#{self.after_clone}") if self.after_clone
|
33
|
+
cloned.ancestor = self
|
34
|
+
cloned.save!
|
35
|
+
self.reload
|
36
|
+
clone_edition_chain
|
37
|
+
clone_resource_chain
|
38
|
+
self.send("#{self.post_hook}") if self.post_hook
|
39
|
+
cloned.save!
|
40
|
+
cloned.reload
|
41
|
+
end if aae_conditions_met
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def aae_conditions_met
|
47
|
+
self.conditions.keys.collect do |k|
|
48
|
+
self.send(k) == self.conditions[k]
|
49
|
+
end.all?
|
50
|
+
end
|
51
|
+
|
52
|
+
def clone_edition_chain
|
53
|
+
self.edition_chain.each do |association|
|
54
|
+
case self.class.reflect_on_association(association).macro
|
55
|
+
when :has_one, :belongs_to
|
56
|
+
cloned = (self.send(association) &&
|
57
|
+
(self.send(association).descendant ||
|
58
|
+
self.send(association).clone_edition!))
|
59
|
+
self.descendant.send("#{association}=", cloned) unless cloned.nil?
|
60
|
+
self.descendant.save!
|
61
|
+
when :has_many, :has_and_belongs_to_many
|
62
|
+
self.send(association) && self.send(association).each do |associated|
|
63
|
+
cloned = associated.descendant || associated.clone_edition!
|
64
|
+
unless (self.descendant.send("#{association}").include?(cloned) ||
|
65
|
+
cloned.nil?)
|
66
|
+
self.descendant.send("#{association}") << cloned
|
67
|
+
self.descendant.save!
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def clone_resource_chain
|
75
|
+
self.reload
|
76
|
+
self.resources.reject { |r| r.to_s.empty? }.each do |association|
|
77
|
+
case self.class.reflect_on_association(association).macro
|
78
|
+
when :has_one
|
79
|
+
resource = self.send(association)
|
80
|
+
self.descendant.send("#{association}=", resource)
|
81
|
+
self.descendant.save!
|
82
|
+
when :has_many, :has_and_belongs_to_many
|
83
|
+
resources = self.send(association)
|
84
|
+
resources.each do |resource|
|
85
|
+
self.descendant.send("#{association}") << resource
|
86
|
+
end
|
87
|
+
self.descendant.save!
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
ActiveRecord::Base.extend ActsAsEdition
|
95
|
+
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ActsAsEditionTest < ActiveSupport::TestCase
|
4
|
+
fixtures :all
|
5
|
+
|
6
|
+
test "should respond to #ancestor" do
|
7
|
+
assert_respond_to Guide.new, :ancestor
|
8
|
+
end
|
9
|
+
|
10
|
+
test "should respond to #descendant" do
|
11
|
+
assert_respond_to Guide.new, :descendant
|
12
|
+
end
|
13
|
+
|
14
|
+
test "#ancestor and #descendant work as expected" do
|
15
|
+
guide = guides(:scotland)
|
16
|
+
cloned = guide.clone
|
17
|
+
cloned.ancestor = guide
|
18
|
+
cloned.save!
|
19
|
+
assert_equal cloned.ancestor, guide
|
20
|
+
assert_equal guide.descendant, cloned
|
21
|
+
end
|
22
|
+
|
23
|
+
test "should respond to #clone_edition!" do
|
24
|
+
assert_respond_to guides(:scotland), :clone_edition!
|
25
|
+
end
|
26
|
+
|
27
|
+
test "#clone_edition! clones edition" do
|
28
|
+
orig = guides(:scotland)
|
29
|
+
cloned = orig.clone_edition!
|
30
|
+
assert_equal cloned.ancestor, orig
|
31
|
+
assert_equal orig.descendant, cloned
|
32
|
+
assert_equal orig.name, cloned.name
|
33
|
+
end
|
34
|
+
|
35
|
+
test ":edition_chain and :resources options are stored as class attrs" do
|
36
|
+
assert_respond_to guides(:scotland), :edition_chain
|
37
|
+
assert guides(:scotland).edition_chain.include? :abbreviation
|
38
|
+
assert_respond_to Law.new, :resources
|
39
|
+
end
|
40
|
+
|
41
|
+
test ":conditions is stored as class attr" do
|
42
|
+
assert_respond_to guides(:scotland), :conditions
|
43
|
+
assert guides(:scotland).conditions.keys.include? :returns_true
|
44
|
+
end
|
45
|
+
|
46
|
+
test "should not clone if conditions not met" do
|
47
|
+
assert_equal guides(:noclonelandia).clone_edition!, nil
|
48
|
+
end
|
49
|
+
|
50
|
+
test "guide and abbreviation are properly related" do
|
51
|
+
assert_equal guides(:scotland).abbreviation, abbreviations(:sco)
|
52
|
+
end
|
53
|
+
|
54
|
+
test "should clone has_one specified in edition_chain" do
|
55
|
+
orig = guides(:scotland)
|
56
|
+
cloned = orig.clone_edition!
|
57
|
+
assert_equal orig.abbreviation, cloned.abbreviation.ancestor
|
58
|
+
assert_equal orig.abbreviation.descendant, cloned.abbreviation
|
59
|
+
end
|
60
|
+
|
61
|
+
test "should not clone has_one in edition_chain if conditions not met" do
|
62
|
+
orig = guides(:england)
|
63
|
+
cloned = orig.clone_edition!
|
64
|
+
assert_equal orig.abbreviation.descendant, nil
|
65
|
+
assert_equal cloned.abbreviation, nil
|
66
|
+
end
|
67
|
+
|
68
|
+
test "should clone belongs_to specified in edition_chain" do
|
69
|
+
orig = guides(:scotland)
|
70
|
+
cloned = orig.clone_edition!
|
71
|
+
orig.reload
|
72
|
+
cloned.reload
|
73
|
+
assert_equal orig.imprint, orig.descendant.imprint.ancestor
|
74
|
+
assert_equal cloned.imprint, orig.imprint.descendant
|
75
|
+
assert orig.imprint.descendant.guides.include? orig.descendant
|
76
|
+
end
|
77
|
+
|
78
|
+
test "should maintain belongs_to relationship in resources" do
|
79
|
+
orig = guides(:scotland)
|
80
|
+
cloned = orig.clone_edition!
|
81
|
+
orig.reload
|
82
|
+
cloned.reload
|
83
|
+
assert_equal orig.abbreviation.alphabet.id,
|
84
|
+
orig.descendant.abbreviation.alphabet.id
|
85
|
+
end
|
86
|
+
|
87
|
+
test "should clone has_manys specified in edition_chain" do
|
88
|
+
orig = guides(:scotland)
|
89
|
+
cloned = orig.clone_edition!
|
90
|
+
orig.reload
|
91
|
+
cloned.reload
|
92
|
+
orig.places.select { |p| p.cloneme? }.each do |place|
|
93
|
+
assert cloned.places.include? place.descendant
|
94
|
+
assert_equal place.descendant.guide, cloned
|
95
|
+
end
|
96
|
+
cloned.places.each do |place|
|
97
|
+
assert orig.places.include? place.ancestor
|
98
|
+
assert_equal place.ancestor.guide, orig
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
test "should not clone edition_chain has_manys if conditions not met" do
|
103
|
+
orig = guides(:scotland)
|
104
|
+
cloned = orig.clone_edition!
|
105
|
+
orig.reload
|
106
|
+
cloned.reload
|
107
|
+
assert_equal orig.places.count, cloned.places.count + 1
|
108
|
+
orig.places.select { |p| !p.cloneme? }.each do |place|
|
109
|
+
assert !cloned.places.include?(place)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
test "should clone nested has_manys" do
|
114
|
+
orig = guides(:scotland)
|
115
|
+
cloned = orig.clone_edition!
|
116
|
+
orig.reload
|
117
|
+
cloned.reload
|
118
|
+
orig_maps_count = cloned_maps_count = 0
|
119
|
+
orig.places.each { |place| orig_maps_count += place.maps.count }
|
120
|
+
cloned.places.each { |place| cloned_maps_count += place.maps.count }
|
121
|
+
assert_equal orig_maps_count, cloned_maps_count
|
122
|
+
cloned.places.each do |place|
|
123
|
+
place.maps.each do |map|
|
124
|
+
assert place.ancestor.maps.include? map.ancestor
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
test "should clone has_and_belongs_to_manys specified in edition_chain" do
|
130
|
+
orig = guides(:scotland)
|
131
|
+
cloned = orig.clone_edition!
|
132
|
+
orig.reload
|
133
|
+
cloned.reload
|
134
|
+
orig_laws = cloned_laws = []
|
135
|
+
orig.places.each do |place|
|
136
|
+
orig_laws += place.laws.select { |l| l.cloneme? }
|
137
|
+
end
|
138
|
+
cloned.places.
|
139
|
+
each { |place| cloned_laws += place.laws.select { |l| l.cloneme? } }
|
140
|
+
assert_equal orig_laws.count, cloned_laws.count
|
141
|
+
orig_laws.each do |orig_law|
|
142
|
+
assert_equal 1, Law.find_all_by_ancestor_id(orig_law.id).count
|
143
|
+
assert !cloned_laws.include?(orig_law)
|
144
|
+
assert cloned_laws.include?(orig_law.descendant)
|
145
|
+
orig_law.places.each do |orig_place|
|
146
|
+
assert !orig_law.descendant.places.include?(orig_place)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
cloned_laws.each do |cloned_law|
|
150
|
+
assert orig_laws.include?(cloned_law.ancestor)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
test "should update relationship to has_ones spec'd in resources" do
|
155
|
+
# XXX this is a contrived case; would probably want to clone . . .
|
156
|
+
orig = guides(:scotland)
|
157
|
+
country = orig.country
|
158
|
+
cloned = orig.clone_edition!
|
159
|
+
orig.reload
|
160
|
+
cloned.reload
|
161
|
+
assert_equal orig.country, nil
|
162
|
+
assert_equal cloned.country, country
|
163
|
+
end
|
164
|
+
|
165
|
+
test "should updated relationship to has_manys spec'd in resources" do
|
166
|
+
orig = guides(:scotland)
|
167
|
+
retailer_ids = orig.retailers.collect { |r| r.id }
|
168
|
+
cloned = orig.clone_edition!
|
169
|
+
orig.reload
|
170
|
+
assert_equal 0, orig.retailers.count
|
171
|
+
assert_equal retailer_ids.count, cloned.retailers.count
|
172
|
+
cloned.retailers.each do |retailer|
|
173
|
+
assert retailer_ids.include?(retailer.id)
|
174
|
+
end
|
175
|
+
cloned_ids = cloned.retailers.collect { |r| r.id }
|
176
|
+
retailer_ids.each do |id|
|
177
|
+
assert cloned_ids.include?(id)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
test "should update relationship to has_and_belongs_to_many resources" do
|
182
|
+
# habtm realtionships are handled differently from other resources.
|
183
|
+
# For the resource, the change is additive. The resource will have
|
184
|
+
# a relationship with original *and* cloned edition object.
|
185
|
+
orig = guides(:scotland)
|
186
|
+
author_ids = orig.authors.collect { |a| a.id }
|
187
|
+
cloned = orig.clone_edition!
|
188
|
+
orig.reload
|
189
|
+
assert_equal cloned.authors.count, orig.authors.count
|
190
|
+
assert_equal cloned.authors.first.guides.count, 2
|
191
|
+
orig.authors.each { |author| assert cloned.authors.include?(author) }
|
192
|
+
cloned.authors.each { |author| assert orig.authors.include?(author) }
|
193
|
+
end
|
194
|
+
|
195
|
+
test "should execute pre_hook" do
|
196
|
+
orig = guides(:scotland)
|
197
|
+
assert orig.published?
|
198
|
+
cloned = orig.clone_edition!
|
199
|
+
orig.reload
|
200
|
+
assert !orig.published?
|
201
|
+
end
|
202
|
+
|
203
|
+
test "should execute after_clone" do
|
204
|
+
orig = guides(:scotland)
|
205
|
+
orig_year = orig.year.to_i
|
206
|
+
cloned = orig.clone_edition!
|
207
|
+
orig.reload
|
208
|
+
assert_equal orig_year + 1, cloned.year.to_i
|
209
|
+
assert_equal orig.year.to_i, orig_year
|
210
|
+
end
|
211
|
+
|
212
|
+
test "should execute post_hook" do
|
213
|
+
orig = guides(:scotland)
|
214
|
+
cloned = orig.clone_edition!
|
215
|
+
orig.reload
|
216
|
+
assert !orig.published?
|
217
|
+
assert cloned.published?
|
218
|
+
end
|
219
|
+
end
|
data/test/database.yml
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
class Guide < ActiveRecord::Base
|
2
|
+
acts_as_edition :edition_chain => [:abbreviation, :imprint, :places],
|
3
|
+
:resources => [:country, :retailers, :authors],
|
4
|
+
:pre_hook => :unpublish_self,
|
5
|
+
:after_clone => :increment_descendant_year,
|
6
|
+
:post_hook => :publish_descendant,
|
7
|
+
:conditions => { :returns_true => true }
|
8
|
+
has_one :abbreviation
|
9
|
+
belongs_to :imprint
|
10
|
+
has_many :places
|
11
|
+
has_one :country
|
12
|
+
has_many :retailers
|
13
|
+
has_and_belongs_to_many :authors
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def unpublish_self
|
18
|
+
self.published = false
|
19
|
+
self.save!
|
20
|
+
end
|
21
|
+
|
22
|
+
def increment_descendant_year
|
23
|
+
self.year = (self.year.to_i + 1).to_s
|
24
|
+
self.save!
|
25
|
+
end
|
26
|
+
|
27
|
+
def publish_descendant
|
28
|
+
self.descendant.published = true
|
29
|
+
self.descendant.save!
|
30
|
+
end
|
31
|
+
|
32
|
+
def returns_true
|
33
|
+
name != 'Noclonelandia'
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
scotland:
|
2
|
+
name: Scotland
|
3
|
+
imprint: fodors
|
4
|
+
published: true
|
5
|
+
year: 2009
|
6
|
+
authors: mary, darren, laura
|
7
|
+
|
8
|
+
noclonelandia:
|
9
|
+
name: Noclonelandia
|
10
|
+
imprint: fodors
|
11
|
+
published: true
|
12
|
+
year: 2009
|
13
|
+
authors: lee
|
14
|
+
|
15
|
+
england:
|
16
|
+
name: England
|
17
|
+
imprint: fodors
|
18
|
+
published: true
|
19
|
+
year: 2009
|
20
|
+
authors: peggy
|
data/test/schema.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 1) do
|
2
|
+
create_table :guides, :force => true do |t|
|
3
|
+
t.column :name, :string
|
4
|
+
t.column :ancestor_id, :integer, :default => nil
|
5
|
+
t.column :imprint_id, :integer, :default => nil
|
6
|
+
t.column :published, :boolean, :default => false
|
7
|
+
t.column :year, :string
|
8
|
+
end
|
9
|
+
|
10
|
+
create_table :places, :force => true do |t|
|
11
|
+
t.column :name, :string
|
12
|
+
t.column :guide_id, :integer
|
13
|
+
t.column :ancestor_id, :integer, :default => nil
|
14
|
+
t.column :cloneme, :boolean, :default => true
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :laws, :force => true do |t|
|
18
|
+
t.column :name, :string
|
19
|
+
t.column :ancestor_id, :integer, :default => nil
|
20
|
+
t.column :cloneme, :boolean, :default => true
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table "laws_places", :id => false, :force => true do |t|
|
24
|
+
t.integer "place_id"
|
25
|
+
t.integer "law_id"
|
26
|
+
end
|
27
|
+
|
28
|
+
create_table :maps, :force => true do |t|
|
29
|
+
t.column :name, :string
|
30
|
+
t.column :place_id, :integer
|
31
|
+
t.column :ancestor_id, :integer, :default => nil
|
32
|
+
end
|
33
|
+
|
34
|
+
create_table :alphabets, :force => true do |t|
|
35
|
+
t.column :name, :string
|
36
|
+
end
|
37
|
+
|
38
|
+
create_table :abbreviations, :force => true do |t|
|
39
|
+
t.column :name, :string
|
40
|
+
t.column :alphabet_id, :integer
|
41
|
+
t.column :guide_id, :integer
|
42
|
+
t.column :ancestor_id, :integer, :default => nil
|
43
|
+
t.column :cloneme, :boolean, :default => true
|
44
|
+
end
|
45
|
+
|
46
|
+
create_table :imprints, :force=> true do |t|
|
47
|
+
t.column :name, :string
|
48
|
+
t.column :ancestor_id, :integer, :default => nil
|
49
|
+
end
|
50
|
+
|
51
|
+
create_table :countries, :force=> true do |t|
|
52
|
+
t.column :name, :string
|
53
|
+
t.column :guide_id, :integer, :default => nil
|
54
|
+
end
|
55
|
+
|
56
|
+
create_table :retailers, :force=> true do |t|
|
57
|
+
t.column :name, :string
|
58
|
+
t.column :guide_id, :integer, :default => nil
|
59
|
+
end
|
60
|
+
|
61
|
+
create_table :authors, :force=> true do |t|
|
62
|
+
t.column :name, :string
|
63
|
+
end
|
64
|
+
|
65
|
+
create_table "authors_guides", :id => false, :force => true do |t|
|
66
|
+
t.integer "author_id"
|
67
|
+
t.integer "guide_id"
|
68
|
+
end
|
69
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
RAILS_ROOT = File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'active_record'
|
5
|
+
require 'active_record/fixtures'
|
6
|
+
require 'active_support'
|
7
|
+
require 'active_support/test_case'
|
8
|
+
require 'rubygems'
|
9
|
+
require 'test/unit'
|
10
|
+
|
11
|
+
require "#{File.dirname(__FILE__)}/../init"
|
12
|
+
|
13
|
+
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
14
|
+
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
15
|
+
ActiveRecord::Base.configurations = { "test" => config[ENV['DB'] || 'sqlite3'] }
|
16
|
+
ActiveRecord::Base.establish_connection(
|
17
|
+
ActiveRecord::Base.configurations['test']
|
18
|
+
)
|
19
|
+
|
20
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
21
|
+
|
22
|
+
class ActiveSupport::TestCase #:nodoc:
|
23
|
+
include ActiveRecord::TestFixtures
|
24
|
+
self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
|
25
|
+
$LOAD_PATH.unshift(self.fixture_path)
|
26
|
+
def create_fixtures(*table_names)
|
27
|
+
if block_given?
|
28
|
+
Fixtures.create_fixtures(self.fixture_path,
|
29
|
+
table_names
|
30
|
+
) { yield }
|
31
|
+
else
|
32
|
+
Fixtures.create_fixtures(self.fixture_path, table_names)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
self.use_transactional_fixtures = true
|
37
|
+
self.use_instantiated_fixtures = false
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: acts_as_edition
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lee Capps
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-06-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.2.13
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.2.13
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sqlite3
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Editions for ActiveRecord models
|
70
|
+
email:
|
71
|
+
- himself@leecapps.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- Gemfile
|
78
|
+
- MIT-LICENSE
|
79
|
+
- README
|
80
|
+
- Rakefile
|
81
|
+
- acts_as_edition.gemspec
|
82
|
+
- init.rb
|
83
|
+
- lib/acts_as_edition.rb
|
84
|
+
- lib/acts_as_edition/version.rb
|
85
|
+
- test/acts_as_edition_test.rb
|
86
|
+
- test/database.yml
|
87
|
+
- test/fixtures/abbreviation.rb
|
88
|
+
- test/fixtures/abbreviations.yml
|
89
|
+
- test/fixtures/alphabet.rb
|
90
|
+
- test/fixtures/alphabets.yml
|
91
|
+
- test/fixtures/author.rb
|
92
|
+
- test/fixtures/authors.yml
|
93
|
+
- test/fixtures/countries.yml
|
94
|
+
- test/fixtures/country.rb
|
95
|
+
- test/fixtures/guide.rb
|
96
|
+
- test/fixtures/guides.yml
|
97
|
+
- test/fixtures/imprint.rb
|
98
|
+
- test/fixtures/imprints.yml
|
99
|
+
- test/fixtures/law.rb
|
100
|
+
- test/fixtures/laws.yml
|
101
|
+
- test/fixtures/map.rb
|
102
|
+
- test/fixtures/maps.yml
|
103
|
+
- test/fixtures/place.rb
|
104
|
+
- test/fixtures/places.yml
|
105
|
+
- test/fixtures/retailer.rb
|
106
|
+
- test/fixtures/retailers.yml
|
107
|
+
- test/schema.rb
|
108
|
+
- test/test_helper.rb
|
109
|
+
homepage: https://github.com/jlcapps/acts_as_edition
|
110
|
+
licenses:
|
111
|
+
- MIT
|
112
|
+
metadata: {}
|
113
|
+
post_install_message:
|
114
|
+
rdoc_options: []
|
115
|
+
require_paths:
|
116
|
+
- lib
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
requirements: []
|
128
|
+
rubyforge_project:
|
129
|
+
rubygems_version: 2.0.0
|
130
|
+
signing_key:
|
131
|
+
specification_version: 4
|
132
|
+
summary: acts_as_edition is a ruby gem for creating new editions of trees of ActiveRecord
|
133
|
+
objects.
|
134
|
+
test_files:
|
135
|
+
- test/acts_as_edition_test.rb
|
136
|
+
- test/database.yml
|
137
|
+
- test/fixtures/abbreviation.rb
|
138
|
+
- test/fixtures/abbreviations.yml
|
139
|
+
- test/fixtures/alphabet.rb
|
140
|
+
- test/fixtures/alphabets.yml
|
141
|
+
- test/fixtures/author.rb
|
142
|
+
- test/fixtures/authors.yml
|
143
|
+
- test/fixtures/countries.yml
|
144
|
+
- test/fixtures/country.rb
|
145
|
+
- test/fixtures/guide.rb
|
146
|
+
- test/fixtures/guides.yml
|
147
|
+
- test/fixtures/imprint.rb
|
148
|
+
- test/fixtures/imprints.yml
|
149
|
+
- test/fixtures/law.rb
|
150
|
+
- test/fixtures/laws.yml
|
151
|
+
- test/fixtures/map.rb
|
152
|
+
- test/fixtures/maps.yml
|
153
|
+
- test/fixtures/place.rb
|
154
|
+
- test/fixtures/places.yml
|
155
|
+
- test/fixtures/retailer.rb
|
156
|
+
- test/fixtures/retailers.yml
|
157
|
+
- test/schema.rb
|
158
|
+
- test/test_helper.rb
|