copyable 0.0.1
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 +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,135 @@
|
|
1
|
+
module Copyable
|
2
|
+
class TestTables
|
3
|
+
|
4
|
+
def self.create!
|
5
|
+
return if @already_created_tables
|
6
|
+
ActiveRecord::Migration.class_eval do
|
7
|
+
suppress_messages do
|
8
|
+
|
9
|
+
# All tables are prefaced with "copyable_" to avoid name collisions
|
10
|
+
# with other tables in the test database.
|
11
|
+
|
12
|
+
create_table :copyable_albums, force: true do |t|
|
13
|
+
t.string :name, null: false
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :copyable_amenities, force: true do |t|
|
18
|
+
t.string :name
|
19
|
+
t.integer :copyable_vehicle_id
|
20
|
+
t.integer :copyable_owner_id
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
|
24
|
+
create_table :copyable_addresses, force: true do |t|
|
25
|
+
t.string :address1, null: false
|
26
|
+
t.string :address2
|
27
|
+
t.string :city, null: false
|
28
|
+
t.string :state, null: false
|
29
|
+
t.integer :copyable_pet_profile_id, null: false
|
30
|
+
t.timestamps
|
31
|
+
end
|
32
|
+
|
33
|
+
create_table :copyable_cars, force: true do |t|
|
34
|
+
t.string :make, null: false
|
35
|
+
t.string :model, null: false
|
36
|
+
t.integer :year, null: false
|
37
|
+
t.timestamps
|
38
|
+
end
|
39
|
+
|
40
|
+
create_table :copyable_coins, force: true do |t|
|
41
|
+
t.string :kind
|
42
|
+
t.integer :year
|
43
|
+
t.timestamps
|
44
|
+
end
|
45
|
+
|
46
|
+
create_table :copyable_owners, force: true do |t|
|
47
|
+
t.string :name, null: false
|
48
|
+
t.timestamps
|
49
|
+
end
|
50
|
+
|
51
|
+
create_table :copyable_pet_foods, force: true do |t|
|
52
|
+
t.string :name, null: false
|
53
|
+
t.timestamps
|
54
|
+
end
|
55
|
+
|
56
|
+
create_table :copyable_pet_foods_pets, force: true, id: false do |t|
|
57
|
+
t.integer :copyable_pet_id, null: false
|
58
|
+
t.integer :copyable_pet_food_id, null: false
|
59
|
+
end
|
60
|
+
|
61
|
+
create_table :copyable_pet_profiles, force: true do |t|
|
62
|
+
t.string :description, null: false
|
63
|
+
t.string :nickname
|
64
|
+
t.integer :copyable_pet_id, null: false
|
65
|
+
t.timestamps
|
66
|
+
end
|
67
|
+
|
68
|
+
create_table :copyable_pet_sitters, force: true do |t|
|
69
|
+
t.string :name, null: false
|
70
|
+
t.timestamps
|
71
|
+
end
|
72
|
+
|
73
|
+
create_table :copyable_pet_sitting_patronages, force: true do |t|
|
74
|
+
t.integer :copyable_pet_id, null: false
|
75
|
+
t.integer :copyable_pet_sitter_id, null: false
|
76
|
+
t.timestamps
|
77
|
+
end
|
78
|
+
|
79
|
+
create_table :copyable_pet_tags, force: true do |t|
|
80
|
+
t.string :registered_name, null: false
|
81
|
+
t.integer :copyable_pet_id, null: false
|
82
|
+
t.timestamps
|
83
|
+
end
|
84
|
+
|
85
|
+
create_table :copyable_pets, force: true do |t|
|
86
|
+
t.string :name, null: false
|
87
|
+
t.string :kind, null: false
|
88
|
+
t.integer :birth_year
|
89
|
+
t.timestamps
|
90
|
+
end
|
91
|
+
|
92
|
+
create_table :copyable_pictures, force: true do |t|
|
93
|
+
t.string :name
|
94
|
+
t.integer :imageable_id
|
95
|
+
t.string :imageable_type
|
96
|
+
t.integer :picture_album_id
|
97
|
+
t.timestamps
|
98
|
+
end
|
99
|
+
|
100
|
+
create_table :copyable_products, force: true do |t|
|
101
|
+
t.string :name
|
102
|
+
t.timestamps
|
103
|
+
end
|
104
|
+
|
105
|
+
create_table :copyable_toys, force: true do |t|
|
106
|
+
t.string :name
|
107
|
+
t.string :kind
|
108
|
+
t.integer :copyable_pet_id
|
109
|
+
t.timestamps
|
110
|
+
end
|
111
|
+
|
112
|
+
create_table :copyable_trees, force: true do |t|
|
113
|
+
t.string :kind
|
114
|
+
t.timestamps
|
115
|
+
end
|
116
|
+
|
117
|
+
create_table :copyable_vehicles, force: true do |t|
|
118
|
+
t.string :name
|
119
|
+
t.integer :copyable_owner_id
|
120
|
+
t.timestamps
|
121
|
+
end
|
122
|
+
|
123
|
+
create_table :copyable_warranties, force: true do |t|
|
124
|
+
t.string :name
|
125
|
+
t.integer :copyable_amenity_id
|
126
|
+
t.timestamps
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
131
|
+
@already_created_tables = true
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative 'helper/copyable_spec_helper'
|
2
|
+
|
3
|
+
describe Copyable::ModelHooks do
|
4
|
+
context 'callbacks' do
|
5
|
+
|
6
|
+
# Note: the relevant callbacks are defined in CopyableTree in helper/test_models.rb
|
7
|
+
|
8
|
+
describe '.disable!' do
|
9
|
+
before(:each) do
|
10
|
+
puts "Dennis before!"
|
11
|
+
Copyable::ModelHooks.disable!(CopyableTree)
|
12
|
+
end
|
13
|
+
after(:each) do
|
14
|
+
puts "Dennis after"
|
15
|
+
Copyable::ModelHooks.reenable!(CopyableTree)
|
16
|
+
end
|
17
|
+
it 'should prevent callbacks from executing' do
|
18
|
+
puts "Dennis 1"
|
19
|
+
expect {
|
20
|
+
tree = CopyableTree.create!(kind: 'magnolia')
|
21
|
+
}.to_not raise_error
|
22
|
+
puts "Dennis 2"
|
23
|
+
end
|
24
|
+
it 'should not prevent model actions from executing' do
|
25
|
+
puts "Dennis 4"
|
26
|
+
expect(CopyableTree.count).to eq(0)
|
27
|
+
tree = CopyableTree.create!(kind: 'magnolia')
|
28
|
+
expect(CopyableTree.count).to eq(1)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '.reenable!' do
|
33
|
+
it 'should allow callbacks to execute again' do
|
34
|
+
Copyable::ModelHooks.disable!(CopyableTree)
|
35
|
+
Copyable::ModelHooks.reenable!(CopyableTree)
|
36
|
+
expect {
|
37
|
+
tree = CopyableTree.create!(kind: 'magnolia')
|
38
|
+
}.to raise_error(RuntimeError, "callback2 called")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'observers' do
|
44
|
+
|
45
|
+
# Note: the relevant model and observer class is defined in helper/test_models.rb
|
46
|
+
|
47
|
+
describe '.disable!' do
|
48
|
+
before(:each) do
|
49
|
+
Copyable::ModelHooks.disable!(CopyableCar)
|
50
|
+
end
|
51
|
+
after(:each) do
|
52
|
+
Copyable::ModelHooks.reenable!(CopyableCar)
|
53
|
+
end
|
54
|
+
it 'should prevent observers from executing' do
|
55
|
+
expect {
|
56
|
+
car = CopyableCar.create!(make: 'Ferrari', model: 'California', year: 2009)
|
57
|
+
}.to_not raise_error
|
58
|
+
end
|
59
|
+
it 'should not prevent model actions from executing' do
|
60
|
+
expect(CopyableCar.count).to eq(0)
|
61
|
+
car = CopyableCar.create!(make: 'Ferrari', model: 'California', year: 2009)
|
62
|
+
expect(CopyableCar.count).to eq(1)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'active_support/all'
|
3
|
+
require 'active_support/testing/time_helpers'
|
4
|
+
require 'database_cleaner'
|
5
|
+
|
6
|
+
Bundler.require(:default, :test)
|
7
|
+
|
8
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
config.before(:suite) do
|
13
|
+
DatabaseCleaner.clean_with(:truncation)
|
14
|
+
end
|
15
|
+
|
16
|
+
config.before(:each) do
|
17
|
+
DatabaseCleaner.strategy = :transaction
|
18
|
+
end
|
19
|
+
|
20
|
+
config.before(:each) do
|
21
|
+
DatabaseCleaner.start
|
22
|
+
end
|
23
|
+
|
24
|
+
config.append_after(:each) do
|
25
|
+
DatabaseCleaner.clean
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,261 @@
|
|
1
|
+
require_relative 'helper/copyable_spec_helper'
|
2
|
+
|
3
|
+
describe 'a model hierarchy with lots of variations in copyable behavior' do
|
4
|
+
before(:each) do
|
5
|
+
|
6
|
+
#***************************************************************************
|
7
|
+
# Define all of the copyable declarations.
|
8
|
+
|
9
|
+
undefine_copyable_in CopyablePet
|
10
|
+
class CopyablePet < ActiveRecord::Base
|
11
|
+
copyable do
|
12
|
+
disable_all_callbacks_and_observers_except_validate
|
13
|
+
columns({
|
14
|
+
name: :copy,
|
15
|
+
kind: :copy,
|
16
|
+
birth_year: lambda { |orig| orig.birth_year + 1 },
|
17
|
+
})
|
18
|
+
associations({
|
19
|
+
copyable_toys: :do_not_copy,
|
20
|
+
copyable_pet_tag: :copy,
|
21
|
+
copyable_pet_profile: :copy,
|
22
|
+
copyable_pet_foods: :copy_only_habtm_join_records,
|
23
|
+
copyable_pet_sitting_patronages: :copy,
|
24
|
+
})
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
undefine_copyable_in CopyableToy
|
29
|
+
class CopyableToy < ActiveRecord::Base
|
30
|
+
copyable do
|
31
|
+
disable_all_callbacks_and_observers_except_validate
|
32
|
+
columns({
|
33
|
+
name: :copy,
|
34
|
+
kind: :copy,
|
35
|
+
copyable_pet_id: :copy,
|
36
|
+
})
|
37
|
+
associations({
|
38
|
+
})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
undefine_copyable_in CopyablePetTag
|
43
|
+
class CopyablePetTag < ActiveRecord::Base
|
44
|
+
copyable do
|
45
|
+
disable_all_callbacks_and_observers_except_validate
|
46
|
+
columns({
|
47
|
+
registered_name: :copy,
|
48
|
+
copyable_pet_id: :copy,
|
49
|
+
})
|
50
|
+
associations({
|
51
|
+
})
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
undefine_copyable_in CopyablePetFood
|
56
|
+
class CopyablePetFood < ActiveRecord::Base
|
57
|
+
copyable do
|
58
|
+
disable_all_callbacks_and_observers_except_validate
|
59
|
+
columns({
|
60
|
+
name: :copy,
|
61
|
+
})
|
62
|
+
associations({
|
63
|
+
copyable_pets: :copy_only_habtm_join_records,
|
64
|
+
})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
undefine_copyable_in CopyablePetSitter
|
69
|
+
class CopyablePetSitter < ActiveRecord::Base
|
70
|
+
copyable do
|
71
|
+
disable_all_callbacks_and_observers_except_validate
|
72
|
+
columns({
|
73
|
+
name: :copy,
|
74
|
+
})
|
75
|
+
associations({
|
76
|
+
copyable_pet_sitting_patronages: :copy,
|
77
|
+
})
|
78
|
+
after_copy do |original_model, new_model|
|
79
|
+
new_model.name = new_model.name + '*'
|
80
|
+
new_model.save!
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
undefine_copyable_in CopyablePetSittingPatronage
|
86
|
+
class CopyablePetSittingPatronage < ActiveRecord::Base
|
87
|
+
copyable do
|
88
|
+
disable_all_callbacks_and_observers_except_validate
|
89
|
+
columns({
|
90
|
+
copyable_pet_id: :copy,
|
91
|
+
copyable_pet_sitter_id: :copy,
|
92
|
+
})
|
93
|
+
associations({
|
94
|
+
})
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
undefine_copyable_in CopyablePetProfile
|
99
|
+
class CopyablePetProfile < ActiveRecord::Base
|
100
|
+
copyable do
|
101
|
+
disable_all_callbacks_and_observers_except_validate
|
102
|
+
columns({
|
103
|
+
description: lambda { |orig| 'overwrite' },
|
104
|
+
nickname: :do_not_copy,
|
105
|
+
copyable_pet_id: :copy,
|
106
|
+
})
|
107
|
+
associations({
|
108
|
+
copyable_address: :copy,
|
109
|
+
})
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
undefine_copyable_in CopyableAddress
|
114
|
+
class CopyableAddress < ActiveRecord::Base
|
115
|
+
copyable do
|
116
|
+
disable_all_callbacks_and_observers_except_validate
|
117
|
+
columns({
|
118
|
+
address1: :copy,
|
119
|
+
address2: :copy,
|
120
|
+
city: :copy,
|
121
|
+
state: :copy,
|
122
|
+
copyable_pet_profile_id: :copy,
|
123
|
+
})
|
124
|
+
associations({
|
125
|
+
})
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#***************************************************************************
|
130
|
+
# Create the models.
|
131
|
+
|
132
|
+
# foods
|
133
|
+
@chow_mix = CopyablePetFood.create!(name: 'chow mix')
|
134
|
+
@mushy_food = CopyablePetFood.create!(name: 'mushy food')
|
135
|
+
@meal_scraps = CopyablePetFood.create!(name: 'meal scraps')
|
136
|
+
|
137
|
+
# sitters
|
138
|
+
@marty = CopyablePetSitter.create!(name: 'Martin Luther King, Jr.')
|
139
|
+
@bob = CopyablePetSitter.create!(name: 'Robert Frost')
|
140
|
+
@bubba = CopyablePetSitter.create!(name: 'William Jefferson Clinton')
|
141
|
+
|
142
|
+
# Rover
|
143
|
+
@rover = CopyablePet.create!(
|
144
|
+
name: 'Rover',
|
145
|
+
kind: 'canine',
|
146
|
+
birth_year: 2009)
|
147
|
+
@plastic_bone = CopyableToy.create!(
|
148
|
+
name: 'favorite bone',
|
149
|
+
kind: 'plastic squeaky toy',
|
150
|
+
copyable_pet: @rover)
|
151
|
+
@rovers_tag = CopyablePetTag.create!(
|
152
|
+
registered_name: 'Rover McSmith #3872D',
|
153
|
+
copyable_pet: @rover)
|
154
|
+
@rovers_profile = CopyablePetProfile.create!(
|
155
|
+
description: 'part Dachshund, part Great Dane, totally badass',
|
156
|
+
nickname: 'Wubby Wubby Roverkins',
|
157
|
+
copyable_pet: @rover)
|
158
|
+
@rovers_address = CopyableAddress.create!(
|
159
|
+
address1: '5 Maple St',
|
160
|
+
city: 'Mapleton',
|
161
|
+
state: 'MA',
|
162
|
+
copyable_pet_profile: @rovers_profile)
|
163
|
+
@rover_bubba_patronage = CopyablePetSittingPatronage.create!(
|
164
|
+
copyable_pet: @rover,
|
165
|
+
copyable_pet_sitter: @bubba)
|
166
|
+
@rover.copyable_pet_foods << @chow_mix
|
167
|
+
@rover.copyable_pet_foods << @meal_scraps
|
168
|
+
|
169
|
+
# Toonces
|
170
|
+
@toonces = CopyablePet.create!(
|
171
|
+
name: 'toonces',
|
172
|
+
kind: 'feline',
|
173
|
+
birth_year: 2005)
|
174
|
+
@yarn = CopyableToy.create!(
|
175
|
+
name: 'red yarn',
|
176
|
+
kind: 'just yarn',
|
177
|
+
copyable_pet: @toonces)
|
178
|
+
@toonces_tag = CopyablePetTag.create!(
|
179
|
+
registered_name: 'Toonces McSmith #3879F',
|
180
|
+
copyable_pet: @toonces)
|
181
|
+
@toonces_profile = CopyablePetProfile.create!(
|
182
|
+
description: 'really bad driver',
|
183
|
+
nickname: '*$#!',
|
184
|
+
copyable_pet: @toonces)
|
185
|
+
@tooncess_address = CopyableAddress.create!(
|
186
|
+
address1: '5 Maple St #2',
|
187
|
+
city: 'Mapleton',
|
188
|
+
state: 'MA',
|
189
|
+
copyable_pet_profile: @toonces_profile)
|
190
|
+
@toonces_bubba_patronage = CopyablePetSittingPatronage.create!(
|
191
|
+
copyable_pet: @toonces,
|
192
|
+
copyable_pet_sitter: @bubba)
|
193
|
+
@toonces_marty_patronage = CopyablePetSittingPatronage.create!(
|
194
|
+
copyable_pet: @toonces,
|
195
|
+
copyable_pet_sitter: @marty)
|
196
|
+
@toonces.copyable_pet_foods << @mushy_food
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'should copy without error' do
|
200
|
+
expect {
|
201
|
+
@rover.create_copy!
|
202
|
+
@toonces.create_copy!
|
203
|
+
}.to_not raise_error
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should not copy toys' do
|
207
|
+
expect(CopyableToy.count).to eq(2)
|
208
|
+
@rover.create_copy!
|
209
|
+
@toonces.create_copy!
|
210
|
+
expect(CopyableToy.count).to eq(2)
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'should not copy pet profile nickname' do
|
214
|
+
copied_rover = @rover.create_copy!
|
215
|
+
expect(copied_rover.copyable_pet_profile.nickname).to be_nil
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'should not copy pet sitters when copying pets' do
|
219
|
+
expect(CopyablePetSitter.count).to eq(3)
|
220
|
+
@rover.create_copy!
|
221
|
+
@toonces.create_copy!
|
222
|
+
expect(CopyablePetSitter.count).to eq(3)
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'should add a * to the pet sitter name after copying' do
|
226
|
+
expect(@marty.create_copy!.name).to eq('Martin Luther King, Jr.*')
|
227
|
+
expect(@bob.create_copy!.name).to eq('Robert Frost*')
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'should allow columns to be overriden' do
|
231
|
+
copied_rover = @rover.create_copy!(
|
232
|
+
override: {
|
233
|
+
name: 'overridden name',
|
234
|
+
kind: 'overridden kind',
|
235
|
+
birth_year: 1900 })
|
236
|
+
expect(copied_rover.name).to eq('overridden name')
|
237
|
+
expect(copied_rover.kind).to eq('overridden kind')
|
238
|
+
expect(copied_rover.birth_year).to eq(1900)
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'should allow multiple copying' do
|
242
|
+
expect {
|
243
|
+
3.times { @rover.create_copy! }
|
244
|
+
}.to_not raise_error
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'should have exactly the expected records after copying' do
|
248
|
+
@rover.create_copy!
|
249
|
+
expect(CopyablePet.count).to eq(3)
|
250
|
+
expect(CopyablePet.where(name: 'Rover').count).to eq(2)
|
251
|
+
expect(CopyableToy.count).to eq(2)
|
252
|
+
expect(CopyablePetTag.count).to eq(3)
|
253
|
+
expect(CopyablePetTag.where(registered_name: 'Rover McSmith #3872D').count).to eq(2)
|
254
|
+
expect(CopyablePetFood.count).to eq(3)
|
255
|
+
expect(CopyablePetSitter.count).to eq(3)
|
256
|
+
expect(CopyablePetSittingPatronage.count).to eq(4)
|
257
|
+
expect(CopyablePetProfile.count).to eq(3)
|
258
|
+
expect(CopyableAddress.count).to eq(3)
|
259
|
+
expect(CopyableAddress.where(address1: '5 Maple St').count).to eq(2)
|
260
|
+
end
|
261
|
+
end
|