copyable 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +264 -0
- data/Rakefile +7 -0
- data/copyable.gemspec +28 -0
- data/lib/copyable.rb +32 -0
- data/lib/copyable/config.rb +19 -0
- data/lib/copyable/copy_registry.rb +50 -0
- data/lib/copyable/copyable_extension.rb +118 -0
- data/lib/copyable/declarations/after_copy.rb +15 -0
- data/lib/copyable/declarations/associations.rb +116 -0
- data/lib/copyable/declarations/columns.rb +34 -0
- data/lib/copyable/declarations/declaration.rb +15 -0
- data/lib/copyable/declarations/declarations.rb +14 -0
- data/lib/copyable/declarations/disable_all_callbacks_and_observers_except_validate.rb +9 -0
- data/lib/copyable/declarations/main.rb +32 -0
- data/lib/copyable/exceptions.rb +10 -0
- data/lib/copyable/model_hooks.rb +40 -0
- data/lib/copyable/option_checker.rb +23 -0
- data/lib/copyable/railtie.rb +9 -0
- data/lib/copyable/saver.rb +19 -0
- data/lib/copyable/single_copy_enforcer.rb +68 -0
- data/lib/copyable/syntax_checking/association_checker.rb +41 -0
- data/lib/copyable/syntax_checking/column_checker.rb +43 -0
- data/lib/copyable/syntax_checking/completeness_checker.rb +27 -0
- data/lib/copyable/syntax_checking/declaration_checker.rb +26 -0
- data/lib/copyable/syntax_checking/declaration_stubber.rb +18 -0
- data/lib/copyable/syntax_checking/syntax_checker.rb +15 -0
- data/lib/copyable/version.rb +3 -0
- data/lib/tasks/copyable.rake +55 -0
- data/spec/config_spec.rb +132 -0
- data/spec/copy_registry_spec.rb +55 -0
- data/spec/copyable_after_copy_spec.rb +28 -0
- data/spec/copyable_associations_spec.rb +366 -0
- data/spec/copyable_columns_spec.rb +116 -0
- data/spec/copyable_spec.rb +7 -0
- data/spec/create_copy_spec.rb +136 -0
- data/spec/deep_structure_copy_spec.rb +169 -0
- data/spec/helper/copyable_spec_helper.rb +15 -0
- data/spec/helper/test_models.rb +136 -0
- data/spec/helper/test_tables.rb +135 -0
- data/spec/model_hooks_spec.rb +66 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/stress_test_spec.rb +261 -0
- data/spec/syntax_checking/association_checker_spec.rb +80 -0
- data/spec/syntax_checking/column_checker_spec.rb +49 -0
- data/spec/syntax_checking/declaration_checker_spec.rb +58 -0
- data/spec/syntax_checking_spec.rb +258 -0
- data/spec/transaction_spec.rb +78 -0
- metadata +200 -0
@@ -0,0 +1,116 @@
|
|
1
|
+
require_relative 'helper/copyable_spec_helper'
|
2
|
+
|
3
|
+
describe 'copyable:columns' do
|
4
|
+
context 'when asked to do a simple column copy' do
|
5
|
+
include ActiveSupport::Testing::TimeHelpers
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
undefine_copyable_in CopyableCoin
|
9
|
+
class CopyableCoin < ActiveRecord::Base
|
10
|
+
copyable do
|
11
|
+
disable_all_callbacks_and_observers_except_validate
|
12
|
+
columns({
|
13
|
+
kind: :copy,
|
14
|
+
year: :copy,
|
15
|
+
})
|
16
|
+
associations({
|
17
|
+
})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should create a new record with matching columns' do
|
23
|
+
expect(CopyableCoin.count).to eq(0)
|
24
|
+
coin = CopyableCoin.create!(kind: 'cent', year: 1943)
|
25
|
+
coin.create_copy!
|
26
|
+
expect(CopyableCoin.where(kind: 'cent').count).to eq(2)
|
27
|
+
expect(CopyableCoin.where(year: 1943).count).to eq(2)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should update timestamp columns' do
|
31
|
+
expect(CopyableCoin.count).to eq(0)
|
32
|
+
coin = CopyableCoin.create!(kind: 'cent', year: 1943)
|
33
|
+
travel_to 2.seconds.from_now do
|
34
|
+
coin_copy = coin.create_copy!
|
35
|
+
expect(coin_copy.updated_at).to be_present
|
36
|
+
expect(coin_copy.created_at).to be_present
|
37
|
+
expect(coin_copy.updated_at).not_to eq(coin.updated_at)
|
38
|
+
expect(coin_copy.created_at).not_to eq(coin.created_at)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when asked to do a column copy with customization' do
|
44
|
+
before(:each) do
|
45
|
+
undefine_copyable_in CopyableCoin
|
46
|
+
class CopyableCoin < ActiveRecord::Base
|
47
|
+
copyable do
|
48
|
+
disable_all_callbacks_and_observers_except_validate
|
49
|
+
columns({
|
50
|
+
kind: lambda { |orig| "Copy of #{orig.kind}" },
|
51
|
+
year: :copy,
|
52
|
+
})
|
53
|
+
associations({
|
54
|
+
})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
describe '#create_copy!' do
|
59
|
+
it 'should create a new record with the customized column' do
|
60
|
+
expect(CopyableCoin.count).to eq(0)
|
61
|
+
coin = CopyableCoin.create!(kind: 'quarter', year: 1964)
|
62
|
+
coin.create_copy!
|
63
|
+
expect(CopyableCoin.where(kind: 'quarter').count).to eq(1)
|
64
|
+
expect(CopyableCoin.where(kind: 'Copy of quarter').count).to eq(1)
|
65
|
+
expect(CopyableCoin.where(year: 1964).count).to eq(2)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when asked to do a copy that produces invalid data' do
|
71
|
+
before(:each) do
|
72
|
+
undefine_copyable_in CopyableCoin
|
73
|
+
class CopyableCoin < ActiveRecord::Base
|
74
|
+
copyable do
|
75
|
+
disable_all_callbacks_and_observers_except_validate
|
76
|
+
columns({
|
77
|
+
kind: :copy,
|
78
|
+
year: lambda { |orig| -444 },
|
79
|
+
})
|
80
|
+
associations({
|
81
|
+
})
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
it 'should raise an error if data is not valid' do
|
86
|
+
expect(CopyableCoin.count).to eq(0)
|
87
|
+
coin = CopyableCoin.create!(kind: 'cent', year: 1982)
|
88
|
+
expect {
|
89
|
+
coin.create_copy!
|
90
|
+
}.to raise_error(ActiveRecord::RecordInvalid)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'with :do_not_copy advice' do
|
95
|
+
before(:each) do
|
96
|
+
undefine_copyable_in CopyableCoin
|
97
|
+
class CopyableCoin < ActiveRecord::Base
|
98
|
+
copyable do
|
99
|
+
disable_all_callbacks_and_observers_except_validate
|
100
|
+
columns({
|
101
|
+
kind: :do_not_copy,
|
102
|
+
year: :copy,
|
103
|
+
})
|
104
|
+
associations({
|
105
|
+
})
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
it 'should result in a nil value for that column' do
|
110
|
+
expect(CopyableCoin.count).to eq(0)
|
111
|
+
coin = CopyableCoin.create!(kind: 'cent', year: 1982)
|
112
|
+
copied_coin = coin.create_copy!
|
113
|
+
expect(copied_coin.kind).to be_nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require_relative 'helper/copyable_spec_helper'
|
2
|
+
|
3
|
+
describe 'create_copy!' do
|
4
|
+
before(:each) do
|
5
|
+
undefine_copyable_in CopyableCoin
|
6
|
+
class CopyableCoin < ActiveRecord::Base
|
7
|
+
copyable do
|
8
|
+
disable_all_callbacks_and_observers_except_validate
|
9
|
+
columns({
|
10
|
+
kind: :copy,
|
11
|
+
year: :copy,
|
12
|
+
})
|
13
|
+
associations({
|
14
|
+
})
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should exist' do
|
20
|
+
coin = CopyableCoin.create!(kind: 'cent', year: 1909)
|
21
|
+
expect(coin).to respond_to(:create_copy!)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should create a new record' do
|
25
|
+
expect(CopyableCoin.count).to eq(0)
|
26
|
+
coin = CopyableCoin.create!(kind: 'cent', year: 1909)
|
27
|
+
coin.create_copy!
|
28
|
+
expect(CopyableCoin.count).to eq(2)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should return the new record' do
|
32
|
+
expect(CopyableCoin.count).to eq(0)
|
33
|
+
coin = CopyableCoin.create!(kind: 'cent', year: 1909)
|
34
|
+
coin2 = coin.create_copy!
|
35
|
+
expect(coin2).to be_a CopyableCoin
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should return the new record having an id' do
|
39
|
+
expect(CopyableCoin.count).to eq(0)
|
40
|
+
coin = CopyableCoin.create!(kind: 'cent', year: 1909)
|
41
|
+
coin2 = coin.create_copy!
|
42
|
+
expect(coin2.id).not_to be_nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should allow columns to be overridden' do
|
46
|
+
expect(CopyableCoin.count).to eq(0)
|
47
|
+
coin = CopyableCoin.create!(kind: 'cent', year: 1909)
|
48
|
+
coin2 = coin.create_copy!(override: { year: 1959 })
|
49
|
+
expect(coin2.kind).to eq('cent')
|
50
|
+
expect(coin2.year).to eq(1959)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should allow columns to be overridden using strings as column names' do
|
54
|
+
expect(CopyableCoin.count).to eq(0)
|
55
|
+
coin = CopyableCoin.create!('kind' => 'cent', 'year' => 1909)
|
56
|
+
coin2 = coin.create_copy!(override: { 'year' => 1959 })
|
57
|
+
expect(coin2.kind).to eq('cent')
|
58
|
+
expect(coin2.year).to eq(1959)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should allow validations to be skipped' do
|
62
|
+
coin = CopyableCoin.create!('kind' => 'cent', 'year' => 1909)
|
63
|
+
expect {
|
64
|
+
coin.create_copy!(override: { 'year' => 2 })
|
65
|
+
}.to raise_error(ActiveRecord::RecordInvalid)
|
66
|
+
expect {
|
67
|
+
coin.create_copy!(override: { 'year' => 2 }, skip_validations: true)
|
68
|
+
}.to_not raise_error
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should skip validations even in associated objects when skip_validations is true' do
|
72
|
+
# set up classes
|
73
|
+
undefine_copyable_in CopyablePet
|
74
|
+
class CopyablePet < ActiveRecord::Base
|
75
|
+
copyable do
|
76
|
+
disable_all_callbacks_and_observers_except_validate
|
77
|
+
columns({
|
78
|
+
name: :copy,
|
79
|
+
kind: :copy,
|
80
|
+
birth_year: :copy,
|
81
|
+
})
|
82
|
+
associations({
|
83
|
+
copyable_toys: :copy,
|
84
|
+
copyable_pet_tag: :do_not_copy,
|
85
|
+
copyable_pet_profile: :do_not_copy,
|
86
|
+
copyable_pet_foods: :do_not_copy,
|
87
|
+
copyable_pet_sitting_patronages: :do_not_copy,
|
88
|
+
})
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
undefine_copyable_in CopyableToy
|
93
|
+
class CopyableToy < ActiveRecord::Base
|
94
|
+
copyable do
|
95
|
+
disable_all_callbacks_and_observers_except_validate
|
96
|
+
columns({
|
97
|
+
name: :copy,
|
98
|
+
kind: :copy,
|
99
|
+
copyable_pet_id: :copy,
|
100
|
+
})
|
101
|
+
associations({
|
102
|
+
})
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# set up objects
|
107
|
+
fido = CopyablePet.create!(
|
108
|
+
name: 'Fido',
|
109
|
+
kind: 'canine',
|
110
|
+
birth_year: 2005)
|
111
|
+
|
112
|
+
pillow = CopyableToy.create!(
|
113
|
+
name: 'favorite pillow',
|
114
|
+
kind: 'soft',
|
115
|
+
copyable_pet: fido)
|
116
|
+
|
117
|
+
# everything should be wonderful
|
118
|
+
expect {
|
119
|
+
fido.create_copy!
|
120
|
+
}.to_not raise_error
|
121
|
+
|
122
|
+
# now inject some invalid data in an associated object
|
123
|
+
pillow.update_column(:name, 'o')
|
124
|
+
fido.reload
|
125
|
+
|
126
|
+
# fail because invalid!
|
127
|
+
expect {
|
128
|
+
fido.create_copy!
|
129
|
+
}.to raise_error(ActiveRecord::RecordInvalid)
|
130
|
+
|
131
|
+
# oh quit complaning and do it anyway!
|
132
|
+
expect {
|
133
|
+
fido.create_copy!(skip_validations: true)
|
134
|
+
}.to_not raise_error
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require_relative 'helper/copyable_spec_helper'
|
2
|
+
|
3
|
+
describe 'complex model hierarchies:' do
|
4
|
+
before(:each) do
|
5
|
+
undefine_copyable_in CopyableOwner
|
6
|
+
class CopyableOwner < ActiveRecord::Base
|
7
|
+
copyable do
|
8
|
+
disable_all_callbacks_and_observers_except_validate
|
9
|
+
columns({
|
10
|
+
name: :copy,
|
11
|
+
})
|
12
|
+
associations({
|
13
|
+
copyable_vehicles: :copy,
|
14
|
+
copyable_amenities: :copy,
|
15
|
+
})
|
16
|
+
end
|
17
|
+
end
|
18
|
+
undefine_copyable_in CopyableVehicle
|
19
|
+
class CopyableVehicle < ActiveRecord::Base
|
20
|
+
copyable do
|
21
|
+
disable_all_callbacks_and_observers_except_validate
|
22
|
+
columns({
|
23
|
+
name: :copy,
|
24
|
+
copyable_owner_id: :copy,
|
25
|
+
})
|
26
|
+
associations({
|
27
|
+
copyable_amenities: :copy,
|
28
|
+
})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
undefine_copyable_in CopyableAmenity
|
32
|
+
class CopyableAmenity < ActiveRecord::Base
|
33
|
+
copyable do
|
34
|
+
disable_all_callbacks_and_observers_except_validate
|
35
|
+
columns({
|
36
|
+
name: :copy,
|
37
|
+
copyable_vehicle_id: :copy,
|
38
|
+
copyable_owner_id: :copy,
|
39
|
+
})
|
40
|
+
associations({
|
41
|
+
copyable_warranty: :copy,
|
42
|
+
})
|
43
|
+
end
|
44
|
+
end
|
45
|
+
undefine_copyable_in CopyableWarranty
|
46
|
+
class CopyableWarranty < ActiveRecord::Base
|
47
|
+
copyable do
|
48
|
+
disable_all_callbacks_and_observers_except_validate
|
49
|
+
columns({
|
50
|
+
name: :copy,
|
51
|
+
copyable_amenity_id: :copy,
|
52
|
+
})
|
53
|
+
associations({
|
54
|
+
})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'a tree structure' do
|
60
|
+
before(:each) do
|
61
|
+
# We are avoiding using owners to avoid the denormalized aspect
|
62
|
+
# of the data model and keep this a simple tree.
|
63
|
+
@vehicle1 = CopyableVehicle.create!(name: 'Corvette')
|
64
|
+
|
65
|
+
@amenity1 = CopyableAmenity.create!(name: 'moon roof', copyable_vehicle: @vehicle1)
|
66
|
+
@warranty1 = CopyableWarranty.create!(name: 'moon roof warranty', copyable_amenity: @amenity1)
|
67
|
+
@amenity2 = CopyableAmenity.create!(name: 'twitter', copyable_vehicle: @vehicle1)
|
68
|
+
@warranty2 = CopyableWarranty.create!(name: 'twitter warranty', copyable_amenity: @amenity2)
|
69
|
+
@amenity3 = CopyableAmenity.create!(name: 'jazz', copyable_vehicle: @vehicle1)
|
70
|
+
@warranty3 = CopyableWarranty.create!(name: 'jazz warranty', copyable_amenity: @amenity3)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should copy the entire tree as directed' do
|
74
|
+
@vehicle2 = @vehicle1.create_copy!
|
75
|
+
expect(CopyableVehicle.count).to eq(2)
|
76
|
+
expect(CopyableAmenity.count).to eq(6)
|
77
|
+
expect(CopyableWarranty.count).to eq(6)
|
78
|
+
expect(@vehicle2.name).to eq('Corvette')
|
79
|
+
expect(@vehicle2.copyable_amenities.map(&:name)).to match_array(
|
80
|
+
['moon roof', 'twitter', 'jazz'])
|
81
|
+
expect(@vehicle2.copyable_amenities.map(&:copyable_warranty).map(&:name)).to match_array(
|
82
|
+
['moon roof warranty', 'twitter warranty', 'jazz warranty'])
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should create the expected records if copied multiple times' do
|
86
|
+
# this test makes sure the SingleCopyEnforcer isn't too eager
|
87
|
+
@vehicle1.create_copy!
|
88
|
+
expect(CopyableVehicle.count).to eq(2)
|
89
|
+
expect(CopyableAmenity.count).to eq(6)
|
90
|
+
expect(CopyableWarranty.count).to eq(6)
|
91
|
+
@vehicle1.create_copy!
|
92
|
+
expect(CopyableVehicle.count).to eq(3)
|
93
|
+
expect(CopyableAmenity.count).to eq(9)
|
94
|
+
expect(CopyableWarranty.count).to eq(9)
|
95
|
+
@vehicle1.create_copy!
|
96
|
+
expect(CopyableVehicle.count).to eq(4)
|
97
|
+
expect(CopyableAmenity.count).to eq(12)
|
98
|
+
expect(CopyableWarranty.count).to eq(12)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'a denormalized structure' do
|
103
|
+
context 'having one redundant association' do
|
104
|
+
before(:each) do
|
105
|
+
@joe = CopyableOwner.create!(name: 'Joe')
|
106
|
+
@porsche = CopyableVehicle.create!(name: 'Porsche', copyable_owner: @joe)
|
107
|
+
@moon_roof = CopyableAmenity.create!(name: 'moon roof',
|
108
|
+
copyable_vehicle: @porsche,
|
109
|
+
copyable_owner: @joe)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should copy the records without copying any given record more than once' do
|
113
|
+
@copy_of_joe = @joe.create_copy!
|
114
|
+
expect(CopyableOwner.count).to eq(2)
|
115
|
+
expect(CopyableVehicle.count).to eq(2)
|
116
|
+
expect(CopyableAmenity.count).to eq(2)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'with many models, some having redundant associations' do
|
121
|
+
before(:each) do
|
122
|
+
@joe = CopyableOwner.create!(name: 'Joe')
|
123
|
+
|
124
|
+
@porsche = CopyableVehicle.create!(name: 'Porsche', copyable_owner: @joe)
|
125
|
+
@moon_roof = CopyableAmenity.create!(
|
126
|
+
name: 'moon roof',
|
127
|
+
copyable_vehicle: @porsche,
|
128
|
+
copyable_owner: @joe)
|
129
|
+
|
130
|
+
@mustang = CopyableVehicle.create!(name: 'Mustang', copyable_owner: @joe)
|
131
|
+
@air_conditioning = CopyableAmenity.create!(
|
132
|
+
name: 'air conditioning',
|
133
|
+
copyable_vehicle: @mustang,
|
134
|
+
copyable_owner: @joe)
|
135
|
+
@air_conditioning_warranty = CopyableWarranty.create!(
|
136
|
+
name: 'air conditioning warranty',
|
137
|
+
copyable_amenity: @air_conditioning)
|
138
|
+
|
139
|
+
@corvette = CopyableVehicle.create!(name: 'Corvette', copyable_owner: @joe)
|
140
|
+
@pillows = CopyableAmenity.create!(
|
141
|
+
name: 'pillows',
|
142
|
+
copyable_vehicle: @corvette,
|
143
|
+
copyable_owner: @joe)
|
144
|
+
@pillow_warranty = CopyableWarranty.create!(name: 'pillow warranty', copyable_amenity: @pillows)
|
145
|
+
@airbags = CopyableAmenity.create!(
|
146
|
+
name: 'airbags',
|
147
|
+
copyable_vehicle: @corvette,
|
148
|
+
copyable_owner: @joe)
|
149
|
+
@airbag_warranty = CopyableWarranty.create!(name: 'airbag warranty', copyable_amenity: @airbags)
|
150
|
+
@radio = CopyableAmenity.create!(
|
151
|
+
name: 'satellite radio',
|
152
|
+
copyable_vehicle: @corvette,
|
153
|
+
copyable_owner: @joe)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'should copy the records without copying any given record more than once' do
|
157
|
+
@copy_of_joe = @joe.create_copy!
|
158
|
+
expect(CopyableOwner.count).to eq(2)
|
159
|
+
expect(CopyableVehicle.count).to eq(6)
|
160
|
+
expect(CopyableAmenity.count).to eq(10)
|
161
|
+
expect(CopyableWarranty.count).to eq(6)
|
162
|
+
expect(@copy_of_joe.copyable_vehicles.map(&:name)).to match_array([
|
163
|
+
'Porsche', 'Mustang', 'Corvette'])
|
164
|
+
expect(@copy_of_joe.copyable_amenities.map(&:name)).to match_array([
|
165
|
+
'moon roof', 'air conditioning', 'pillows', 'airbags', 'satellite radio'])
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# copyable needs its own set of tables and ActiveRecord models to test with.
|
4
|
+
require_relative 'test_tables'
|
5
|
+
Copyable::TestTables.create!
|
6
|
+
require_relative 'test_models'
|
7
|
+
|
8
|
+
# useful for starting off specs with a clean slate
|
9
|
+
def undefine_copyable_in(klass)
|
10
|
+
klass.instance_eval do
|
11
|
+
define_method(:create_copy!) do |*args|
|
12
|
+
raise "the create_copy! method has been wiped clean for testing purposes"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# We need various test models in various configurations to properly test the
|
2
|
+
# copying functionality.
|
3
|
+
|
4
|
+
|
5
|
+
#*******************************************************************************
|
6
|
+
# For testing basic copying behavior and validation.
|
7
|
+
#
|
8
|
+
|
9
|
+
class CopyableCoin < ActiveRecord::Base
|
10
|
+
validates :year, numericality: { greater_than: 1700 }
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
#*******************************************************************************
|
16
|
+
# For testing observers.
|
17
|
+
#
|
18
|
+
|
19
|
+
class CopyableCar < ActiveRecord::Base
|
20
|
+
validates :make, presence: true
|
21
|
+
validates :model, presence: true
|
22
|
+
validates :year, presence: true
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
#*******************************************************************************
|
28
|
+
# For testing callbacks.
|
29
|
+
#
|
30
|
+
|
31
|
+
class CopyableTree < ActiveRecord::Base
|
32
|
+
after_create :callback1
|
33
|
+
before_save { |tree| raise "callback2 called" }
|
34
|
+
def callback1
|
35
|
+
raise "callback1 called"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
#*******************************************************************************
|
42
|
+
# For testing basic associations.
|
43
|
+
#
|
44
|
+
|
45
|
+
class CopyablePet < ActiveRecord::Base
|
46
|
+
has_many :copyable_toys
|
47
|
+
has_one :copyable_pet_tag
|
48
|
+
has_one :copyable_pet_profile
|
49
|
+
has_one :copyable_address, through: :copyable_pet_profile
|
50
|
+
# Rails 3 expects the join table to be called copyable_pet_foods_copyable_pets
|
51
|
+
# Rails 4 expects the join table to be called copyable_pet_foods_pets
|
52
|
+
# We explicitly name the join table so that this code works with both Rails 3 and Rails 4.
|
53
|
+
has_and_belongs_to_many :copyable_pet_foods, join_table: 'copyable_pet_foods_pets'
|
54
|
+
has_many :copyable_pet_sitting_patronages
|
55
|
+
has_many :copyable_pet_sitters, through: :copyable_pet_sitting_patronages
|
56
|
+
end
|
57
|
+
|
58
|
+
class CopyableToy < ActiveRecord::Base
|
59
|
+
validates :name, length: { minimum: 3 }
|
60
|
+
belongs_to :copyable_pet
|
61
|
+
end
|
62
|
+
|
63
|
+
class CopyablePetTag < ActiveRecord::Base
|
64
|
+
belongs_to :copyable_pet
|
65
|
+
end
|
66
|
+
|
67
|
+
class CopyablePetFood < ActiveRecord::Base
|
68
|
+
# Rails 3 expects the join table to be called copyable_pet_foods_copyable_pets
|
69
|
+
# Rails 4 expects the join table to be called copyable_pet_foods_pets
|
70
|
+
# We explicitly name the join table so that this code works with both Rails 3 and Rails 4.
|
71
|
+
has_and_belongs_to_many :copyable_pets, join_table: 'copyable_pet_foods_pets'
|
72
|
+
end
|
73
|
+
|
74
|
+
class CopyablePetSitter < ActiveRecord::Base
|
75
|
+
has_many :copyable_pet_sitting_patronages
|
76
|
+
has_many :copyable_pets, through: :copyable_pet_sitting_patronages
|
77
|
+
end
|
78
|
+
|
79
|
+
class CopyablePetSittingPatronage < ActiveRecord::Base
|
80
|
+
belongs_to :copyable_pet
|
81
|
+
belongs_to :copyable_pet_sitter
|
82
|
+
end
|
83
|
+
|
84
|
+
class CopyablePetProfile < ActiveRecord::Base
|
85
|
+
belongs_to :copyable_pet
|
86
|
+
has_one :copyable_address
|
87
|
+
end
|
88
|
+
|
89
|
+
class CopyableAddress < ActiveRecord::Base
|
90
|
+
belongs_to :copyable_pet_profile
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
#*******************************************************************************
|
96
|
+
# For testing polymorphic associations and custom-named foreign keys.
|
97
|
+
#
|
98
|
+
|
99
|
+
class CopyablePicture < ActiveRecord::Base
|
100
|
+
belongs_to :imageable, polymorphic: true
|
101
|
+
belongs_to :copyable_album, foreign_key: 'picture_album_id'
|
102
|
+
end
|
103
|
+
|
104
|
+
class CopyableProduct < ActiveRecord::Base
|
105
|
+
has_many :copyable_pictures, as: :imageable
|
106
|
+
end
|
107
|
+
|
108
|
+
class CopyableAlbum < ActiveRecord::Base
|
109
|
+
has_many :copyable_pictures, foreign_key: 'picture_album_id'
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
#*******************************************************************************
|
115
|
+
# For testing denormalized data structures.
|
116
|
+
#
|
117
|
+
|
118
|
+
class CopyableOwner < ActiveRecord::Base
|
119
|
+
has_many :copyable_vehicles
|
120
|
+
has_many :copyable_amenities # this is not normalized, you wouldn't normally do this
|
121
|
+
end
|
122
|
+
|
123
|
+
class CopyableVehicle < ActiveRecord::Base
|
124
|
+
has_many :copyable_amenities
|
125
|
+
belongs_to :copyable_owner
|
126
|
+
end
|
127
|
+
|
128
|
+
class CopyableAmenity < ActiveRecord::Base
|
129
|
+
belongs_to :copyable_vehicle
|
130
|
+
belongs_to :copyable_owner # this is not normalized, you wouldn't normally do this
|
131
|
+
has_one :copyable_warranty
|
132
|
+
end
|
133
|
+
|
134
|
+
class CopyableWarranty < ActiveRecord::Base
|
135
|
+
belongs_to :copyable_amenity
|
136
|
+
end
|