multiple_table_inheritance 0.1.3 → 0.1.4
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.
- data/.travis.yml +7 -0
- data/CHANGELOG.md +7 -0
- data/README.md +49 -13
- data/lib/multiple_table_inheritance/child.rb +1 -0
- data/lib/multiple_table_inheritance/child/base.rb +15 -12
- data/lib/multiple_table_inheritance/child/sanitizer.rb +57 -0
- data/lib/multiple_table_inheritance/parent/base.rb +1 -1
- data/lib/multiple_table_inheritance/parent/relation.rb +1 -1
- data/lib/multiple_table_inheritance/version.rb +1 -1
- data/{multiiple_table_inheritance.gemspec → multiple_table_inheritance.gemspec} +0 -0
- data/spec/active_record/child_spec.rb +383 -56
- data/spec/active_record/parent_spec.rb +51 -37
- data/spec/spec_helper.rb +10 -5
- data/spec/support/models.rb +75 -9
- data/spec/support/tables.rb +59 -10
- metadata +17 -15
data/.travis.yml
ADDED
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
|
+
[](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
|
24
|
+
gem install multiple_table_inheritance
|
23
25
|
|
24
26
|
From your Gemfile:
|
25
27
|
|
26
|
-
gem 'multiple_table_inheritance', '~> 0.1.
|
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,
|
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
|
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
|
166
|
+
class Programmer < ActiveRecord::Base
|
153
167
|
inherits_from :employee
|
154
168
|
end
|
155
169
|
|
156
|
-
class
|
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.
|
@@ -2,7 +2,7 @@ module MultipleTableInheritance
|
|
2
2
|
module Child
|
3
3
|
module Base
|
4
4
|
def self.default_options
|
5
|
-
{ :dependent => :destroy, :
|
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(:
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
|
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
|
File without changes
|
@@ -1,92 +1,415 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe MultipleTableInheritance::Child do
|
4
|
-
context '
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
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 '
|
23
|
-
context '
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
30
|
-
|
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
|
-
|
34
|
-
|
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 '
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
48
|
-
|
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
|
-
|
66
|
-
|
67
|
-
Employee.find_by_id(@programmer_id).should be_nil
|
356
|
+
context 'updating records' do
|
357
|
+
pending "todo"
|
68
358
|
end
|
69
359
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
84
|
-
|
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
|
-
@
|
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
|