remarkable_activerecord 3.1.8 → 3.1.9

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.
Files changed (48) hide show
  1. data/CHANGELOG +140 -138
  2. data/LICENSE +20 -20
  3. data/README +80 -80
  4. data/lib/remarkable_activerecord.rb +29 -29
  5. data/lib/remarkable_activerecord/base.rb +248 -237
  6. data/lib/remarkable_activerecord/describe.rb +27 -27
  7. data/lib/remarkable_activerecord/human_names.rb +36 -36
  8. data/lib/remarkable_activerecord/matchers/accept_nested_attributes_for_matcher.rb +30 -30
  9. data/lib/remarkable_activerecord/matchers/allow_mass_assignment_of_matcher.rb +59 -59
  10. data/lib/remarkable_activerecord/matchers/allow_values_for_matcher.rb +85 -94
  11. data/lib/remarkable_activerecord/matchers/association_matcher.rb +283 -283
  12. data/lib/remarkable_activerecord/matchers/have_column_matcher.rb +68 -68
  13. data/lib/remarkable_activerecord/matchers/have_default_scope_matcher.rb +38 -38
  14. data/lib/remarkable_activerecord/matchers/have_index_matcher.rb +73 -73
  15. data/lib/remarkable_activerecord/matchers/have_readonly_attributes_matcher.rb +30 -30
  16. data/lib/remarkable_activerecord/matchers/have_scope_matcher.rb +85 -85
  17. data/lib/remarkable_activerecord/matchers/validate_acceptance_of_matcher.rb +50 -50
  18. data/lib/remarkable_activerecord/matchers/validate_associated_matcher.rb +97 -97
  19. data/lib/remarkable_activerecord/matchers/validate_confirmation_of_matcher.rb +44 -44
  20. data/lib/remarkable_activerecord/matchers/validate_exclusion_of_matcher.rb +53 -53
  21. data/lib/remarkable_activerecord/matchers/validate_inclusion_of_matcher.rb +52 -52
  22. data/lib/remarkable_activerecord/matchers/validate_length_of_matcher.rb +150 -150
  23. data/lib/remarkable_activerecord/matchers/validate_numericality_of_matcher.rb +181 -181
  24. data/lib/remarkable_activerecord/matchers/validate_presence_of_matcher.rb +29 -29
  25. data/lib/remarkable_activerecord/matchers/validate_uniqueness_of_matcher.rb +233 -233
  26. data/locale/en.yml +261 -261
  27. data/spec/accept_nested_attributes_for_matcher_spec.rb +1 -1
  28. data/spec/allow_mass_assignment_of_matcher_spec.rb +90 -82
  29. data/spec/allow_values_for_matcher_spec.rb +72 -63
  30. data/spec/association_matcher_spec.rb +612 -612
  31. data/spec/describe_spec.rb +3 -3
  32. data/spec/have_column_matcher_spec.rb +73 -73
  33. data/spec/have_default_scope_matcher_spec.rb +1 -1
  34. data/spec/have_index_matcher_spec.rb +87 -87
  35. data/spec/have_readonly_attributes_matcher_spec.rb +47 -47
  36. data/spec/have_scope_matcher_spec.rb +77 -77
  37. data/spec/model_builder.rb +101 -101
  38. data/spec/rcov.opts +1 -1
  39. data/spec/spec.opts +4 -4
  40. data/spec/spec_helper.rb +27 -27
  41. data/spec/validate_acceptance_of_matcher_spec.rb +68 -68
  42. data/spec/validate_associated_matcher_spec.rb +121 -121
  43. data/spec/validate_confirmation_of_matcher_spec.rb +58 -58
  44. data/spec/validate_length_of_matcher_spec.rb +218 -218
  45. data/spec/validate_numericality_of_matcher_spec.rb +179 -179
  46. data/spec/validate_presence_of_matcher_spec.rb +56 -56
  47. data/spec/validate_uniqueness_of_matcher_spec.rb +164 -164
  48. metadata +5 -5
@@ -1,283 +1,283 @@
1
- module Remarkable
2
- module ActiveRecord
3
- module Matchers
4
- class AssociationMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
- arguments :macro, :collection => :associations, :as => :association
6
-
7
- optionals :through, :source, :source_type, :class_name, :foreign_key, :dependent, :join_table, :as
8
- optionals :select, :conditions, :include, :group, :having, :order, :limit, :offset, :finder_sql, :counter_sql
9
- optionals :uniq, :readonly, :validate, :autosave, :polymorphic, :counter_cache, :default => true
10
-
11
- collection_assertions :association_exists?, :macro_matches?, :through_exists?, :source_exists?,
12
- :klass_exists?, :join_table_exists?, :foreign_key_exists?, :polymorphic_exists?,
13
- :counter_cache_exists?, :options_match?
14
-
15
- protected
16
-
17
- def association_exists?
18
- reflection
19
- end
20
-
21
- def macro_matches?
22
- reflection.macro == @macro
23
- end
24
-
25
- def through_exists?
26
- return true unless @options.key?(:through)
27
- reflection.through_reflection rescue false
28
- end
29
-
30
- def source_exists?
31
- return true unless @options.key?(:through)
32
- reflection.source_reflection rescue false
33
- end
34
-
35
- # Polymorphic associations does not have a klass.
36
- #
37
- def klass_exists?
38
- return true if @options[:polymorphic]
39
- reflection.klass rescue nil
40
- end
41
-
42
- # has_and_belongs_to_many only works if the tables are in the same
43
- # database, so we always look for the table in the subject connection.
44
- #
45
- def join_table_exists?
46
- return true unless reflection.macro == :has_and_belongs_to_many
47
- subject_class.connection.tables.include?(reflection.options[:join_table].to_s)
48
- end
49
-
50
- def foreign_key_exists?
51
- return true unless foreign_key_table
52
- table_has_column?(foreign_key_table_class, foreign_key_table, reflection_foreign_key)
53
- end
54
-
55
- def polymorphic_exists?
56
- return true unless @options[:polymorphic]
57
- klass_table_has_column?(subject_class, reflection_foreign_key.sub(/_id$/, '_type'))
58
- end
59
-
60
- def counter_cache_exists?
61
- return true unless @options[:counter_cache]
62
- klass_table_has_column?(reflection.klass, reflection.counter_cache_column.to_s)
63
- end
64
-
65
- def options_match?
66
- actual_options = {}
67
-
68
- @options.keys.each do |key|
69
- method = :"reflection_#{key}"
70
-
71
- @options[key] = @options[key].to_s
72
- actual_options[key] = (respond_to?(method, true) ? send(method) : reflection.options[key]).to_s
73
- end
74
-
75
- return @options == actual_options, :actual => actual_options.inspect
76
- end
77
-
78
- private
79
-
80
- def reflection
81
- @reflection ||= subject_class.reflect_on_association(@association.to_sym)
82
- end
83
-
84
- def subject_table_name
85
- subject_class.table_name.to_s
86
- end
87
-
88
- def reflection_class_name
89
- reflection.class_name.to_s rescue nil
90
- end
91
-
92
- def reflection_table_name
93
- reflection.klass.table_name.to_s rescue nil
94
- end
95
-
96
- def reflection_foreign_key
97
- reflection.primary_key_name.to_s
98
- end
99
-
100
- def table_has_column?(klass, table_name, column)
101
- klass.connection.columns(table_name, 'Remarkable column retrieval').any?{|c| c.name == column }
102
- end
103
-
104
- def klass_table_has_column?(klass, column)
105
- table_has_column?(klass, klass.table_name, column)
106
- end
107
-
108
- # In through we don't check the foreign_key, because it's spread
109
- # accross the through and the source reflection which should be tested
110
- # with their own macros.
111
- #
112
- # In cases a join table exists (has_and_belongs_to_many and through
113
- # associations), we check the foreign key in the join table.
114
- #
115
- # On belongs to, the foreign_key is in the subject class table and in
116
- # other cases it's on the reflection class table.
117
- #
118
- def foreign_key_table
119
- if reflection.options.key?(:through)
120
- nil
121
- elsif reflection.macro == :has_and_belongs_to_many
122
- reflection.options[:join_table]
123
- elsif reflection.macro == :belongs_to
124
- subject_table_name
125
- else
126
- reflection_table_name
127
- end
128
- end
129
-
130
- # Returns the foreign key table class to use the proper connection
131
- # when searching for the table and foreign key.
132
- #
133
- def foreign_key_table_class
134
- if [:belongs_to, :has_and_belongs_to_many].include?(reflection.macro)
135
- subject_class
136
- else
137
- reflection.klass
138
- end
139
- end
140
-
141
- def interpolation_options
142
- options = {}
143
- options[:macro] = Remarkable.t(@macro, :scope => matcher_i18n_scope, :default => @macro.to_s.gsub("_", ""))
144
- options[:options] = @options.inspect
145
-
146
- if @subject && reflection
147
- options.merge!(
148
- :actual_macro => Remarkable.t(reflection.macro, :scope => matcher_i18n_scope, :default => reflection.macro.to_s),
149
- :subject_table => subject_table_name.inspect,
150
- :reflection_table => reflection_table_name.inspect,
151
- :foreign_key_table => foreign_key_table.inspect,
152
- :polymorphic_column => reflection_foreign_key.sub(/_id$/, '_type').inspect,
153
- :counter_cache_column => reflection.counter_cache_column.to_s.inspect,
154
- :join_table => reflection.options[:join_table].inspect,
155
- :foreign_key => reflection_foreign_key.inspect
156
- )
157
- end
158
-
159
- options
160
- end
161
- end
162
-
163
- # Ensure that the belongs_to relationship exists. Will also test that the
164
- # subject table has the association_id column.
165
- #
166
- # == Options
167
- #
168
- # * <tt>:class_name</tt> - the expected associted class name.
169
- # * <tt>:foreign_key</tt> - the expected foreign key in the subject table.
170
- # * <tt>:dependent</tt> - the expected dependent value for the association.
171
- # * <tt>:readonly</tt> - checks wether readonly is true or false.
172
- # * <tt>:validate</tt> - checks wether validate is true or false.
173
- # * <tt>:autosave</tt> - checks wether autosave is true or false.
174
- # * <tt>:counter_cache</tt> - the expected dependent value for the association.
175
- # It also checks if the column actually exists in the table.
176
- # * <tt>:polymorphic</tt> - if the association should be polymorphic or not.
177
- # When true it also checks for the association_type column in the subject table.
178
- #
179
- # Plus all supported sql conditions options: :select, :conditions, :order,
180
- # :limit, :offset, :include, :group, :having.
181
- #
182
- # == Examples
183
- #
184
- # should_belong_to :parent, :polymorphic => true
185
- # it { should belong_to(:parent) }
186
- #
187
- def belong_to(*associations, &block)
188
- AssociationMatcher.new(:belongs_to, *associations, &block).spec(self)
189
- end
190
-
191
- # Ensures that the has_and_belongs_to_many relationship exists, if the join
192
- # table is in place and if the foreign_key column exists.
193
- #
194
- # == Options
195
- #
196
- # * <tt>:class_name</tt> - the expected associted class name.
197
- # * <tt>:join_table</tt> - the expected join table name.
198
- # * <tt>:foreign_key</tt> - the expected foreign key in the association table.
199
- # * <tt>:uniq</tt> - checks wether uniq is true or false.
200
- # * <tt>:readonly</tt> - checks wether readonly is true or false.
201
- # * <tt>:validate</tt> - checks wether validate is true or false.
202
- # * <tt>:autosave</tt> - checks wether autosave is true or false.
203
- #
204
- # Plus all supported sql conditions options: :select, :conditions, :order,
205
- # :limit, :offset, :include, :group, :having.
206
- #
207
- # == Examples
208
- #
209
- # should_have_and_belong_to_many :posts, :cars
210
- # it{ should have_and_belong_to_many :posts, :cars }
211
- #
212
- def have_and_belong_to_many(*associations, &block)
213
- AssociationMatcher.new(:has_and_belongs_to_many, *associations, &block).spec(self)
214
- end
215
-
216
- # Ensures that the has_many relationship exists. Will also test that the
217
- # associated table has the required columns. It works by default with
218
- # polymorphic association (:as does not have to be supplied).
219
- #
220
- # == Options
221
- #
222
- # * <tt>:class_name</tt> - the expected associted class name.
223
- # * <tt>:through</tt> - the expected join model which to perform the query.
224
- # It also checks if the through table exists.
225
- # * <tt>:source</tt> - the source of the through association.
226
- # * <tt>:source_type</tt> - the source type of the through association.
227
- # * <tt>:foreign_key</tt> - the expected foreign key in the associated table.
228
- # When used with :through, it will check for the foreign key in the join table.
229
- # * <tt>:dependent</tt> - the expected dependent value for the association.
230
- # * <tt>:uniq</tt> - checks wether uniq is true or false.
231
- # * <tt>:readonly</tt> - checks wether readonly is true or false.
232
- # * <tt>:validate</tt> - checks wether validate is true or false.
233
- # * <tt>:autosave</tt> - checks wether autosave is true or false.
234
- #
235
- # Plus all supported sql conditions options: :select, :conditions, :order,
236
- # :limit, :offset, :include, :group, :having.
237
- #
238
- # == Examples
239
- #
240
- # should_have_many :friends
241
- # should_have_many :enemies, :through => :friends
242
- # should_have_many :enemies, :dependent => :destroy
243
- #
244
- # it{ should have_many(:friends) }
245
- # it{ should have_many(:enemies, :through => :friends) }
246
- # it{ should have_many(:enemies, :dependent => :destroy) }
247
- #
248
- def have_many(*associations, &block)
249
- AssociationMatcher.new(:has_many, *associations, &block).spec(self)
250
- end
251
-
252
- # Ensures that the has_many relationship exists. Will also test that the
253
- # associated table has the required columns. It works by default with
254
- # polymorphic association (:as does not have to be supplied).
255
- #
256
- # == Options
257
- #
258
- # * <tt>:class_name</tt> - the expected associted class name.
259
- # * <tt>:through</tt> - the expected join model which to perform the query.
260
- # It also checks if the through table exists.
261
- # * <tt>:source</tt> - the source of the through association.
262
- # * <tt>:source_type</tt> - the source type of the through association.
263
- # * <tt>:foreign_key</tt> - the expected foreign key in the associated table.
264
- # When used with :through, it will check for the foreign key in the join table.
265
- # * <tt>:dependent</tt> - the expected dependent value for the association.
266
- # * <tt>:validate</tt> - checks wether validate is true or false.
267
- # * <tt>:autosave</tt> - checks wether autosave is true or false.
268
- #
269
- # Plus all supported sql conditions options: :select, :conditions, :order,
270
- # :limit, :offset, :include, :group, :having.
271
- #
272
- # == Examples
273
- #
274
- # should_have_one :universe
275
- # it{ should have_one(:universe) }
276
- #
277
- def have_one(*associations, &block)
278
- AssociationMatcher.new(:has_one, *associations, &block).spec(self)
279
- end
280
-
281
- end
282
- end
283
- end
1
+ module Remarkable
2
+ module ActiveRecord
3
+ module Matchers
4
+ class AssociationMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
+ arguments :macro, :collection => :associations, :as => :association
6
+
7
+ optionals :through, :source, :source_type, :class_name, :foreign_key, :dependent, :join_table, :as
8
+ optionals :select, :conditions, :include, :group, :having, :order, :limit, :offset, :finder_sql, :counter_sql
9
+ optionals :uniq, :readonly, :validate, :autosave, :polymorphic, :counter_cache, :default => true
10
+
11
+ collection_assertions :association_exists?, :macro_matches?, :through_exists?, :source_exists?,
12
+ :klass_exists?, :join_table_exists?, :foreign_key_exists?, :polymorphic_exists?,
13
+ :counter_cache_exists?, :options_match?
14
+
15
+ protected
16
+
17
+ def association_exists?
18
+ reflection
19
+ end
20
+
21
+ def macro_matches?
22
+ reflection.macro == @macro
23
+ end
24
+
25
+ def through_exists?
26
+ return true unless @options.key?(:through)
27
+ reflection.through_reflection rescue false
28
+ end
29
+
30
+ def source_exists?
31
+ return true unless @options.key?(:through)
32
+ reflection.source_reflection rescue false
33
+ end
34
+
35
+ # Polymorphic associations does not have a klass.
36
+ #
37
+ def klass_exists?
38
+ return true if @options[:polymorphic]
39
+ reflection.klass rescue nil
40
+ end
41
+
42
+ # has_and_belongs_to_many only works if the tables are in the same
43
+ # database, so we always look for the table in the subject connection.
44
+ #
45
+ def join_table_exists?
46
+ return true unless reflection.macro == :has_and_belongs_to_many
47
+ subject_class.connection.tables.include?(reflection.options[:join_table].to_s)
48
+ end
49
+
50
+ def foreign_key_exists?
51
+ return true unless foreign_key_table
52
+ table_has_column?(foreign_key_table_class, foreign_key_table, reflection_foreign_key)
53
+ end
54
+
55
+ def polymorphic_exists?
56
+ return true unless @options[:polymorphic]
57
+ klass_table_has_column?(subject_class, reflection_foreign_key.sub(/_id$/, '_type'))
58
+ end
59
+
60
+ def counter_cache_exists?
61
+ return true unless @options[:counter_cache]
62
+ klass_table_has_column?(reflection.klass, reflection.counter_cache_column.to_s)
63
+ end
64
+
65
+ def options_match?
66
+ actual_options = {}
67
+
68
+ @options.keys.each do |key|
69
+ method = :"reflection_#{key}"
70
+
71
+ @options[key] = @options[key].to_s
72
+ actual_options[key] = (respond_to?(method, true) ? send(method) : reflection.options[key]).to_s
73
+ end
74
+
75
+ return @options == actual_options, :actual => actual_options.inspect
76
+ end
77
+
78
+ private
79
+
80
+ def reflection
81
+ @reflection ||= subject_class.reflect_on_association(@association.to_sym)
82
+ end
83
+
84
+ def subject_table_name
85
+ subject_class.table_name.to_s
86
+ end
87
+
88
+ def reflection_class_name
89
+ reflection.class_name.to_s rescue nil
90
+ end
91
+
92
+ def reflection_table_name
93
+ reflection.klass.table_name.to_s rescue nil
94
+ end
95
+
96
+ def reflection_foreign_key
97
+ reflection.primary_key_name.to_s
98
+ end
99
+
100
+ def table_has_column?(klass, table_name, column)
101
+ klass.connection.columns(table_name, 'Remarkable column retrieval').any?{|c| c.name == column }
102
+ end
103
+
104
+ def klass_table_has_column?(klass, column)
105
+ table_has_column?(klass, klass.table_name, column)
106
+ end
107
+
108
+ # In through we don't check the foreign_key, because it's spread
109
+ # accross the through and the source reflection which should be tested
110
+ # with their own macros.
111
+ #
112
+ # In cases a join table exists (has_and_belongs_to_many and through
113
+ # associations), we check the foreign key in the join table.
114
+ #
115
+ # On belongs to, the foreign_key is in the subject class table and in
116
+ # other cases it's on the reflection class table.
117
+ #
118
+ def foreign_key_table
119
+ if reflection.options.key?(:through)
120
+ nil
121
+ elsif reflection.macro == :has_and_belongs_to_many
122
+ reflection.options[:join_table]
123
+ elsif reflection.macro == :belongs_to
124
+ subject_table_name
125
+ else
126
+ reflection_table_name
127
+ end
128
+ end
129
+
130
+ # Returns the foreign key table class to use the proper connection
131
+ # when searching for the table and foreign key.
132
+ #
133
+ def foreign_key_table_class
134
+ if [:belongs_to, :has_and_belongs_to_many].include?(reflection.macro)
135
+ subject_class
136
+ else
137
+ reflection.klass
138
+ end
139
+ end
140
+
141
+ def interpolation_options
142
+ options = {}
143
+ options[:macro] = Remarkable.t(@macro, :scope => matcher_i18n_scope, :default => @macro.to_s.gsub("_", ""))
144
+ options[:options] = @options.inspect
145
+
146
+ if @subject && reflection
147
+ options.merge!(
148
+ :actual_macro => Remarkable.t(reflection.macro, :scope => matcher_i18n_scope, :default => reflection.macro.to_s),
149
+ :subject_table => subject_table_name.inspect,
150
+ :reflection_table => reflection_table_name.inspect,
151
+ :foreign_key_table => foreign_key_table.inspect,
152
+ :polymorphic_column => reflection_foreign_key.sub(/_id$/, '_type').inspect,
153
+ :counter_cache_column => reflection.counter_cache_column.to_s.inspect,
154
+ :join_table => reflection.options[:join_table].inspect,
155
+ :foreign_key => reflection_foreign_key.inspect
156
+ )
157
+ end
158
+
159
+ options
160
+ end
161
+ end
162
+
163
+ # Ensure that the belongs_to relationship exists. Will also test that the
164
+ # subject table has the association_id column.
165
+ #
166
+ # == Options
167
+ #
168
+ # * <tt>:class_name</tt> - the expected associted class name.
169
+ # * <tt>:foreign_key</tt> - the expected foreign key in the subject table.
170
+ # * <tt>:dependent</tt> - the expected dependent value for the association.
171
+ # * <tt>:readonly</tt> - checks wether readonly is true or false.
172
+ # * <tt>:validate</tt> - checks wether validate is true or false.
173
+ # * <tt>:autosave</tt> - checks wether autosave is true or false.
174
+ # * <tt>:counter_cache</tt> - the expected dependent value for the association.
175
+ # It also checks if the column actually exists in the table.
176
+ # * <tt>:polymorphic</tt> - if the association should be polymorphic or not.
177
+ # When true it also checks for the association_type column in the subject table.
178
+ #
179
+ # Plus all supported sql conditions options: :select, :conditions, :order,
180
+ # :limit, :offset, :include, :group, :having.
181
+ #
182
+ # == Examples
183
+ #
184
+ # should_belong_to :parent, :polymorphic => true
185
+ # it { should belong_to(:parent) }
186
+ #
187
+ def belong_to(*associations, &block)
188
+ AssociationMatcher.new(:belongs_to, *associations, &block).spec(self)
189
+ end
190
+
191
+ # Ensures that the has_and_belongs_to_many relationship exists, if the join
192
+ # table is in place and if the foreign_key column exists.
193
+ #
194
+ # == Options
195
+ #
196
+ # * <tt>:class_name</tt> - the expected associted class name.
197
+ # * <tt>:join_table</tt> - the expected join table name.
198
+ # * <tt>:foreign_key</tt> - the expected foreign key in the association table.
199
+ # * <tt>:uniq</tt> - checks wether uniq is true or false.
200
+ # * <tt>:readonly</tt> - checks wether readonly is true or false.
201
+ # * <tt>:validate</tt> - checks wether validate is true or false.
202
+ # * <tt>:autosave</tt> - checks wether autosave is true or false.
203
+ #
204
+ # Plus all supported sql conditions options: :select, :conditions, :order,
205
+ # :limit, :offset, :include, :group, :having.
206
+ #
207
+ # == Examples
208
+ #
209
+ # should_have_and_belong_to_many :posts, :cars
210
+ # it{ should have_and_belong_to_many :posts, :cars }
211
+ #
212
+ def have_and_belong_to_many(*associations, &block)
213
+ AssociationMatcher.new(:has_and_belongs_to_many, *associations, &block).spec(self)
214
+ end
215
+
216
+ # Ensures that the has_many relationship exists. Will also test that the
217
+ # associated table has the required columns. It works by default with
218
+ # polymorphic association (:as does not have to be supplied).
219
+ #
220
+ # == Options
221
+ #
222
+ # * <tt>:class_name</tt> - the expected associted class name.
223
+ # * <tt>:through</tt> - the expected join model which to perform the query.
224
+ # It also checks if the through table exists.
225
+ # * <tt>:source</tt> - the source of the through association.
226
+ # * <tt>:source_type</tt> - the source type of the through association.
227
+ # * <tt>:foreign_key</tt> - the expected foreign key in the associated table.
228
+ # When used with :through, it will check for the foreign key in the join table.
229
+ # * <tt>:dependent</tt> - the expected dependent value for the association.
230
+ # * <tt>:uniq</tt> - checks wether uniq is true or false.
231
+ # * <tt>:readonly</tt> - checks wether readonly is true or false.
232
+ # * <tt>:validate</tt> - checks wether validate is true or false.
233
+ # * <tt>:autosave</tt> - checks wether autosave is true or false.
234
+ #
235
+ # Plus all supported sql conditions options: :select, :conditions, :order,
236
+ # :limit, :offset, :include, :group, :having.
237
+ #
238
+ # == Examples
239
+ #
240
+ # should_have_many :friends
241
+ # should_have_many :enemies, :through => :friends
242
+ # should_have_many :enemies, :dependent => :destroy
243
+ #
244
+ # it{ should have_many(:friends) }
245
+ # it{ should have_many(:enemies, :through => :friends) }
246
+ # it{ should have_many(:enemies, :dependent => :destroy) }
247
+ #
248
+ def have_many(*associations, &block)
249
+ AssociationMatcher.new(:has_many, *associations, &block).spec(self)
250
+ end
251
+
252
+ # Ensures that the has_many relationship exists. Will also test that the
253
+ # associated table has the required columns. It works by default with
254
+ # polymorphic association (:as does not have to be supplied).
255
+ #
256
+ # == Options
257
+ #
258
+ # * <tt>:class_name</tt> - the expected associted class name.
259
+ # * <tt>:through</tt> - the expected join model which to perform the query.
260
+ # It also checks if the through table exists.
261
+ # * <tt>:source</tt> - the source of the through association.
262
+ # * <tt>:source_type</tt> - the source type of the through association.
263
+ # * <tt>:foreign_key</tt> - the expected foreign key in the associated table.
264
+ # When used with :through, it will check for the foreign key in the join table.
265
+ # * <tt>:dependent</tt> - the expected dependent value for the association.
266
+ # * <tt>:validate</tt> - checks wether validate is true or false.
267
+ # * <tt>:autosave</tt> - checks wether autosave is true or false.
268
+ #
269
+ # Plus all supported sql conditions options: :select, :conditions, :order,
270
+ # :limit, :offset, :include, :group, :having.
271
+ #
272
+ # == Examples
273
+ #
274
+ # should_have_one :universe
275
+ # it{ should have_one(:universe) }
276
+ #
277
+ def have_one(*associations, &block)
278
+ AssociationMatcher.new(:has_one, *associations, &block).spec(self)
279
+ end
280
+
281
+ end
282
+ end
283
+ end