multiple_table_inheritance 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - ruby-head
7
+ - ree
data/CHANGELOG.md CHANGED
@@ -1,6 +1,13 @@
1
+ 0.1.4
2
+
3
+ * Fixed mass assignment when security is only defined in child model.
4
+ * Added support for namespaced models.
5
+ * Added many more unit tests.
6
+
1
7
  0.1.3
2
8
 
3
9
  * Fixed a bug that prevented migrations from being run properly.
10
+ * Added several unit tests.
4
11
 
5
12
  0.1.2
6
13
 
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  Multiple Table Inheritance
2
2
  ==========================
3
3
 
4
+ [![Build Status](https://secure.travis-ci.org/mhuggins/multiple_table_inheritance.png)](http://travis-ci.org/mhuggins/multiple_table_inheritance)
5
+
4
6
  Multiple Table Inheritance is a plugin designed to allow for multiple table
5
7
  inheritance between your database tables and your ActiveRecord models.
6
8
 
@@ -19,15 +21,20 @@ How to Install
19
21
 
20
22
  From the command line:
21
23
 
22
- gem install multiple-table-inheritance
24
+ gem install multiple_table_inheritance
23
25
 
24
26
  From your Gemfile:
25
27
 
26
- gem 'multiple_table_inheritance', '~> 0.1.3'
28
+ gem 'multiple_table_inheritance', '~> 0.1.4'
27
29
 
28
30
  Usage
29
31
  =====
30
32
 
33
+ The following sections attempt to explain full coverage for the Multiple Table
34
+ Inheritance plugin. For full code examples, take a look at the test database
35
+ structure and associated models found in `spec/support/tables.rb` and
36
+ `spec/support/models.rb`, respectively.
37
+
31
38
  Running Migrations
32
39
  ------------------
33
40
 
@@ -44,8 +51,10 @@ that this column be non-null for sanity.
44
51
  t.timestamps
45
52
  end
46
53
 
47
- When creating tables that are derived from your superclass table, simple
48
- provide the `:inherits` hash option to your `create_table` call.
54
+ When creating tables that are derived from your superclass table, simply
55
+ provide the `:inherits` hash option to your `create_table` call. The value of
56
+ the option represent the name by which the association is referenced in your
57
+ model.
49
58
 
50
59
  create_table :programmers, :inherits => :employee do |t|
51
60
  t.datetime :training_completed_at
@@ -96,6 +105,11 @@ to `inherits_from`. (Presently, this has only be tested to work with the
96
105
  Creating Records
97
106
  ----------------
98
107
 
108
+ Records can be created directly from their inheriting (child) model classes.
109
+ Given model names `Employee`, `Programmer`, and `Manager` based upon the table
110
+ structures outlined in the "Running Migrations" section, records can be created
111
+ in the following manner.
112
+
99
113
  Programmer.create(
100
114
  :first_name => 'Bob',
101
115
  :last_name => 'Smith',
@@ -142,18 +156,18 @@ Validation
142
156
  When creating a new record that inherits from another model, validation is
143
157
  taken into consideration across both models.
144
158
 
145
- class ::Employee < ActiveRecord::Base
159
+ class Employee < ActiveRecord::Base
146
160
  acts_as_superclass
147
161
  validates :first_name, :presence => true
148
162
  validates :last_name, :presence => true
149
163
  validates :salary, :presence => true, :numericality => { :min => 0 }
150
164
  end
151
165
 
152
- class ::Programmer < ActiveRecord::Base
166
+ class Programmer < ActiveRecord::Base
153
167
  inherits_from :employee
154
168
  end
155
169
 
156
- class ::Manager < ActiveRecord::Base
170
+ class Manager < ActiveRecord::Base
157
171
  inherits_from :employee
158
172
  validates :bonus, :presence => true, :numericality => true
159
173
  end
@@ -180,12 +194,6 @@ for a normal ActiveRecord model.
180
194
  attr_accessible :bonus
181
195
  end
182
196
 
183
- **NOTE:** When an ActiveRecord model does not make a call to `attr_accessible`,
184
- all its fields are presumed to be accessible. Currently, when using
185
- MultipleTableInheritance, if a parent class does not call `attr_accesible` and
186
- one of its children does, then the parent's attributes cannot properly be
187
- stored. This will be fixed in a future release.
188
-
189
197
  Associations
190
198
  ------------
191
199
 
@@ -220,3 +228,31 @@ Associations will also work in the same way as other attributes.
220
228
  programmer = Programmer.first # <Programmer employee_id: 1 training_completed_at: "2009-03-06 00:30:00">
221
229
  programmer.languages.collect(&:name) # ['Java', 'C++']
222
230
  programmer.team.name # 'Website Front-End'
231
+
232
+ Methods
233
+ -------
234
+
235
+ When inheriting from another parent model, methods can optionally be called on
236
+ the parent model automatically as well. To do so, specify the `:methods`
237
+ option when calling `inherits_from`.
238
+
239
+ class Employee < ActiveRecord::Base
240
+ acts_as_superclass
241
+ belongs_to :team
242
+
243
+ def give_raise!(amount)
244
+ update_attribute!(:salary, self.salary + amount)
245
+ puts "Congrats on your well-deserved raise, #{self.first_name}!"
246
+ end
247
+ end
248
+
249
+ class Programmer < ActiveRecord::Base
250
+ inherits_from :employee, :methods => true
251
+ end
252
+
253
+ @programmer = Programmer.first
254
+ @programmer.give_raise!
255
+ # yields: "Congrats on your well-deserved raise, Mike!"
256
+
257
+ NOTE: This is not fully implemented yet as of version 0.1.4. Please wait until
258
+ a future release to prior to using this feature.
@@ -3,5 +3,6 @@ module MultipleTableInheritance
3
3
  extend ActiveSupport::Autoload
4
4
 
5
5
  autoload :Base
6
+ autoload :Sanitizer
6
7
  end
7
8
  end
@@ -2,7 +2,7 @@ module MultipleTableInheritance
2
2
  module Child
3
3
  module Base
4
4
  def self.default_options
5
- { :dependent => :destroy, :inherit_methods => false }
5
+ { :dependent => :destroy, :methods => false }
6
6
  end
7
7
 
8
8
  def self.included(base)
@@ -14,7 +14,7 @@ module MultipleTableInheritance
14
14
  def inherits_from(association_name, options={})
15
15
  # Standardize options, and remove those that should not affect the belongs_to relationship
16
16
  options = Base::default_options.merge(options.to_options)
17
- inherit_methods = options.delete(:inherit_methods)
17
+ inherit_methods = options.delete(:methods)
18
18
 
19
19
  extend FinderMethods, SharedMethods
20
20
  include InstanceMethods, SharedMethods
@@ -39,6 +39,9 @@ module MultipleTableInheritance
39
39
  attr_accessible attr.to_sym
40
40
  end
41
41
 
42
+ # Sanitize attributes across parent and child
43
+ self.mass_assignment_sanitizer = Child::Sanitizer.new(self)
44
+
42
45
  # Bind relationship, handle validation, and save properly.
43
46
  belongs_to parent_association_name, options
44
47
  alias_method_chain parent_association_name, :autobuild
@@ -47,13 +50,13 @@ module MultipleTableInheritance
47
50
  before_save :parent_association_must_be_saved
48
51
  end
49
52
 
50
- private
51
-
52
53
  def parent_association_class
53
54
  reflection = create_reflection(:belongs_to, parent_association_name, {}, self)
54
55
  reflection.klass
55
56
  end
56
57
 
58
+ private
59
+
57
60
  def inherited_columns_and_associations
58
61
  # Get the associated columns and relationship names
59
62
  inherited_columns = parent_association_class.column_names
@@ -78,7 +81,8 @@ module MultipleTableInheritance
78
81
  child_records = super(*args)
79
82
 
80
83
  child_records.each do |child|
81
- child.send(:parent_association=, parent_association_class.as_supertype.find_by_id(child.id))
84
+ parent = parent_association_class.as_supertype.find_by_id(child.id)
85
+ child.send(:parent_association=, parent)
82
86
  end
83
87
  end
84
88
  end
@@ -127,18 +131,17 @@ module MultipleTableInheritance
127
131
  end
128
132
 
129
133
  module DelegateMethods
130
- private
131
-
132
- def method_missing(name, *args)
134
+ def method_missing(name, *args, &block)
133
135
  if parent_association.respond_to?(name)
134
- parent_association.public_send(name, *args)
136
+ parent_association.send(name, *args, &block)
135
137
  else
136
- super(name, *args)
138
+ super(name, *args, &block)
137
139
  end
138
140
  end
139
141
 
140
- def respond_to?(name)
141
- parent_association.respond_to?(name) || super
142
+ def respond_to?(name, *args)
143
+ return true if name.to_sym == :parent_association
144
+ super(name, *args) || parent_association.respond_to?(name)
142
145
  end
143
146
  end
144
147
  end
@@ -0,0 +1,57 @@
1
+ module MultipleTableInheritance
2
+ module Child
3
+ class Sanitizer < ActiveModel::MassAssignmentSecurity::LoggerSanitizer
4
+ def initialize(target)
5
+ @target = target
6
+ super
7
+ end
8
+
9
+ def sanitize(attributes, authorizer)
10
+ sanitized_attributes = attributes.reject { |key, value| deny?(key) }
11
+ debug_protected_attribute_removal(attributes, sanitized_attributes)
12
+ sanitized_attributes
13
+ end
14
+
15
+ protected
16
+
17
+ def deny?(key)
18
+ return true if protected_attribute?(key)
19
+ return !accessible_attribute?(key)
20
+ end
21
+
22
+ def protected_attribute?(key)
23
+ protected_attributes.include?(key)
24
+ end
25
+
26
+ def accessible_attribute?(key)
27
+ return true if accessible_parent_attributes.empty? && parent_attribute_names.include?(key)
28
+ return true if accessible_child_attributes.empty? && child_attribute_names.include?(key)
29
+ accessible_attributes.include?(key)
30
+ end
31
+
32
+ def protected_attributes
33
+ @protected_attributes ||= (@target.protected_attributes + @target.parent_association_class.protected_attributes)
34
+ end
35
+
36
+ def accessible_attributes
37
+ @accessible_attributes ||= (@target.accessible_attributes + @target.parent_association_class.accessible_attributes)
38
+ end
39
+
40
+ def accessible_child_attributes
41
+ @accessible_child_attributes ||= @target.accessible_attributes
42
+ end
43
+
44
+ def accessible_parent_attributes
45
+ @accessible_parent_attributes ||= @target.parent_association_class.accessible_attributes
46
+ end
47
+
48
+ def child_attribute_names
49
+ @child_attribute_names ||= @target.attribute_names
50
+ end
51
+
52
+ def parent_attribute_names
53
+ @parent_attribute_names ||= @target.parent_association_class.attribute_names
54
+ end
55
+ end
56
+ end
57
+ end
@@ -31,7 +31,7 @@ module MultipleTableInheritance
31
31
  child.delete
32
32
  end
33
33
  rescue NameError => e
34
- # TODO log error
34
+ logger.warn "Can't find matching child association for deletion: #{self.class} ##{id}" if logger
35
35
  end
36
36
 
37
37
  def find_by_subtype(*args)
@@ -19,7 +19,7 @@ module MultipleTableInheritance
19
19
  klass = type.constantize
20
20
  child_records += klass.find(ids)
21
21
  rescue NameError => e
22
- # TODO log error
22
+ logger.warn "Can't find matching child association for deletion: #{type} #{ids.inspect}" if logger
23
23
  end
24
24
  end
25
25
 
@@ -1,3 +1,3 @@
1
1
  module MultipleTableInheritance
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
@@ -1,92 +1,415 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe MultipleTableInheritance::Child do
4
- context 'retrieving records' do
5
- before do
6
- programmer = Programmer.create!(:first_name => 'Bob', :last_name => 'Smith', :salary => 50000)
7
- @programmer_id = programmer.id
8
- @programmer = Programmer.find(@programmer_id)
4
+ context 'non-namespaced classes with default subtype' do
5
+ context 'retrieving records' do
6
+ before do
7
+ programmer = Programmer.create!(:first_name => 'Bob', :last_name => 'Smith', :salary => 50000)
8
+ @programmer_id = programmer.id
9
+ @programmer = Programmer.find(@programmer_id)
10
+ end
11
+
12
+ it 'should retrieve child records' do
13
+ @programmer.should be_instance_of(Programmer)
14
+ @programmer.id.should be(@programmer_id)
15
+ end
16
+
17
+ it 'should allow access to parent record' do
18
+ @programmer.employee.should be_instance_of(Employee)
19
+ @programmer.employee.id.should be(@programmer_id)
20
+ end
9
21
  end
10
22
 
11
- it 'should retrieve child records' do
12
- @programmer.should be_instance_of(Programmer)
13
- @programmer.id.should be(@programmer_id)
23
+ context 'creating records' do
24
+ context 'with mass assignment security' do
25
+ context 'on just child' do
26
+ context 'without assigning all attributes' do
27
+ before(:each) do
28
+ @shirt = Shirt.create(:color => 'Red')
29
+ end
30
+
31
+ it 'should have errors' do
32
+ @shirt.errors.messages.should_not be_empty
33
+ @shirt.errors.messages.keys.should include(:size)
34
+ end
35
+
36
+ it 'should not have been saved' do
37
+ @shirt.should be_new_record
38
+ end
39
+ end
40
+
41
+ context 'with all attributes assigned' do
42
+ before(:each) do
43
+ @shirt = Shirt.create(:color => 'Red', :size => 'XL')
44
+ end
45
+
46
+ it 'should not have errors' do
47
+ @shirt.errors.messages.should be_empty
48
+ end
49
+
50
+ it 'should have been saved' do
51
+ @shirt.should_not be_new_record
52
+ end
53
+ end
54
+ end
55
+
56
+ context 'on just parent' do
57
+ context 'without assigning all attributes' do
58
+ before(:each) do
59
+ @janitor = Janitor.create(:first_name => 'Billy', :last_name => 'McGee')
60
+ end
61
+
62
+ it 'should have errors' do
63
+ @janitor.errors.messages.should_not be_empty
64
+ @janitor.errors.messages.keys.should include(:salary)
65
+ end
66
+
67
+ it 'should not have been saved' do
68
+ @janitor.should be_new_record
69
+ end
70
+ end
71
+
72
+ context 'with all attributes assigned' do
73
+ before(:each) do
74
+ @janitor = Janitor.create(:first_name => 'Billy', :last_name => 'McGee', :salary => 40000, :favorite_cleaner => 'Swiffer')
75
+ end
76
+
77
+ it 'should not have errors' do
78
+ @janitor.errors.messages.should be_empty
79
+ end
80
+
81
+ it 'should have been saved' do
82
+ @janitor.should_not be_new_record
83
+ end
84
+ end
85
+ end
86
+
87
+ context 'on child and parent' do
88
+ context 'with attributes set on instance' do
89
+ before(:each) do
90
+ @programmer = Programmer.create(:first_name => 'Joe', :last_name => 'Schmoe') do |programmer|
91
+ programmer.team = Team.first
92
+ programmer.salary = 45000
93
+ end
94
+ end
95
+
96
+ it 'should not have errors' do
97
+ @programmer.errors.messages.should be_empty
98
+ end
99
+
100
+ it 'should have been saved' do
101
+ @programmer.should_not be_new_record
102
+ end
103
+ end
104
+
105
+ context 'with attributes specified in hash' do
106
+ before(:each) do
107
+ @programmer = Programmer.create(:first_name => 'Joe', :last_name => 'Schmoe', :salary => 45000)
108
+ end
109
+
110
+ it 'should not have errors' do
111
+ @programmer.errors.messages.should be_empty
112
+ end
113
+
114
+ it 'should have been saved' do
115
+ @programmer.should_not be_new_record
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ context 'without mass assignment security' do
122
+ context 'without assigning all attributes' do
123
+ before(:each) do
124
+ @pants = Pants.create(:color => 'Blue', :waist_size => 32)
125
+ end
126
+
127
+ it 'should have errors' do
128
+ @pants.errors.messages.should_not be_empty
129
+ @pants.errors.messages.keys.should include(:length)
130
+ end
131
+
132
+ it 'should not have been saved' do
133
+ @pants.should be_new_record
134
+ end
135
+ end
136
+
137
+ context 'with all attributes assigned' do
138
+ before(:each) do
139
+ @pants = Pants.create(:color => 'Blue', :waist_size => 32, :length => 34)
140
+ end
141
+
142
+ it 'should not have errors' do
143
+ @pants.errors.messages.should be_empty
144
+ end
145
+
146
+ it 'should have been saved' do
147
+ @pants.should_not be_new_record
148
+ end
149
+ end
150
+ end
14
151
  end
15
152
 
16
- it 'should allow access to parent record' do
17
- @programmer.employee.should be_instance_of(Employee)
18
- @programmer.employee.id.should be(@programmer_id)
153
+ context 'updating records' do
154
+ pending "todo"
155
+ end
156
+
157
+ context 'deleting records' do
158
+ before do
159
+ mock_employees!
160
+ @programmer = Programmer.first
161
+ @programmer_id = @programmer.id
162
+ end
163
+
164
+ it 'should delete the parent record' do
165
+ @programmer.destroy
166
+ Employee.find_by_id(@programmer_id).should be_nil
167
+ end
168
+
169
+ it 'should delete the child record' do
170
+ @programmer.destroy.should be_true
171
+ Programmer.find_by_id(@programmer_id).should be_nil
172
+ end
19
173
  end
20
174
  end
21
175
 
22
- context 'creating records' do
23
- context 'with mass assignment security' do
24
- context 'specifying fields that are accessible' do
25
- it 'should assign accessible fields' do
26
- pending "create a model that allows for proper testing"
176
+ context 'namespaced classes with custom subtype' do
177
+ context 'retrieving records' do
178
+ before do
179
+ mock_pets!
180
+ @dog = Pet::Dog.first
181
+ @dog_id = @dog.id
182
+ end
183
+
184
+ it 'should retrieve child records' do
185
+ @dog.should be_instance_of(Pet::Dog)
186
+ @dog.id.should be(@dog_id)
187
+ end
188
+
189
+ it 'should allow access to parent record' do
190
+ @dog.pet.should be_instance_of(Pet::Pet)
191
+ @dog.pet.id.should be(@dog_id)
192
+ end
193
+ end
194
+
195
+ context 'creating records' do
196
+ before(:each) do
197
+ @owner = Pet::Owner.create!(:first_name => 'Bob', :last_name => 'Smith') { |owner| owner.ssn = '123456789' }
198
+ end
199
+
200
+ context 'with mass assignment security' do
201
+ context 'on just child' do
202
+ context 'without assigning all attributes' do
203
+ before(:each) do
204
+ @table = Store::Table.create(:brand => 'Rio', :name => 'Corner Office')
205
+ end
206
+
207
+ it 'should have errors' do
208
+ @table.errors.messages.should_not be_empty
209
+ @table.errors.messages.keys.should include(:color)
210
+ @table.errors.messages.keys.should include(:chairs)
211
+ end
212
+
213
+ it 'should not have been saved' do
214
+ @table.should be_new_record
215
+ end
216
+ end
217
+
218
+ context 'with all attributes assigned' do
219
+ before(:each) do
220
+ @table = Store::Table.create(:brand => 'Rio', :name => 'Corner Office', :color => 'Brown', :chairs => 1)
221
+ end
222
+
223
+ it 'should not have errors' do
224
+ @table.errors.messages.should be_empty
225
+ end
226
+
227
+ it 'should have been saved' do
228
+ @table.should_not be_new_record
229
+ end
230
+ end
27
231
  end
28
232
 
29
- it 'should save child associations' do
30
- pending "create a model that allows for proper testing"
233
+ context 'on just parent' do
234
+ context 'with attributes set on instance' do
235
+ before(:each) do
236
+ @dog = Pet::Dog.create(:name => 'Fido') do |dog|
237
+ dog.owner = @owner
238
+ dog.color = 'black'
239
+ dog.favorite_toy = 'Squeeky Ball'
240
+ end
241
+ end
242
+
243
+ it 'should not have errors' do
244
+ @dog.errors.messages.should be_empty
245
+ end
246
+
247
+ it 'should have been saved' do
248
+ @dog.should_not be_new_record
249
+ end
250
+ end
251
+
252
+ context 'with attributes specified in hash' do
253
+ before(:each) do
254
+ owner = Pet::Owner.create!(:first_name => 'Bob', :last_name => 'Smith') { |owner| owner.ssn = '123456789' }
255
+ @dog = Pet::Dog.create(:name => 'Fido', :color => 'black', :owner => @owner, :favorite_toy => 'Squeeky Ball')
256
+ end
257
+
258
+ it 'should assign accessible fields' do
259
+ @dog.name.should be_present
260
+ end
261
+
262
+ it 'should not assign secure fields' do
263
+ @dog.color.should_not be_present
264
+ @dog.owner.should_not be_present
265
+ @dog.owner_id.should_not be_present
266
+ @dog.favorite_toy.should_not be_present
267
+ end
268
+
269
+ it 'should have errors' do
270
+ @dog.errors.messages.should_not be_empty
271
+ end
272
+
273
+ it 'should not have been saved' do
274
+ @dog.should be_new_record
275
+ end
276
+ end
31
277
  end
32
278
 
33
- it 'should save parent associations' do
34
- pending "create a model that allows for proper testing"
279
+ context 'on child and parent' do
280
+ context 'with attributes set on instance' do
281
+ before(:each) do
282
+ @cat = Pet::Cat.create(:name => 'Mittens', :longest_nap => 100) do |cat|
283
+ cat.owner = @owner
284
+ cat.color = 'orange'
285
+ end
286
+ end
287
+
288
+ it 'should not have errors' do
289
+ @cat.errors.messages.should be_empty
290
+ end
291
+
292
+ it 'should have been saved' do
293
+ @cat.should_not be_new_record
294
+ end
295
+ end
296
+
297
+ context 'with attributes specified in hash' do
298
+ before(:each) do
299
+ @cat = Pet::Cat.create(:name => 'Fido', :color => 'black', :owner => @owner, :longest_nap => 50)
300
+ end
301
+
302
+ it 'should assign accessible fields' do
303
+ @cat.name.should be_present
304
+ @cat.longest_nap.should be_present
305
+ end
306
+
307
+ it 'should not assign secure fields' do
308
+ @cat.color.should_not be_present
309
+ @cat.owner.should_not be_present
310
+ @cat.owner_id.should_not be_present
311
+ end
312
+
313
+ it 'should have errors' do
314
+ @cat.errors.messages.should_not be_empty
315
+ end
316
+
317
+ it 'should not have been saved' do
318
+ @cat.should be_new_record
319
+ end
320
+ end
35
321
  end
36
322
  end
37
323
 
38
- context 'specifying fields that are not accessible' do
39
- it 'should not assign secure fields' do
40
- pending "create a model that allows for proper testing"
41
- end
42
-
43
- it 'should not save child associations' do
44
- pending "create a model that allows for proper testing"
324
+ context 'without mass assignment security' do
325
+ context 'without assigning all attributes' do
326
+ before(:each) do
327
+ @bed = Store::Bed.create(:brand => 'Sealy', :size => 'Queen')
328
+ end
329
+
330
+ it 'should have errors' do
331
+ @bed.errors.messages.should_not be_empty
332
+ @bed.errors.messages.keys.should include(:name)
333
+ end
334
+
335
+ it 'should not have been saved' do
336
+ @bed.should be_new_record
337
+ end
45
338
  end
46
339
 
47
- it 'should not save parent associations' do
48
- pending "create a model that allows for proper testing"
340
+ context 'with all attributes assigned' do
341
+ before(:each) do
342
+ @bed = Store::Bed.create(:brand => 'Sealy', :name => 'Feathertop', :size => 'Queen')
343
+ end
344
+
345
+ it 'should not have errors' do
346
+ @bed.errors.messages.should be_empty
347
+ end
348
+
349
+ it 'should have been saved' do
350
+ @bed.should_not be_new_record
351
+ end
49
352
  end
50
353
  end
51
354
  end
52
- end
53
-
54
- context 'updating records' do
55
- pending "todo"
56
- end
57
-
58
- context 'deleting records' do
59
- before do
60
- mock_employees!
61
- @programmer = Programmer.first
62
- @programmer_id = @programmer.id
63
- end
64
355
 
65
- it 'should delete the parent record' do
66
- @programmer.destroy
67
- Employee.find_by_id(@programmer_id).should be_nil
356
+ context 'updating records' do
357
+ pending "todo"
68
358
  end
69
359
 
70
- it 'should delete the child record' do
71
- @programmer.destroy.should be_true
72
- Programmer.find_by_id(@programmer_id).should be_nil
73
- end
74
- end
75
-
76
- context 'when instructed to inherit parent methods' do
77
- it 'should inherit parent methods' do
78
- pending "todo"
360
+ context 'deleting records' do
361
+ before do
362
+ mock_employees!
363
+ @programmer = Programmer.first
364
+ @programmer_id = @programmer.id
365
+ end
366
+
367
+ it 'should delete the parent record' do
368
+ @programmer.destroy
369
+ Employee.find_by_id(@programmer_id).should be_nil
370
+ end
371
+
372
+ it 'should delete the child record' do
373
+ @programmer.destroy.should be_true
374
+ Programmer.find_by_id(@programmer_id).should be_nil
375
+ end
79
376
  end
80
377
  end
81
378
 
82
379
  context 'methods' do
83
- before do
84
- mock_employees!
380
+ context 'accessing parent methods' do
381
+ context 'on models that allow methods to be inherited' do
382
+ before do
383
+ mock_employees!
384
+ @programmer = Programmer.first
385
+ end
386
+
387
+ it 'should allow parent methods to be called through child' do
388
+ pending "infinite recursion must be addressed"
389
+ # @programmer.should respond_to(:give_raise!)
390
+ end
391
+ end
392
+
393
+ context 'on models that do not allow methods to be inherited' do
394
+ before do
395
+ mock_pets!
396
+ @cat = Pet::Cat.first
397
+ end
398
+
399
+ it 'should not allow parent methods to be called through child' do
400
+ @cat.should_not respond_to(:rename!)
401
+ end
402
+ end
85
403
  end
86
404
 
87
405
  context 'accessing parent records' do
88
406
  before do
89
- @programmer = Programmer.create!(:first_name => 'Bob', :last_name => 'Smith', :salary => 50000)
407
+ @salary = 50000
408
+ @programmer = Programmer.create!(:first_name => 'Bob', :last_name => 'Smith', :salary => @salary)
409
+ end
410
+
411
+ it 'should allow access to parent attribute methods' do
412
+ @programmer.salary.should == @salary
90
413
  end
91
414
 
92
415
  it 'should allow access to parent records' do
@@ -97,6 +420,10 @@ describe MultipleTableInheritance::Child do
97
420
  end
98
421
 
99
422
  context 'searching by id' do
423
+ before do
424
+ mock_employees!
425
+ end
426
+
100
427
  context 'at the class level' do
101
428
  before do
102
429
  @employee_id = Employee.first.id