active_mocker 1.5.2 → 1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +111 -120
- data/lib/active_mocker.rb +1 -1
- data/lib/active_mocker/active_record.rb +2 -2
- data/lib/active_mocker/active_record/relationships.rb +89 -79
- data/lib/active_mocker/active_record/scope.rb +16 -6
- data/lib/active_mocker/active_record/unknown_class_method.rb +7 -3
- data/lib/active_mocker/active_record/unknown_module.rb +19 -14
- data/lib/active_mocker/db_to_ruby_type.rb +1 -0
- data/lib/active_mocker/field.rb +1 -1
- data/lib/{file_reader.rb → active_mocker/file_reader.rb} +5 -0
- data/lib/active_mocker/generate.rb +6 -1
- data/lib/active_mocker/loaded_mocks.rb +43 -27
- data/lib/active_mocker/logger.rb +1 -0
- data/lib/active_mocker/mock/base.rb +153 -42
- data/lib/active_mocker/mock/collection.rb +6 -1
- data/lib/active_mocker/mock/do_nothing_active_record_methods.rb +4 -0
- data/lib/active_mocker/mock/exceptions.rb +14 -1
- data/lib/active_mocker/mock/has_many.rb +7 -0
- data/lib/active_mocker/mock/hash_process.rb +1 -0
- data/lib/active_mocker/mock/next_id.rb +1 -0
- data/lib/active_mocker/mock/queries.rb +216 -23
- data/lib/active_mocker/mock/relation.rb +21 -0
- data/lib/active_mocker/mock_template.erb +12 -2
- data/lib/active_mocker/model_reader.rb +1 -1
- data/lib/active_mocker/model_schema.rb +2 -1
- data/lib/active_mocker/public_methods.rb +24 -11
- data/lib/active_mocker/reparameterize.rb +1 -0
- data/lib/active_mocker/rspec_helper.rb +2 -0
- data/lib/active_mocker/schema_reader.rb +1 -0
- data/lib/active_mocker/string_reader.rb +14 -0
- data/lib/active_mocker/table.rb +1 -1
- data/lib/active_mocker/version.rb +1 -1
- metadata +4 -4
- data/lib/string_reader.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35f6abbcfa326377ac5fa015a3e1f9b434a9f936
|
4
|
+
data.tar.gz: 8aad0e3c8158712b9a09790d663854df1f5c96ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50d226edfaa35c359327b9ee3eb888bccd395fbe378d943302e368b04f7ed2885609fe6044ea4dffec02364af7fe46f11ced835d874c0acb8a957e61ee2e6ed7
|
7
|
+
data.tar.gz: ecc81c94d5186bf9a1e9b2e55556a2ec709ad97411df75b0a7806fc12d1491445eb44ba3d9971e7bcf0448837ac78b2ef9cb4d1e451a53cb2aa05850087d514c
|
data/CHANGELOG.md
CHANGED
@@ -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
|
[](https://gemnasium.com/zeisler/active_mocker)
|
6
6
|
[](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.
|
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
|
-
|
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
|
-
|
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
|
-
|
113
|
-
|
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
|
-
|
134
|
+
---------
|
135
|
+
|
136
|
+
Person.column_names
|
116
137
|
=> ["id", "account_id", "first_name", "last_name", "address", "city"]
|
117
138
|
|
118
|
-
|
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
|
-
|
144
|
+
person.first_name
|
124
145
|
=> "Dustin"
|
125
146
|
|
126
147
|
### When schema.rb changes, the mock fails
|
127
|
-
(
|
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
|
-
|
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
|
-
|
174
|
+
Person.bar('baz')
|
156
175
|
=> RuntimeError: ::bar is not Implemented for Class: PersonMock
|
157
176
|
|
158
177
|
# Rspec 3 Mocks
|
159
|
-
|
160
|
-
|
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
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
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(
|
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
|
-
|
242
|
+
* Any locally defined modules will not be included or extended.
|
220
243
|
|
221
|
-
|
244
|
+
---------------
|
222
245
|
|
223
|
-
|
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
|
-
|
252
|
+
PersonMock::CONSTANT_VALUE
|
253
|
+
=> 13
|
264
254
|
|
265
|
-
require 'spec/mocks/user_mock.rb'
|
266
255
|
|
267
|
-
|
268
|
-
|
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
|
-
###
|
271
|
-
|
272
|
-
|
273
|
-
|
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
|
-
*
|
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
|
|
data/lib/active_mocker.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
36
|
-
@options.symbolize_keys
|
37
|
-
end
|
5
|
+
module ActiveRecord
|
38
6
|
|
39
|
-
|
40
|
-
options[:through]
|
41
|
-
end
|
7
|
+
module Relationships
|
42
8
|
|
43
|
-
|
44
|
-
|
45
|
-
|
9
|
+
def self.included(base)
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
end
|
46
12
|
|
47
|
-
|
48
|
-
|
49
|
-
|
13
|
+
module ClassMethods
|
14
|
+
attr_reader :has_and_belongs_to_many, :belongs_to, :has_one, :has_many
|
15
|
+
end
|
50
16
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
24
|
+
def single_relationships
|
25
|
+
belongs_to + has_one
|
26
|
+
end
|
57
27
|
|
58
|
-
|
28
|
+
def collections
|
29
|
+
has_and_belongs_to_many + has_many
|
30
|
+
end
|
59
31
|
|
60
|
-
|
61
|
-
|
62
|
-
super(name, options)
|
63
|
-
end
|
32
|
+
class Relationship
|
33
|
+
attr_reader :name
|
64
34
|
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
52
|
+
def foreign_key
|
53
|
+
options[:foreign_key] || name.to_s.foreign_key
|
54
|
+
end
|
70
55
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
56
|
+
def join_table
|
57
|
+
options[:join_table]
|
58
|
+
end
|
59
|
+
end
|
75
60
|
|
76
|
-
|
77
|
-
end
|
61
|
+
class HasMany < Relationship
|
78
62
|
|
79
|
-
|
80
|
-
@has_one ||= []
|
81
|
-
@has_one.push HasOne.new(args.shift, args)
|
82
|
-
end
|
63
|
+
attr_reader :klass_name
|
83
64
|
|
84
|
-
|
85
|
-
|
65
|
+
def initialize(name, klass_name, options={})
|
66
|
+
@klass_name = klass_name
|
67
|
+
super(name, options)
|
68
|
+
end
|
86
69
|
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
94
|
-
|
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
|
-
|
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
|