active_mocker 1.5.2 → 1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/README.md +111 -120
  4. data/lib/active_mocker.rb +1 -1
  5. data/lib/active_mocker/active_record.rb +2 -2
  6. data/lib/active_mocker/active_record/relationships.rb +89 -79
  7. data/lib/active_mocker/active_record/scope.rb +16 -6
  8. data/lib/active_mocker/active_record/unknown_class_method.rb +7 -3
  9. data/lib/active_mocker/active_record/unknown_module.rb +19 -14
  10. data/lib/active_mocker/db_to_ruby_type.rb +1 -0
  11. data/lib/active_mocker/field.rb +1 -1
  12. data/lib/{file_reader.rb → active_mocker/file_reader.rb} +5 -0
  13. data/lib/active_mocker/generate.rb +6 -1
  14. data/lib/active_mocker/loaded_mocks.rb +43 -27
  15. data/lib/active_mocker/logger.rb +1 -0
  16. data/lib/active_mocker/mock/base.rb +153 -42
  17. data/lib/active_mocker/mock/collection.rb +6 -1
  18. data/lib/active_mocker/mock/do_nothing_active_record_methods.rb +4 -0
  19. data/lib/active_mocker/mock/exceptions.rb +14 -1
  20. data/lib/active_mocker/mock/has_many.rb +7 -0
  21. data/lib/active_mocker/mock/hash_process.rb +1 -0
  22. data/lib/active_mocker/mock/next_id.rb +1 -0
  23. data/lib/active_mocker/mock/queries.rb +216 -23
  24. data/lib/active_mocker/mock/relation.rb +21 -0
  25. data/lib/active_mocker/mock_template.erb +12 -2
  26. data/lib/active_mocker/model_reader.rb +1 -1
  27. data/lib/active_mocker/model_schema.rb +2 -1
  28. data/lib/active_mocker/public_methods.rb +24 -11
  29. data/lib/active_mocker/reparameterize.rb +1 -0
  30. data/lib/active_mocker/rspec_helper.rb +2 -0
  31. data/lib/active_mocker/schema_reader.rb +1 -0
  32. data/lib/active_mocker/string_reader.rb +14 -0
  33. data/lib/active_mocker/table.rb +1 -1
  34. data/lib/active_mocker/version.rb +1 -1
  35. metadata +4 -4
  36. data/lib/string_reader.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f663b4c6a2cc7e33f7452262782b2c880108f11
4
- data.tar.gz: 3c20207a292f97736ab241483da432d4da87510a
3
+ metadata.gz: 35f6abbcfa326377ac5fa015a3e1f9b434a9f936
4
+ data.tar.gz: 8aad0e3c8158712b9a09790d663854df1f5c96ec
5
5
  SHA512:
6
- metadata.gz: fbb61f7da2ff8078c5a02dc1f865f85e54a24ea3693e8928cb4be590ee1947aefbf8f5693529a57129631b769d0e6d6a49d6a1546c0800fc197d6b6cc7902b72
7
- data.tar.gz: afd4c94e4f99f62b8f380aa7196174fc9968e0fae7fd0f15ea1b80d3dbfd751fe1f65aa6aea48387dc1451a854db1f02edf262cf96e2a60815649d28932b7903
6
+ metadata.gz: 50d226edfaa35c359327b9ee3eb888bccd395fbe378d943302e368b04f7ed2885609fe6044ea4dffec02364af7fe46f11ced835d874c0acb8a957e61ee2e6ed7
7
+ data.tar.gz: ecc81c94d5186bf9a1e9b2e55556a2ec709ad97411df75b0a7806fc12d1491445eb44ba3d9971e7bcf0448837ac78b2ef9cb4d1e451a53cb2aa05850087d514c
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
+ ## 1.6 - 2014-07-29
4
+
5
+ ### Enhancement
6
+ - When calling limit and then delete_all will raises an ActiveMocker error:
7
+ - When mass assignment an unknown attribute the error message will mirror ActiveRecord.
8
+ - `where` can now accept a range instead of just an array as a value.
9
+ - `update` can update multiple records.
10
+ - `delete` can take an array or an integer
11
+ - `find` will now raise RecordNotFound
12
+ - `count` can now take an attribute name and will return the total count of records where the attribute is present.
13
+ - `create` now supports creating multiple records at once.
14
+
15
+ ### Added
16
+ - Added documentation for many methods.
17
+ - find_or_create_by/find_or_initialize_by now accessible from any collection.
18
+ - Instance methods attribute_names, attribute_present?, has_attribute?
19
+ - Using `ActiveMocker::Mock.config.experimental = true`. This will turn on features that are not complete and may not work as expected, especially if you have complex relationships. This will activate the following features:
20
+ - When passing in collection all item in collection will set its foreign key to the parent.
21
+ - When setting association by object it will set the child association.
22
+
23
+ ### Fixed
24
+ - `new_record?` would return the opposite of the truth.
25
+ - Remove method `to_hash` because it is unsupported in ActiveRecord.
26
+ - Requiring `active_mocker/rspec_helper` will now require loaded_mocks class. If no mocks were required the constant lookup would fails.
27
+ - When a model is deleted the mock file will not linger and letting tests pass.
3
28
 
4
29
  ## 1.5.2 - 2014-07-14
5
30
 
data/README.md CHANGED
@@ -5,19 +5,25 @@
5
5
  [![Dependency Status](https://gemnasium.com/zeisler/active_mocker.svg)](https://gemnasium.com/zeisler/active_mocker)
6
6
  [![Gitter chat](https://badges.gitter.im/zeisler/active_mocker.png)](https://gitter.im/zeisler/active_mocker)
7
7
 
8
- ActiveMocker creates mocks classes from ActiveRecord models. Allowing your test suite to run very fast by not loading Rails or hooking to a database. It parses the schema definition and the defined methods on a model then saves a ruby file that can be included within a test. Mocks are regenerated when the schema is modified so your mocks will not go stale. This prevents the case where your units tests pass but production code is failing.
8
+ ActiveMocker creates mocks classes from ActiveRecord models. Allowing your test suite to run very fast by not loading Rails or hooking to a database. It parses the schema definition and the defined methods on a model then saves a ruby file that can be included within a test. The mock can be run by themselves and run with a partial implementation of ActiveRecord. Mocks are regenerated when the schema is modified so your mocks will not go stale. Preventing the case where your units tests pass but production code is failing.
9
9
 
10
- Example from a real app
10
+ Examples from a real apps
11
11
 
12
12
  Finished in 0.54599 seconds
13
13
  190 examples, 0 failures
14
+
15
+ ------
16
+
17
+ Finished in 1 seconds
18
+ 374 examples, 0 failures
14
19
 
15
20
 
16
21
  ------------------------------------------
22
+
23
+ * [Documentation](#documentation)
17
24
  * [Contact](#contact)
18
25
  * [Installation](#installation)
19
26
  * [Setup](#setup)
20
- * [Configuration](#overwrite_defaults_configuration)
21
27
  * [Generate](#generate_mocks)
22
28
  * [Dependencies](#dependencies)
23
29
  * [Usage](#usage)
@@ -29,6 +35,12 @@ Example from a real app
29
35
  * [Contributing](#contributing)
30
36
 
31
37
 
38
+ ---------------------------
39
+
40
+ ## Documentation
41
+
42
+ [rdoc](http://rdoc.info/github/zeisler/active_mocker/master/ActiveMocker)
43
+
32
44
  ------------------------------------------
33
45
 
34
46
  ## Contact
@@ -59,15 +71,6 @@ Or install it yourself as:
59
71
 
60
72
  ## Setup
61
73
 
62
- ### Overwrite defaults configuration
63
-
64
- ActiveMocker::Generate.configure do |config|
65
- config.schema_file = File.join(Rails.root, 'db/schema.rb')
66
- config.model_dir = File.join(Rails.root, 'app/models')
67
- config.mock_dir = File.join(Rails.root, 'spec/mocks')
68
- config.logger = Rails.logger
69
- end
70
-
71
74
  ### Generate Mocks
72
75
 
73
76
  Running this rake task builds/rebuilds the mocks. It will be ran automatically after every schema modification. If the model changes this rake task needs to be called manually. You could add a file watcher for when your models change and have it run the rake task.
@@ -105,26 +108,44 @@ Running this rake task builds/rebuilds the mocks. It will be ran automatically a
105
108
 
106
109
  end
107
110
 
108
- -----------------
111
+ -----------------
112
+
113
+ ### Using With Rspec, --tag active_mocker:true
109
114
 
110
- #person_spec.rb
115
+ require 'rspec'
116
+ require 'active_mocker/rspec_helper'
117
+ require 'spec/mocks/person_mock'
118
+ require 'spec/mocks/account_mock'
119
+
120
+ describe 'Example', active_mocker:true do
121
+
122
+ before do
123
+ Person.create # stubbed for PersonMock.create
124
+ end
125
+
126
+ end
127
+
128
+ ----------
111
129
 
112
- require 'spec/mocks/person_mock.rb'
113
- require 'spec/mocks/account_mock.rb'
130
+ * Assigning the tag `active_mocker:true` will stub any ActiveRecord model Constants for Mock classes in `it` or `before/after(:each)`. This removes any need for dependency injection. Write tests and code like you would normally.
131
+ * To stub any Constants in `before(:all)`, `after(:all)` use `mock_class('ClassName')`.
132
+ * Will call `ActiveMocker::LoadedMocks.delete_all` in `after(:all)` block to clean up mock state for other tests.
114
133
 
115
- PersonMock.column_names
134
+ ---------
135
+
136
+ Person.column_names
116
137
  => ["id", "account_id", "first_name", "last_name", "address", "city"]
117
138
 
118
- person_mock = PersonMock.new( first_name: "Dustin",
139
+ person = Person.new( first_name: "Dustin",
119
140
  last_name: "Zeisler",
120
141
  account: AccountMock.new )
121
142
  => "#<PersonMock id: nil, account_id: nil, first_name: "Dustin", last_name: "Zeisler, address: nil, city: nil>"
122
143
 
123
- person_mock.first_name
144
+ person.first_name
124
145
  => "Dustin"
125
146
 
126
147
  ### When schema.rb changes, the mock fails
127
- (Requires a regeneration of the mocks files.)
148
+ (After `rake db:migrate` is called the mocks will be regenerated.)
128
149
 
129
150
  #db/schema.rb
130
151
 
@@ -142,34 +163,38 @@ Running this rake task builds/rebuilds the mocks. It will be ran automatically a
142
163
 
143
164
  --------------
144
165
 
145
- #person_spec.rb
146
-
147
- PersonMock.new(first_name: "Dustin", last_name: "Zeisler")
148
- =>#<RuntimeError Rejected params: {"first_name"=>"Dustin", "last_name"=>"Zeisler"} for PersonMock>
166
+ Person.new(first_name: "Dustin", last_name: "Zeisler")
167
+ =>#<UnknownAttributeError unknown attribute: first_name >
149
168
 
150
169
  ## Mocking Methods
151
170
 
152
171
 
153
172
  ### Class Methods
154
173
 
155
- PersonMock.bar('baz')
174
+ Person.bar('baz')
156
175
  => RuntimeError: ::bar is not Implemented for Class: PersonMock
157
176
 
158
177
  # Rspec 3 Mocks
159
- allow(PersonMock).to receive(:bar) do |name, type=nil|
160
- "Now implemented with #{name} and #{type}"
178
+
179
+ RSpec.configure do |config|
180
+ config.mock_framework = :rspec
181
+ config.mock_with :rspec do |mocks|
182
+ mocks.verify_doubled_constant_names = true
183
+ mocks.verify_partial_doubles = true
184
+ end
161
185
  end
162
186
 
187
+ allow(Person).to receive(:bar) do |name, type=nil|
188
+ "Now implemented with #{name} and #{type}"
189
+ end
163
190
 
164
- ### Instance Methods
165
-
166
- PersonMock.new.bar('foo', 'type')
167
- => "Now implemented with foo and type"
168
-
169
- # Rspec 3 Mocks
170
- allow_any_instance_of(PersonMock).to receive(:bar) do
171
- "Now implemented"
172
- end
191
+ Person.bar('foo', 'type')
192
+ => "Now implemented with foo and type"
193
+
194
+ # Rspec 3 mock class method
195
+ allow_any_instance_of(Person).to receive(:bar) do
196
+ "Now implemented"
197
+ end
173
198
 
174
199
 
175
200
  #### When the model changes, the mock fails
@@ -188,9 +213,7 @@ Running this rake task builds/rebuilds the mocks. It will be ran automatically a
188
213
 
189
214
  --------------
190
215
 
191
- #person_spec.rb
192
-
193
- person_mock.new.bar('foo', 'type')
216
+ Person.new.bar('foo', 'type')
194
217
  => ArgumentError: wrong number of arguments (2 for 1)
195
218
 
196
219
  ----------------
@@ -208,69 +231,36 @@ Running this rake task builds/rebuilds the mocks. It will be ran automatically a
208
231
 
209
232
  --------------
210
233
 
211
- #person_spec.rb
212
-
213
234
  # Rspec 3 Mocks
214
- allow(person_mock).to receive(:bar) do |name, type=nil|
235
+ allow(person).to receive(:bar) do |name, type=nil|
215
236
  "Now implemented with #{name} and #{type}"
216
237
  end
217
238
  => NoMethodError: undefined method `bar' for class `PersonMock'
239
+
240
+ ### Constants and Modules are Available.
218
241
 
219
- ### Managing Mocks
242
+ * Any locally defined modules will not be included or extended.
220
243
 
221
- Rspec Tag - active_mocker:true
244
+ ---------------
222
245
 
223
- describe 'Example', active_mocker:true do
224
-
246
+ class Person < ActiveRecord::Base
247
+ CONSTANT_VALUE = 13
225
248
  end
226
-
227
- Assigning this tag will stub any ActiveRecord model Constants for Mock classes in any `it's` or `before(:each)`. To stub any Constants in `before(:all)`, `after(:all)` use `mock_class('ClassName')`.
228
-
229
- Deletes All Records and Clears Mocked Methods
230
-
231
- PersonMock.clear_mock
232
-
233
- Clears all Loaded Mocks - (Use in after(:all) to keep state from leaking to other tests.)
234
-
235
- ActiveMocker::LoadedMocks.clear_all
236
-
237
- Deletes All Records for Loaded Mocks - (Useful in after(:each) to clean up state between examples)
238
-
239
- ActiveMocker::LoadedMocks.delete_all
240
-
241
- List All Loaded Mocks
242
-
243
- ActiveMocker::LoadedMocks.all
244
- => { 'PersonMock' => PersonMock }
245
-
246
- Map The Mock Class to it's Model
247
-
248
- ActiveMocker::LoadedMocks.class_name_to_mock
249
- => { 'Person' => PersonMock }
250
-
251
-
252
-
253
- ### Constants and included and extended Modules are Available.
254
-
255
- #app/models/person.rb
256
-
257
- class User < ActiveRecord::Base
258
- CONSTANT_VALUE = 13
259
- end
260
249
 
261
250
  -----------------------
262
251
 
263
- #user_spec.rb
252
+ PersonMock::CONSTANT_VALUE
253
+ => 13
264
254
 
265
- require 'spec/mocks/user_mock.rb'
266
255
 
267
- UserMock::CONSTANT_VALUE
268
- => 13
256
+ ### Scoped Methods
257
+ * As long the mock file that holds the scope method is required it will be available but implemented.
269
258
 
270
- ### Mocked Class
271
-
272
- UserMock.mocked_class
273
- => 'User'
259
+ ### Managing Mocks
260
+
261
+ Deletes All Records for Loaded Mocks - (Useful in after(:each) to clean up state between examples)
262
+
263
+ ActiveMocker::LoadedMocks.delete_all
274
264
 
275
265
  ### ActiveRecord supported methods
276
266
  **class methods**
@@ -278,6 +268,10 @@ Map The Mock Class to it's Model
278
268
  * new
279
269
  * create/create!
280
270
  * column_names/attribute_names
271
+ * delete_all/destroy_all
272
+
273
+ **Query Methods**
274
+ * all
281
275
  * find
282
276
  * find_by/find_by!
283
277
  * find_or_create_by
@@ -289,10 +283,28 @@ Map The Mock Class to it's Model
289
283
  * delete_all(conditions_hash)
290
284
  * destroy(id)/delete(id)
291
285
  * update_all
292
- * all
286
+ * update(id, attributes)
293
287
  * count
288
+ * uniq
294
289
  * first/last
290
+ * average(:field_name)
291
+ * minimum(:field_name)
292
+ * maximum(:field_name)
293
+ * sum(:field_name)
294
+ * order(:field_name)
295
+ * reverse_order
295
296
  * limit
297
+
298
+ **Relation Methods**
299
+ * concat
300
+ * include
301
+ * push
302
+ * clear
303
+ * take
304
+ * empty?
305
+ * replace
306
+ * any?
307
+ * many?
296
308
 
297
309
  **instance methods**
298
310
 
@@ -301,50 +313,29 @@ Map The Mock Class to it's Model
301
313
  * save/save!
302
314
  * write_attribute/read_attribute - (protected, can be used within modules)
303
315
  * delete
316
+ * new_record?
317
+ * persisted?
318
+ * reload
304
319
 
305
- **has_one/belongs_to**
320
+ **has_one/belongs_to/has_many**
306
321
 
307
322
  * build_< association >
308
323
  * create_< association >
309
324
  * create_< association >!
310
-
311
- **has_many associations/Collections**
312
-
313
- * empty?
314
- * length/size/count
315
- * uniq
316
- * replace
317
- * first/last
318
- * concat
319
- * include
320
- * push
321
- * clear
322
- * take
323
- * average(:field_name)
324
- * minimum(:field_name)
325
- * maximum(:field_name)
326
- * sum(:field_name)
327
- * find
328
- * find_by/find_by!
329
- * where(conditions_hash)
330
- * where(key: array_of_values)
331
- * where.not(conditions_hash)
332
- * update_all
333
- * delete_all
334
- * order(:field_name)
335
- * reverse_order
336
- * limit
337
325
  * < association >.create
338
326
  * < association >.build
327
+
339
328
 
340
329
  ### Schema/Migration Option Support
341
330
  * All schema types are supported and coerced by [Virtus](https://github.com/solnic/virtus). If coercion fails the passed value will be retained.
342
- * Default value
343
- * Scale and Precision not supported.
331
+ * Default value is supported.
332
+ * Scale and Precision are not supported.
344
333
 
345
334
  ### Known Limitations
346
335
  * Model names and table names must follow the default ActiveRecord naming pattern.
347
- * Whatever associations are setup in one mock object will not reflected in any other objects.
336
+ * Whatever associations are setup in one mock object will not be reflected in any other objects.
337
+ * There partial support for this feature coming in v1.6 when `ActiveMocker::Mock.config.experimental = true` is set.
338
+
348
339
  * Validation are not present in mocks.
349
340
  * Sql queries, joins, etc will never be supported.
350
341
 
@@ -12,7 +12,7 @@ require 'active_mocker/logger'
12
12
  require 'active_support/all'
13
13
  require 'active_mocker/table'
14
14
  require 'active_mocker/field'
15
- require 'file_reader'
15
+ require 'active_mocker/file_reader'
16
16
  require 'active_mocker/reparameterize'
17
17
  require 'active_mocker/active_record'
18
18
  require 'active_mocker/model_reader'
@@ -5,12 +5,12 @@ require 'active_mocker/active_record/unknown_class_method'
5
5
  require 'active_mocker/active_record/unknown_module'
6
6
 
7
7
  module ActiveMocker
8
+ # @api private
8
9
  module ActiveRecord
9
- class Base #< ::ActiveRecord::Base
10
+ class Base
10
11
  extend Scope
11
12
  extend Relationships
12
13
  extend UnknownClassMethod
13
- # extend UnknownModule
14
14
 
15
15
  def self.table_name=(table_name)
16
16
  @table_name = table_name
@@ -1,101 +1,111 @@
1
1
  require 'ostruct'
2
2
  require 'active_support/core_ext/string'
3
- module Relationships
4
-
5
- def self.included(base)
6
- base.extend(ClassMethods)
7
- end
8
-
9
- module ClassMethods
10
- attr_reader :has_and_belongs_to_many, :belongs_to, :has_one, :has_many
11
- end
12
-
13
- def relationships
14
- OpenStruct.new({has_many: @has_many ||= [],
15
- has_one: @has_one ||= [],
16
- belongs_to: @belongs_to ||= [],
17
- has_and_belongs_to_many: @has_and_belongs_to_many ||= []})
18
- end
19
-
20
- def single_relationships
21
- belongs_to + has_one
22
- end
23
-
24
- def collections
25
- has_and_belongs_to_many + has_many
26
- end
27
-
28
- class Relationship
29
- attr_reader :name
30
- def initialize(name, options={})
31
- @name = name
32
- @options = options.reduce(HashWithIndifferentAccess.new, :merge)
33
- end
3
+ module ActiveMocker
34
4
 
35
- def options
36
- @options.symbolize_keys
37
- end
5
+ module ActiveRecord
38
6
 
39
- def through
40
- options[:through]
41
- end
7
+ module Relationships
42
8
 
43
- def class_name
44
- options[:class_name] || name.to_s.camelize.singularize
45
- end
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+ end
46
12
 
47
- def foreign_key
48
- options[:foreign_key] || name.to_s.foreign_key
49
- end
13
+ module ClassMethods
14
+ attr_reader :has_and_belongs_to_many, :belongs_to, :has_one, :has_many
15
+ end
50
16
 
51
- def join_table
52
- options[:join_table]
53
- end
54
- end
17
+ def relationships
18
+ OpenStruct.new({has_many: @has_many ||= [],
19
+ has_one: @has_one ||= [],
20
+ belongs_to: @belongs_to ||= [],
21
+ has_and_belongs_to_many: @has_and_belongs_to_many ||= []})
22
+ end
55
23
 
56
- class HasMany < Relationship
24
+ def single_relationships
25
+ belongs_to + has_one
26
+ end
57
27
 
58
- attr_reader :klass_name
28
+ def collections
29
+ has_and_belongs_to_many + has_many
30
+ end
59
31
 
60
- def initialize(name, klass_name, options={})
61
- @klass_name = klass_name
62
- super(name, options)
63
- end
32
+ class Relationship
33
+ attr_reader :name
64
34
 
65
- def foreign_key
66
- options[:foreign_key] || klass_name.to_s.foreign_key
67
- end
35
+ def initialize(name, options={})
36
+ @name = name
37
+ @options = options.reduce(HashWithIndifferentAccess.new, :merge)
38
+ end
39
+
40
+ def options
41
+ @options.symbolize_keys
42
+ end
43
+
44
+ def through
45
+ options[:through]
46
+ end
47
+
48
+ def class_name
49
+ options[:class_name] || name.to_s.camelize.singularize
50
+ end
68
51
 
69
- end
52
+ def foreign_key
53
+ options[:foreign_key] || name.to_s.foreign_key
54
+ end
70
55
 
71
- def has_many(*args)
72
- @has_many ||= []
73
- @has_many.push HasMany.new(args.shift, self.name, args)
74
- end
56
+ def join_table
57
+ options[:join_table]
58
+ end
59
+ end
75
60
 
76
- class HasOne < Relationship
77
- end
61
+ class HasMany < Relationship
78
62
 
79
- def has_one(*args)
80
- @has_one ||= []
81
- @has_one.push HasOne.new(args.shift, args)
82
- end
63
+ attr_reader :klass_name
83
64
 
84
- class BelongsTo < Relationship
85
- end
65
+ def initialize(name, klass_name, options={})
66
+ @klass_name = klass_name
67
+ super(name, options)
68
+ end
86
69
 
87
- def belongs_to(*args)
88
- @belongs_to ||= []
89
- @belongs_to.push BelongsTo.new(args.shift, args)
90
- end
70
+ def foreign_key
71
+ options[:foreign_key] || klass_name.to_s.foreign_key
72
+ end
91
73
 
74
+ end
92
75
 
93
- class HasAndBelongsToMany < Relationship
94
- end
76
+ def has_many(*args)
77
+ @has_many ||= []
78
+ @has_many.push HasMany.new(args.shift, self.name, args)
79
+ end
80
+
81
+ class HasOne < Relationship
82
+ end
83
+
84
+ def has_one(*args)
85
+ @has_one ||= []
86
+ @has_one.push HasOne.new(args.shift, args)
87
+ end
88
+
89
+ class BelongsTo < Relationship
90
+ end
91
+
92
+ def belongs_to(*args)
93
+ @belongs_to ||= []
94
+ @belongs_to.push BelongsTo.new(args.shift, args)
95
+ end
96
+
97
+
98
+ class HasAndBelongsToMany < Relationship
99
+ end
100
+
101
+ def has_and_belongs_to_many(*args)
102
+ @has_and_belongs_to_many ||= []
103
+ @has_and_belongs_to_many.push HasAndBelongsToMany.new(args.shift, args)
104
+ end
105
+
106
+
107
+ end
95
108
 
96
- def has_and_belongs_to_many(*args)
97
- @has_and_belongs_to_many ||= []
98
- @has_and_belongs_to_many.push HasAndBelongsToMany.new(args.shift, args)
99
- end
109
+ end
100
110
 
101
111
  end