shoulda 2.0.6 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +35 -7
- data/Rakefile +5 -3
- data/lib/shoulda.rb +7 -15
- data/lib/shoulda/action_mailer.rb +1 -1
- data/lib/shoulda/action_mailer/assertions.rb +32 -33
- data/lib/shoulda/active_record.rb +6 -2
- data/lib/shoulda/active_record/assertions.rb +62 -81
- data/lib/shoulda/active_record/helpers.rb +40 -0
- data/lib/shoulda/active_record/macros.rb +518 -639
- data/lib/shoulda/active_record/matchers.rb +42 -0
- data/lib/shoulda/active_record/matchers/allow_mass_assignment_of_matcher.rb +83 -0
- data/lib/shoulda/active_record/matchers/allow_value_matcher.rb +102 -0
- data/lib/shoulda/active_record/matchers/association_matcher.rb +226 -0
- data/lib/shoulda/active_record/matchers/ensure_inclusion_of_matcher.rb +87 -0
- data/lib/shoulda/active_record/matchers/ensure_length_of_matcher.rb +141 -0
- data/lib/shoulda/active_record/matchers/have_db_column_matcher.rb +169 -0
- data/lib/shoulda/active_record/matchers/have_index_matcher.rb +105 -0
- data/lib/shoulda/active_record/matchers/have_named_scope_matcher.rb +125 -0
- data/lib/shoulda/active_record/matchers/have_readonly_attribute_matcher.rb +59 -0
- data/lib/shoulda/active_record/matchers/validate_acceptance_of_matcher.rb +41 -0
- data/lib/shoulda/active_record/matchers/validate_numericality_of_matcher.rb +39 -0
- data/lib/shoulda/active_record/matchers/validate_presence_of_matcher.rb +60 -0
- data/lib/shoulda/active_record/matchers/validate_uniqueness_of_matcher.rb +148 -0
- data/lib/shoulda/active_record/matchers/validation_matcher.rb +56 -0
- data/lib/shoulda/assertions.rb +50 -40
- data/lib/shoulda/autoload_macros.rb +46 -0
- data/lib/shoulda/context.rb +124 -126
- data/lib/shoulda/controller.rb +8 -8
- data/lib/shoulda/controller/formats/html.rb +158 -160
- data/lib/shoulda/controller/formats/xml.rb +132 -134
- data/lib/shoulda/controller/helpers.rb +51 -53
- data/lib/shoulda/controller/macros.rb +278 -258
- data/lib/shoulda/controller/resource_options.rb +211 -214
- data/lib/shoulda/helpers.rb +5 -7
- data/lib/shoulda/macros.rb +63 -64
- data/lib/shoulda/private_helpers.rb +16 -18
- data/lib/shoulda/rails.rb +1 -8
- data/lib/shoulda/rspec.rb +5 -0
- data/lib/shoulda/tasks/list_tests.rake +6 -1
- data/lib/shoulda/test_unit.rb +19 -0
- data/rails/init.rb +1 -1
- data/test/README +2 -2
- data/test/fail_macros.rb +16 -16
- data/test/functional/posts_controller_test.rb +5 -2
- data/test/matchers/allow_mass_assignment_of_matcher_test.rb +68 -0
- data/test/matchers/allow_value_matcher_test.rb +41 -0
- data/test/matchers/association_matcher_test.rb +258 -0
- data/test/matchers/ensure_inclusion_of_matcher_test.rb +80 -0
- data/test/matchers/ensure_length_of_matcher_test.rb +158 -0
- data/test/matchers/have_db_column_matcher_test.rb +169 -0
- data/test/matchers/have_index_matcher_test.rb +74 -0
- data/test/matchers/have_named_scope_matcher_test.rb +65 -0
- data/test/matchers/have_readonly_attributes_matcher_test.rb +29 -0
- data/test/matchers/validate_acceptance_of_matcher_test.rb +44 -0
- data/test/matchers/validate_numericality_of_matcher_test.rb +52 -0
- data/test/matchers/validate_presence_of_matcher_test.rb +86 -0
- data/test/matchers/validate_uniqueness_of_matcher_test.rb +141 -0
- data/test/model_builder.rb +61 -0
- data/test/other/autoload_macro_test.rb +18 -0
- data/test/other/helpers_test.rb +58 -0
- data/test/other/private_helpers_test.rb +1 -1
- data/test/other/should_test.rb +16 -16
- data/test/rails_root/app/controllers/posts_controller.rb +6 -5
- data/test/rails_root/app/models/pets/dog.rb +10 -0
- data/test/rails_root/app/models/treat.rb +3 -0
- data/test/rails_root/app/models/user.rb +2 -2
- data/test/rails_root/app/views/layouts/posts.rhtml +2 -0
- data/test/rails_root/config/database.yml +1 -1
- data/test/rails_root/config/environments/{sqlite3.rb → test.rb} +0 -0
- data/test/rails_root/db/migrate/001_create_users.rb +3 -2
- data/test/rails_root/db/migrate/011_create_treats.rb +12 -0
- data/test/rails_root/log/test.log +0 -0
- data/test/rails_root/test/shoulda_macros/custom_macro.rb +6 -0
- data/test/rails_root/vendor/gems/gem_with_macro-0.0.1/shoulda_macros/gem_macro.rb +6 -0
- data/test/rails_root/vendor/plugins/plugin_with_macro/shoulda_macros/plugin_macro.rb +6 -0
- data/test/test_helper.rb +3 -1
- data/test/unit/address_test.rb +1 -1
- data/test/unit/dog_test.rb +5 -2
- data/test/unit/post_test.rb +7 -3
- data/test/unit/product_test.rb +2 -2
- data/test/unit/tag_test.rb +2 -1
- data/test/unit/user_test.rb +17 -8
- metadata +44 -4
- data/test/rails_root/app/models/dog.rb +0 -5
@@ -0,0 +1,141 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
2
|
+
|
3
|
+
class ValidateUniquenessOfMatcherTest < Test::Unit::TestCase # :nodoc:
|
4
|
+
|
5
|
+
context "a unique attribute" do
|
6
|
+
setup do
|
7
|
+
@model = define_model(:example, :attr => :string,
|
8
|
+
:other => :integer) do
|
9
|
+
validates_uniqueness_of :attr
|
10
|
+
end.new
|
11
|
+
end
|
12
|
+
|
13
|
+
context "with an existing value" do
|
14
|
+
setup do
|
15
|
+
@existing = Example.create!(:attr => 'value', :other => 1)
|
16
|
+
end
|
17
|
+
|
18
|
+
should "require a unique value for that attribute" do
|
19
|
+
assert_accepts validate_uniqueness_of(:attr), @model
|
20
|
+
end
|
21
|
+
|
22
|
+
should "pass when the subject is an existing record" do
|
23
|
+
assert_accepts validate_uniqueness_of(:attr), @existing
|
24
|
+
end
|
25
|
+
|
26
|
+
should "fail when a scope is specified" do
|
27
|
+
assert_rejects validate_uniqueness_of(:attr).scoped_to(:other), @model
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "without an existing value" do
|
32
|
+
setup do
|
33
|
+
assert_nil Example.find(:first)
|
34
|
+
end
|
35
|
+
|
36
|
+
should "fail to require a unique value" do
|
37
|
+
assert_rejects validate_uniqueness_of(:attr), @model
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "a unique attribute with a custom error and an existing value" do
|
43
|
+
setup do
|
44
|
+
@model = define_model(:example, :attr => :string) do
|
45
|
+
validates_uniqueness_of :attr, :message => 'Bad value'
|
46
|
+
end.new
|
47
|
+
Example.create!
|
48
|
+
end
|
49
|
+
|
50
|
+
should "fail when checking the default message" do
|
51
|
+
assert_rejects validate_uniqueness_of(:attr), @model
|
52
|
+
end
|
53
|
+
|
54
|
+
should "fail when checking a message that doesn't match" do
|
55
|
+
assert_rejects validate_uniqueness_of(:attr).with_message(/abc/i), @model
|
56
|
+
end
|
57
|
+
|
58
|
+
should "pass when checking a message that matches" do
|
59
|
+
assert_accepts validate_uniqueness_of(:attr).with_message(/bad/i), @model
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "a scoped unique attribute with an existing value" do
|
64
|
+
setup do
|
65
|
+
@model = define_model(:example, :attr => :string,
|
66
|
+
:scope1 => :integer,
|
67
|
+
:scope2 => :integer) do
|
68
|
+
validates_uniqueness_of :attr, :scope => [:scope1, :scope2]
|
69
|
+
end.new
|
70
|
+
@existing = Example.create!(:attr => 'value', :scope1 => 1, :scope2 => 2)
|
71
|
+
end
|
72
|
+
|
73
|
+
should "pass when the correct scope is specified" do
|
74
|
+
assert_accepts validate_uniqueness_of(:attr).scoped_to(:scope1, :scope2),
|
75
|
+
@model
|
76
|
+
end
|
77
|
+
|
78
|
+
should "pass when the subject is an existing record" do
|
79
|
+
assert_accepts validate_uniqueness_of(:attr).scoped_to(:scope1, :scope2),
|
80
|
+
@existing
|
81
|
+
end
|
82
|
+
|
83
|
+
should "fail when a different scope is specified" do
|
84
|
+
assert_rejects validate_uniqueness_of(:attr).scoped_to(:scope1), @model
|
85
|
+
end
|
86
|
+
|
87
|
+
should "fail when no scope is specified" do
|
88
|
+
assert_rejects validate_uniqueness_of(:attr), @model
|
89
|
+
end
|
90
|
+
|
91
|
+
should "fail when a non-existent attribute is specified as a scope" do
|
92
|
+
assert_rejects validate_uniqueness_of(:attr).scoped_to(:fake), @model
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "a non-unique attribute with an existing value" do
|
97
|
+
setup do
|
98
|
+
@model = define_model(:example, :attr => :string).new
|
99
|
+
Example.create!(:attr => 'value')
|
100
|
+
end
|
101
|
+
|
102
|
+
should "not require a unique value for that attribute" do
|
103
|
+
assert_rejects validate_uniqueness_of(:attr), @model
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "a case sensitive unique attribute with an existing value" do
|
108
|
+
setup do
|
109
|
+
@model = define_model(:example, :attr => :string) do
|
110
|
+
validates_uniqueness_of :attr, :case_sensitive => true
|
111
|
+
end.new
|
112
|
+
Example.create!(:attr => 'value')
|
113
|
+
end
|
114
|
+
|
115
|
+
should "not require a unique, case-insensitive value for that attribute" do
|
116
|
+
assert_rejects validate_uniqueness_of(:attr).case_insensitive, @model
|
117
|
+
end
|
118
|
+
|
119
|
+
should "require a unique, case-sensitive value for that attribute" do
|
120
|
+
assert_accepts validate_uniqueness_of(:attr), @model
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context "a case sensitive unique integer attribute with an existing value" do
|
125
|
+
setup do
|
126
|
+
@model = define_model(:example, :attr => :integer) do
|
127
|
+
validates_uniqueness_of :attr, :case_sensitive => true
|
128
|
+
end.new
|
129
|
+
Example.create!(:attr => 'value')
|
130
|
+
end
|
131
|
+
|
132
|
+
should "require a unique, case-insensitive value for that attribute" do
|
133
|
+
assert_accepts validate_uniqueness_of(:attr).case_insensitive, @model
|
134
|
+
end
|
135
|
+
|
136
|
+
should "require a unique, case-sensitive value for that attribute" do
|
137
|
+
assert_accepts validate_uniqueness_of(:attr), @model
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class Test::Unit::TestCase
|
2
|
+
def create_table(table_name, &block)
|
3
|
+
connection = ActiveRecord::Base.connection
|
4
|
+
|
5
|
+
begin
|
6
|
+
connection.execute("DROP TABLE IF EXISTS #{table_name}")
|
7
|
+
connection.create_table(table_name, &block)
|
8
|
+
@created_tables ||= []
|
9
|
+
@created_tables << table_name
|
10
|
+
connection
|
11
|
+
rescue Exception => e
|
12
|
+
connection.execute("DROP TABLE IF EXISTS #{table_name}")
|
13
|
+
raise e
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def define_model_class(class_name, &block)
|
18
|
+
klass = Class.new(ActiveRecord::Base)
|
19
|
+
Object.const_set(class_name, klass)
|
20
|
+
|
21
|
+
klass.class_eval(&block) if block_given?
|
22
|
+
|
23
|
+
@defined_constants ||= []
|
24
|
+
@defined_constants << class_name
|
25
|
+
|
26
|
+
klass
|
27
|
+
end
|
28
|
+
|
29
|
+
def define_model(name, columns = {}, &block)
|
30
|
+
class_name = name.to_s.pluralize.classify
|
31
|
+
table_name = class_name.tableize
|
32
|
+
|
33
|
+
create_table(table_name) do |table|
|
34
|
+
columns.each do |name, type|
|
35
|
+
table.column name, type
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
define_model_class(class_name, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def teardown_with_models
|
43
|
+
if @defined_constants
|
44
|
+
@defined_constants.each do |class_name|
|
45
|
+
Object.send(:remove_const, class_name)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if @created_tables
|
50
|
+
@created_tables.each do |table_name|
|
51
|
+
ActiveRecord::Base.
|
52
|
+
connection.
|
53
|
+
execute("DROP TABLE IF EXISTS #{table_name}")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
teardown_without_models
|
58
|
+
end
|
59
|
+
alias_method :teardown_without_models, :teardown
|
60
|
+
alias_method :teardown, :teardown_with_models
|
61
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
2
|
+
|
3
|
+
class AutoloadMacroTest < Test::Unit::TestCase # :nodoc:
|
4
|
+
context "The macro auto-loader" do
|
5
|
+
should "load macros from the plugins" do
|
6
|
+
assert self.class.respond_to?('plugin_macro')
|
7
|
+
end
|
8
|
+
|
9
|
+
should "load macros from the gems" do
|
10
|
+
assert self.class.respond_to?('gem_macro')
|
11
|
+
end
|
12
|
+
|
13
|
+
should "load custom macros from ROOT/test/shoulda_macros" do
|
14
|
+
assert self.class.respond_to?('custom_macro')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
data/test/other/helpers_test.rb
CHANGED
@@ -180,4 +180,62 @@ class HelpersTest < Test::Unit::TestCase # :nodoc:
|
|
180
180
|
end
|
181
181
|
end
|
182
182
|
end
|
183
|
+
|
184
|
+
context "a matching matcher" do
|
185
|
+
setup do
|
186
|
+
@matcher = stub('matcher', :matches? => true,
|
187
|
+
:failure_message => 'bad failure message',
|
188
|
+
:negative_failure_message => 'big time failure')
|
189
|
+
end
|
190
|
+
|
191
|
+
should "pass when given to assert_accepts" do
|
192
|
+
assert_accepts @matcher, 'target'
|
193
|
+
end
|
194
|
+
|
195
|
+
context "when given to assert_rejects" do
|
196
|
+
setup do
|
197
|
+
begin
|
198
|
+
assert_rejects @matcher, 'target'
|
199
|
+
rescue Test::Unit::AssertionFailedError => @error
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
should "fail" do
|
204
|
+
assert_not_nil @error
|
205
|
+
end
|
206
|
+
|
207
|
+
should "use the error message from the matcher" do
|
208
|
+
assert_equal 'big time failure', @error.message
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "a non-matching matcher" do
|
214
|
+
setup do
|
215
|
+
@matcher = stub('matcher', :matches? => false,
|
216
|
+
:failure_message => 'big time failure',
|
217
|
+
:negative_failure_message => 'bad failure message')
|
218
|
+
end
|
219
|
+
|
220
|
+
should "pass when given to assert_rejects" do
|
221
|
+
assert_rejects @matcher, 'target'
|
222
|
+
end
|
223
|
+
|
224
|
+
context "when given to assert_accepts" do
|
225
|
+
setup do
|
226
|
+
begin
|
227
|
+
assert_accepts @matcher, 'target'
|
228
|
+
rescue Test::Unit::AssertionFailedError => @error
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
should "fail" do
|
233
|
+
assert_not_nil @error
|
234
|
+
end
|
235
|
+
|
236
|
+
should "use the error message from the matcher" do
|
237
|
+
assert_equal 'big time failure', @error.message
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
183
241
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
2
2
|
|
3
3
|
class PrivateHelpersTest < Test::Unit::TestCase # :nodoc:
|
4
|
-
include
|
4
|
+
include Shoulda::Private
|
5
5
|
context "get_options!" do
|
6
6
|
should "remove opts from args" do
|
7
7
|
args = [:a, :b, {}]
|
data/test/other/should_test.rb
CHANGED
@@ -119,21 +119,21 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
|
|
119
119
|
|
120
120
|
def test_should_create_a_new_context
|
121
121
|
assert_nothing_raised do
|
122
|
-
|
122
|
+
Shoulda::Context.new("context name", self) do; end
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
126
|
def test_should_create_a_nested_context
|
127
127
|
assert_nothing_raised do
|
128
|
-
parent =
|
129
|
-
child =
|
128
|
+
parent = Shoulda::Context.new("Parent", self) do; end
|
129
|
+
child = Shoulda::Context.new("Child", parent) do; end
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
133
|
def test_should_name_a_contexts_correctly
|
134
|
-
parent =
|
135
|
-
child =
|
136
|
-
grandchild =
|
134
|
+
parent = Shoulda::Context.new("Parent", self) do; end
|
135
|
+
child = Shoulda::Context.new("Child", parent) do; end
|
136
|
+
grandchild = Shoulda::Context.new("GrandChild", child) do; end
|
137
137
|
|
138
138
|
assert_equal "Parent", parent.full_name
|
139
139
|
assert_equal "Parent Child", child.full_name
|
@@ -143,7 +143,7 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
|
|
143
143
|
# Should statements
|
144
144
|
|
145
145
|
def test_should_have_should_hashes_when_given_should_statements
|
146
|
-
context =
|
146
|
+
context = Shoulda::Context.new("name", self) do
|
147
147
|
should "be good" do; end
|
148
148
|
should "another" do; end
|
149
149
|
end
|
@@ -155,7 +155,7 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
|
|
155
155
|
# setup and teardown
|
156
156
|
|
157
157
|
def test_should_capture_setup_and_teardown_blocks
|
158
|
-
context =
|
158
|
+
context = Shoulda::Context.new("name", self) do
|
159
159
|
setup do; "setup"; end
|
160
160
|
teardown do; "teardown"; end
|
161
161
|
end
|
@@ -167,7 +167,7 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
|
|
167
167
|
# building
|
168
168
|
|
169
169
|
def test_should_create_shoulda_test_for_each_should_on_build
|
170
|
-
context =
|
170
|
+
context = Shoulda::Context.new("name", self) do
|
171
171
|
should "one" do; end
|
172
172
|
should "two" do; end
|
173
173
|
end
|
@@ -178,7 +178,7 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
|
|
178
178
|
|
179
179
|
def test_should_create_test_methods_on_build
|
180
180
|
tu_class = Test::Unit::TestCase
|
181
|
-
context =
|
181
|
+
context = Shoulda::Context.new("A Context", tu_class) do
|
182
182
|
should "define the test" do; end
|
183
183
|
end
|
184
184
|
|
@@ -188,7 +188,7 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
|
|
188
188
|
|
189
189
|
def test_should_create_test_methods_on_build_when_subcontext
|
190
190
|
tu_class = Test::Unit::TestCase
|
191
|
-
context =
|
191
|
+
context = Shoulda::Context.new("A Context", tu_class) do
|
192
192
|
context "with a child" do
|
193
193
|
should "define the test" do; end
|
194
194
|
end
|
@@ -203,21 +203,21 @@ class ShouldTest < Test::Unit::TestCase # :nodoc:
|
|
203
203
|
def test_should_create_a_new_context_and_build_it_on_Test_Unit_context
|
204
204
|
c = mock("context")
|
205
205
|
c.expects(:build)
|
206
|
-
|
206
|
+
Shoulda::Context.expects(:new).with("foo", kind_of(Class)).returns(c)
|
207
207
|
self.class.context "foo" do; end
|
208
208
|
end
|
209
209
|
|
210
210
|
def test_should_create_a_one_off_context_and_build_it_on_Test_Unit_should
|
211
211
|
s = mock("test")
|
212
|
-
|
213
|
-
|
212
|
+
Shoulda::Context.any_instance.expects(:should).with("rock", {}).returns(s)
|
213
|
+
Shoulda::Context.any_instance.expects(:build)
|
214
214
|
self.class.should "rock" do; end
|
215
215
|
end
|
216
216
|
|
217
217
|
def test_should_create_a_one_off_context_and_build_it_on_Test_Unit_should_eventually
|
218
218
|
s = mock("test")
|
219
|
-
|
220
|
-
|
219
|
+
Shoulda::Context.any_instance.expects(:should_eventually).with("rock").returns(s)
|
220
|
+
Shoulda::Context.any_instance.expects(:build)
|
221
221
|
self.class.should_eventually "rock" do; end
|
222
222
|
end
|
223
223
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class PostsController < ApplicationController
|
2
2
|
before_filter :ensure_logged_in
|
3
3
|
before_filter :load_user
|
4
|
-
|
4
|
+
|
5
5
|
def index
|
6
6
|
@posts = @user.posts
|
7
7
|
|
@@ -19,6 +19,7 @@ class PostsController < ApplicationController
|
|
19
19
|
|
20
20
|
def show
|
21
21
|
@post = @user.posts.find(params[:id])
|
22
|
+
@false_flag = false
|
22
23
|
|
23
24
|
respond_to do |format|
|
24
25
|
format.html { render :layout => 'wide' }
|
@@ -68,17 +69,17 @@ class PostsController < ApplicationController
|
|
68
69
|
def destroy
|
69
70
|
@post = @user.posts.find(params[:id])
|
70
71
|
@post.destroy
|
71
|
-
|
72
|
+
|
72
73
|
flash[:notice] = "Post was removed"
|
73
|
-
|
74
|
+
|
74
75
|
respond_to do |format|
|
75
76
|
format.html { redirect_to user_posts_url(@post.user) }
|
76
77
|
format.xml { head :ok }
|
77
78
|
end
|
78
79
|
end
|
79
|
-
|
80
|
+
|
80
81
|
private
|
81
|
-
|
82
|
+
|
82
83
|
def load_user
|
83
84
|
@user = User.find(params[:user_id])
|
84
85
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Pets
|
2
|
+
class Dog < ActiveRecord::Base
|
3
|
+
belongs_to :user, :foreign_key => :owner_id
|
4
|
+
belongs_to :address, :dependent => :destroy
|
5
|
+
has_many :treats
|
6
|
+
has_and_belongs_to_many :fleas, :join_table => :fleas
|
7
|
+
validates_presence_of :treats, :fleas
|
8
|
+
validates_presence_of :owner_id
|
9
|
+
end
|
10
|
+
end
|