remarkable_activerecord 3.1.8 → 3.1.9

Sign up to get free protection for your applications and to get access to all the features.
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