active_mocker 1.3.2 → 1.4.1
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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/README.md +136 -24
- data/Rakefile +8 -2
- data/active_mocker.gemspec +3 -3
- data/lib/active_mock/association.rb +7 -0
- data/lib/active_mock/base.rb +250 -0
- data/lib/active_mock/collection.rb +52 -0
- data/lib/active_mock/creators.rb +25 -0
- data/lib/active_mock/do_nothing_active_record_methods.rb +51 -0
- data/lib/active_mock/has_and_belongs_to_many.rb +7 -0
- data/lib/active_mock/has_many.rb +54 -0
- data/lib/active_mock/next_id.rb +16 -0
- data/lib/active_mock/object_inspect.rb +39 -0
- data/lib/{active_mocker/collection → active_mock}/queries.rb +26 -19
- data/lib/active_mock/records.rb +81 -0
- data/lib/active_mock/relation.rb +8 -0
- data/lib/active_mocker.rb +3 -1
- data/lib/active_mocker/active_mock.rb +26 -0
- data/lib/active_mocker/active_record.rb +18 -0
- data/lib/active_mocker/active_record/relationships.rb +57 -6
- data/lib/active_mocker/active_record/schema.rb +1 -1
- data/lib/active_mocker/active_record/scope.rb +1 -1
- data/lib/active_mocker/active_record/unknown_class_method.rb +1 -1
- data/lib/active_mocker/active_record/unknown_module.rb +10 -9
- data/lib/active_mocker/db_to_ruby_type.rb +26 -0
- data/lib/active_mocker/field.rb +16 -8
- data/lib/active_mocker/generate.rb +19 -174
- data/lib/active_mocker/loaded_mocks.rb +48 -8
- data/lib/active_mocker/logger.rb +2 -0
- data/lib/active_mocker/mock_template.erb +123 -53
- data/lib/active_mocker/model_reader.rb +42 -13
- data/lib/active_mocker/model_schema.rb +279 -0
- data/lib/active_mocker/model_schema/generate.rb +175 -0
- data/lib/active_mocker/reparameterize.rb +23 -3
- data/lib/active_mocker/table.rb +2 -2
- data/lib/active_mocker/version.rb +1 -1
- data/sample_app_rails_4/Gemfile +1 -1
- data/sample_app_rails_4/app/models/micropost.rb +13 -1
- data/sample_app_rails_4/db/schema.rb +1 -1
- data/sample_app_rails_4/spec/compare_mocker_and_record_spec.rb +194 -7
- data/sample_app_rails_4/spec/micropost_mock_spec.rb +145 -0
- data/sample_app_rails_4/spec/mocks/micropost_mock.rb +81 -55
- data/sample_app_rails_4/spec/mocks/relationship_mock.rb +85 -54
- data/sample_app_rails_4/spec/mocks/user_mock.rb +71 -72
- data/sample_app_rails_4/spec/reload_spec.rb +1 -1
- data/sample_app_rails_4/spec/user_mock_spec.rb +25 -7
- data/spec/lib/acitve_mock/queriable_spec.rb +207 -0
- data/spec/lib/active_mocker/db_to_ruby_type_spec.rb +124 -0
- data/spec/lib/active_mocker/loaded_mocks_spec.rb +167 -0
- data/spec/lib/active_mocker/logger_spec.rb +32 -0
- data/spec/lib/active_mocker/model_reader_spec.rb +79 -28
- data/spec/lib/active_mocker/model_schema/generate_spec.rb +111 -0
- data/spec/lib/active_mocker/model_schema_spec.rb +145 -0
- data/spec/lib/model.rb +2 -1
- data/spec/lib/reparameterize_spec.rb +202 -0
- data/spec/unit_logger.rb +2 -2
- metadata +55 -35
- data/lib/active_hash/ar_api.rb +0 -77
- data/lib/active_hash/init.rb +0 -32
- data/lib/active_mocker/collection/association.rb +0 -12
- data/lib/active_mocker/collection/base.rb +0 -65
- data/lib/active_mocker/collection/relation.rb +0 -11
- data/lib/active_mocker/mock_class_methods.rb +0 -92
- data/lib/active_mocker/mock_instance_methods.rb +0 -84
- data/lib/active_mocker/mock_requires.rb +0 -10
- data/spec/lib/active_mocker/collection.rb +0 -94
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6dee4f02b727c2c89ef5d4c74de74b6fa308b1cf
|
4
|
+
data.tar.gz: 4943e1fc8b417dfd2afbd4239a2d50bed8cca98f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a844dd82570b6c67170723113f883c0270c273bf89467b349c99141fe28b00be4bbf8093228b4dbd72f10534e400a0960bef18430a0158dfeb3e6cc8bbc9b52
|
7
|
+
data.tar.gz: 44e29ca9c8da2cb8db4901bd92993ca1ab0ecd45196a29d1e79cfec05688e3c99595601cb5b41a4abab9dfa91d08d5cff0af8141710d120e604111fb486d051a
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,13 +1,32 @@
|
|
1
1
|
# ActiveMocker
|
2
2
|
[](https://travis-ci.org/zeisler/active_mocker)
|
3
|
+
[](https://codeclimate.com/github/zeisler/active_mocker)
|
3
4
|
|
4
|
-
|
5
|
+
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.
|
5
6
|
|
6
7
|
Example from a real app
|
7
8
|
|
8
9
|
Finished in 0.54599 seconds
|
9
10
|
190 examples, 0 failures
|
10
11
|
|
12
|
+
|
13
|
+
------------------------------------------
|
14
|
+
|
15
|
+
* [Installation](#installation)
|
16
|
+
* [Setup](#setup)
|
17
|
+
* [Dependencies](#dependencies)
|
18
|
+
* [Usage](#usage)
|
19
|
+
* [Mocking Methods](#mocking-methods)
|
20
|
+
* [Managing Mocks](#managing-mocks)
|
21
|
+
* [ActiveRecord supported methods](#activerecord-supported-methods)
|
22
|
+
* [Known Limitations](#known-limitations)
|
23
|
+
* [Inspiration](#inspiration)
|
24
|
+
* [Contributing](#contributing)
|
25
|
+
|
26
|
+
|
27
|
+
------------------------------------------
|
28
|
+
|
29
|
+
|
11
30
|
## Installation
|
12
31
|
|
13
32
|
Add this line to your application's Gemfile:
|
@@ -22,8 +41,14 @@ Or install it yourself as:
|
|
22
41
|
|
23
42
|
$ gem install active_mocker
|
24
43
|
|
44
|
+
## Dependencies
|
45
|
+
* Tested with Rails 4.1 may work with older versions but not supported.
|
46
|
+
* Requires Ruby MRI =< 2.1.
|
25
47
|
|
26
|
-
|
48
|
+
|
49
|
+
## Setup
|
50
|
+
|
51
|
+
### Configure the Mock Generator
|
27
52
|
config/initializers/active_mocker.rb
|
28
53
|
|
29
54
|
ActiveMocker::Generate.configure do |config|
|
@@ -35,6 +60,8 @@ Or install it yourself as:
|
|
35
60
|
config.logger = Rails.logger
|
36
61
|
end
|
37
62
|
|
63
|
+
### Create a Rake Task to Auto Regenerate Mocks
|
64
|
+
|
38
65
|
Here is an example of a rake task to regenerate mocks after every schema modifiation. 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.
|
39
66
|
|
40
67
|
lib/tasks/active_mocker.rake
|
@@ -52,7 +79,7 @@ Here is an example of a rake task to regenerate mocks after every schema modifia
|
|
52
79
|
|
53
80
|
## Usage
|
54
81
|
|
55
|
-
|
82
|
+
#db/schema.rb
|
56
83
|
|
57
84
|
ActiveRecord::Schema.define(version: 20140327205359) do
|
58
85
|
|
@@ -65,6 +92,7 @@ Here is an example of a rake task to regenerate mocks after every schema modifia
|
|
65
92
|
end
|
66
93
|
|
67
94
|
end
|
95
|
+
--------------
|
68
96
|
|
69
97
|
#app/models/person.rb
|
70
98
|
|
@@ -79,24 +107,29 @@ Here is an example of a rake task to regenerate mocks after every schema modifia
|
|
79
107
|
end
|
80
108
|
|
81
109
|
end
|
82
|
-
|
110
|
+
|
111
|
+
-----------------
|
83
112
|
|
84
|
-
|
113
|
+
#person_spec.rb
|
85
114
|
|
86
|
-
|
115
|
+
require 'spec/mocks/person_mock.rb'
|
116
|
+
require 'spec/mocks/account_mock.rb'
|
87
117
|
|
88
118
|
PersonMock.column_names
|
89
119
|
=> ["id", "account_id", "first_name", "last_name", "address", "city"]
|
90
120
|
|
91
|
-
person_mock = PersonMock.new(first_name:
|
92
|
-
|
121
|
+
person_mock = PersonMock.new( first_name: "Dustin",
|
122
|
+
last_name: "Zeisler",
|
123
|
+
account: AccountMock.new )
|
124
|
+
=> "#<PersonMock id: nil, account_id: nil, first_name: "Dustin", last_name: "Zeisler, address: nil, city: nil>"
|
93
125
|
|
94
126
|
person_mock.first_name
|
95
127
|
=> "Dustin"
|
96
128
|
|
97
129
|
### When schema.rb changes, the mock fails
|
98
|
-
|
99
|
-
|
130
|
+
(Requires a regeneration of the mocks files.)
|
131
|
+
|
132
|
+
#db/schema.rb
|
100
133
|
|
101
134
|
ActiveRecord::Schema.define(version: 20140327205359) do
|
102
135
|
|
@@ -110,29 +143,44 @@ Here is an example of a rake task to regenerate mocks after every schema modifia
|
|
110
143
|
|
111
144
|
end
|
112
145
|
|
146
|
+
--------------
|
147
|
+
|
148
|
+
#person_spec.rb
|
149
|
+
|
113
150
|
PersonMock.new(first_name: "Dustin", last_name: "Zeisler")
|
114
151
|
=>#<RuntimeError Rejected params: {"first_name"=>"Dustin", "last_name"=>"Zeisler"} for PersonMock>
|
115
152
|
|
153
|
+
## Mocking Methods
|
154
|
+
|
116
155
|
|
117
|
-
###
|
156
|
+
### Class Methods
|
118
157
|
|
119
|
-
|
158
|
+
PersonMock.bar('baz')
|
120
159
|
=> RuntimeError: ::bar is not Implemented for Class: PersonMock
|
121
160
|
|
122
|
-
|
161
|
+
PersonMock.mock_class_method(:bar) do |name, type=nil|
|
123
162
|
"Now implemented with #{name} and #{type}"
|
124
163
|
end
|
164
|
+
|
125
165
|
|
126
|
-
|
166
|
+
### Instance Methods
|
167
|
+
|
168
|
+
PersonMock.new.bar('foo', 'type')
|
127
169
|
=> "Now implemented with foo and type"
|
128
170
|
|
129
|
-
|
130
|
-
|
171
|
+
PersonMock.mock_instance_method(:bar) do
|
172
|
+
"Now implemented"
|
173
|
+
end
|
174
|
+
|
175
|
+
# override mock on an individual instance
|
176
|
+
PersonMock.new.mock_instance_method(:bar) do
|
177
|
+
"Now implemented!!!!"
|
131
178
|
end
|
132
179
|
|
133
|
-
|
180
|
+
#### When the model changes, the mock fails
|
181
|
+
(Requires a regeneration of the mocks files.)
|
134
182
|
|
135
|
-
|
183
|
+
#app/models/person.rb
|
136
184
|
|
137
185
|
class Person < ActiveRecord::Base
|
138
186
|
belongs_to :account
|
@@ -142,12 +190,17 @@ Here is an example of a rake task to regenerate mocks after every schema modifia
|
|
142
190
|
end
|
143
191
|
|
144
192
|
end
|
193
|
+
|
194
|
+
--------------
|
195
|
+
|
196
|
+
#person_spec.rb
|
145
197
|
|
146
198
|
person_mock.new.bar('foo', 'type')
|
147
199
|
=> ArgumentError: wrong number of arguments (2 for 1)
|
148
200
|
|
201
|
+
----------------
|
149
202
|
|
150
|
-
app/models/person.rb
|
203
|
+
#app/models/person.rb
|
151
204
|
|
152
205
|
class Person < ActiveRecord::Base
|
153
206
|
belongs_to :account
|
@@ -157,12 +210,62 @@ Here is an example of a rake task to regenerate mocks after every schema modifia
|
|
157
210
|
end
|
158
211
|
|
159
212
|
end
|
213
|
+
|
214
|
+
--------------
|
215
|
+
|
216
|
+
#person_spec.rb
|
160
217
|
|
161
218
|
person_mock.mock_instance_method(:bar) do |name, type=nil|
|
162
219
|
"Now implemented with #{name} and #{type}"
|
163
220
|
end
|
164
221
|
=> NoMethodError: undefined method `bar' for class `PersonMock'
|
165
222
|
|
223
|
+
### Managing Mocks
|
224
|
+
|
225
|
+
Deletes All Records and Clears Mocked Methods
|
226
|
+
|
227
|
+
PersonMock.clear_mock
|
228
|
+
|
229
|
+
Clears all Loaded Mocks - (Use in after(:all) to keep state from leaking to other tests.)
|
230
|
+
|
231
|
+
ActiveMocker::LoadedMocks.clear_all
|
232
|
+
|
233
|
+
Deletes All Records for Loaded Mocks - (Useful in after(:each) to clean up state between examples)
|
234
|
+
|
235
|
+
ActiveMocker::LoadedMocks.delete_all
|
236
|
+
|
237
|
+
List All Loaded Mocks
|
238
|
+
|
239
|
+
ActiveMocker::LoadedMocks.all
|
240
|
+
=> { 'PersonMock' => PersonMock }
|
241
|
+
|
242
|
+
Map The Mock Class to it's Model
|
243
|
+
|
244
|
+
ActiveMocker::LoadedMocks.class_name_to_mock
|
245
|
+
=> { 'Person' => PersonMock }
|
246
|
+
|
247
|
+
### Constants are Available - (Modules and classes are excluded.)
|
248
|
+
|
249
|
+
#app/models/person.rb
|
250
|
+
|
251
|
+
class User < ActiveRecord::Base
|
252
|
+
CONSTANT_VALUE = 13
|
253
|
+
end
|
254
|
+
|
255
|
+
-----------------------
|
256
|
+
|
257
|
+
#user_spec.rb
|
258
|
+
|
259
|
+
require 'spec/mocks/user_mock.rb'
|
260
|
+
|
261
|
+
UserMock::CONSTANT_VALUE
|
262
|
+
=> 13
|
263
|
+
|
264
|
+
### Mocked Class
|
265
|
+
|
266
|
+
UserMock.mocked_class
|
267
|
+
=> 'User'
|
268
|
+
|
166
269
|
### ActiveRecord supported methods
|
167
270
|
**class methods**
|
168
271
|
|
@@ -190,9 +293,15 @@ Here is an example of a rake task to regenerate mocks after every schema modifia
|
|
190
293
|
* attributes
|
191
294
|
* update
|
192
295
|
* save/save!
|
193
|
-
* write_attribute/read_attribute - (
|
296
|
+
* write_attribute/read_attribute - (protected, can be used within modules)
|
194
297
|
* delete
|
195
298
|
|
299
|
+
**has_one/belongs_to**
|
300
|
+
|
301
|
+
* build_< association >
|
302
|
+
* create_< association >
|
303
|
+
* create_< association >!
|
304
|
+
|
196
305
|
**has_many associations/Collections**
|
197
306
|
|
198
307
|
* empty?
|
@@ -219,22 +328,25 @@ Here is an example of a rake task to regenerate mocks after every schema modifia
|
|
219
328
|
* order(:field_name)
|
220
329
|
* reverse_order
|
221
330
|
* limit
|
331
|
+
* < association >.create
|
332
|
+
* < association >.build
|
222
333
|
|
223
334
|
### Schema/Migration Option Support
|
224
|
-
* All schema types are supported and
|
335
|
+
* All schema types are supported and coerced by [Virtus](https://github.com/solnic/virtus). If coercion fails the passed value will be retained.
|
225
336
|
* Default value
|
337
|
+
* Scale and Precision not supported.
|
226
338
|
|
227
339
|
### Known Limitations
|
228
340
|
* Model names and table names must follow the default ActiveRecord naming pattern.
|
229
|
-
* Included/extended module methods will not be included on the mock. I suggest you keep domain logic out of the model and only add database queries. Domain logic can be put into modules and then included into the mock during test setup.
|
230
|
-
*
|
231
|
-
* Creation of association like `User.create_friend` or `User.build_friend` are not supported. If you need this functionality use rspec's stub any instance.
|
341
|
+
* Included/extended module methods will not be included on the mock. I suggest you keep domain logic out of the model and only add database queries. Domain logic can be put into modules and then included into the mock during test setup.
|
342
|
+
* Whatever associations are setup in one mock object will not effect any other objects.
|
232
343
|
* Validation are not present in mocks.
|
233
344
|
|
234
345
|
## Inspiration
|
235
346
|
Thanks to Jeff Olfert for being my original inspiration for this project.
|
236
347
|
|
237
348
|
## Contributing
|
349
|
+
Your contribution are welcome!
|
238
350
|
|
239
351
|
1. Fork it ( http://github.com/zeisler/active_mocker/fork )
|
240
352
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
data/Rakefile
CHANGED
@@ -15,6 +15,12 @@ task :specs do
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
|
-
raise "Tests Failed" unless system
|
19
|
-
raise "Tests Failed" unless system
|
18
|
+
raise "Tests Failed" unless system "rspec --seed #{random_seed}"
|
19
|
+
raise "Tests Failed" unless system "cd sample_app_rails_4 && rspec --seed #{random_seed}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def random_seed
|
23
|
+
seed = rand(99999)
|
24
|
+
puts "Seed: #{seed}"
|
25
|
+
seed
|
20
26
|
end
|
data/active_mocker.gemspec
CHANGED
@@ -21,16 +21,16 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.required_ruby_version = '>= 2.0.0'
|
22
22
|
|
23
23
|
spec.add_runtime_dependency "activesupport", "~>4.1"
|
24
|
-
spec.add_runtime_dependency "active_hash", "~>1.3"
|
25
24
|
spec.add_runtime_dependency "virtus", '~> 1.0'
|
26
25
|
|
27
26
|
spec.add_development_dependency "bundler", "~> 1.5"
|
28
27
|
spec.add_development_dependency "rake", "~>10.1"
|
29
|
-
spec.add_development_dependency "rspec", "
|
28
|
+
spec.add_development_dependency "rspec", "2.14.1"
|
30
29
|
spec.add_development_dependency "sqlite3", "~>1.3"
|
31
30
|
spec.add_development_dependency "rails", "~>4.1"
|
32
|
-
spec.add_development_dependency "rspec-rails", "
|
31
|
+
spec.add_development_dependency "rspec-rails", "2.14.1"
|
33
32
|
spec.add_development_dependency "fuubar", "~>1.3"
|
33
|
+
spec.add_development_dependency "rspec-given", "~>3.5"
|
34
34
|
|
35
35
|
if ENV['DEBUG'] == '1'
|
36
36
|
spec.add_development_dependency "debase", "~>0.0"
|
@@ -0,0 +1,250 @@
|
|
1
|
+
module ActiveMock
|
2
|
+
|
3
|
+
class RecordNotFound < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class ReservedFieldError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
class IdError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class FileTypeMismatchError < StandardError
|
13
|
+
end
|
14
|
+
|
15
|
+
class RejectedParams < Exception
|
16
|
+
end
|
17
|
+
|
18
|
+
class Base
|
19
|
+
|
20
|
+
include DoNothingActiveRecordMethods
|
21
|
+
extend ActiveMock::Queries
|
22
|
+
extend ActiveMock::Creators
|
23
|
+
|
24
|
+
def self.inherited(subclass)
|
25
|
+
return ActiveMocker::LoadedMocks.add(subclass) if subclass.superclass == Base
|
26
|
+
ActiveMocker::LoadedMocks.add_subclass(subclass)
|
27
|
+
end
|
28
|
+
|
29
|
+
class << self
|
30
|
+
|
31
|
+
def records
|
32
|
+
@records ||= Records.new
|
33
|
+
end
|
34
|
+
|
35
|
+
private :records
|
36
|
+
|
37
|
+
delegate :count, :insert, :exists?, :to_a, :to => :records
|
38
|
+
delegate :first, :last, :to => :all
|
39
|
+
|
40
|
+
def delete(id)
|
41
|
+
find(id).delete
|
42
|
+
end
|
43
|
+
|
44
|
+
alias_method :destroy, :delete
|
45
|
+
|
46
|
+
def delete_all(options=nil)
|
47
|
+
return records.reset_all_records if options.nil?
|
48
|
+
where(options).map { |r| r.delete }.count
|
49
|
+
end
|
50
|
+
|
51
|
+
alias_method :destroy_all, :delete_all
|
52
|
+
|
53
|
+
def build_type(type)
|
54
|
+
Virtus::Attribute.build(type)
|
55
|
+
end
|
56
|
+
|
57
|
+
def classes(klass)
|
58
|
+
ActiveMocker::LoadedMocks.find(klass)
|
59
|
+
end
|
60
|
+
|
61
|
+
private :classes
|
62
|
+
|
63
|
+
public
|
64
|
+
|
65
|
+
def clear_mock
|
66
|
+
@model_class_methods, @model_instance_methods = nil, nil
|
67
|
+
delete_all
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def classes(klass)
|
73
|
+
self.class.send(:classes, klass)
|
74
|
+
end
|
75
|
+
|
76
|
+
private :classes
|
77
|
+
|
78
|
+
attr_reader :associations, :types, :attributes
|
79
|
+
|
80
|
+
def initialize(attributes = {}, &block)
|
81
|
+
setup_instance_variables
|
82
|
+
set_properties_block(attributes, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
def setup_instance_variables
|
86
|
+
{:@model_instance_methods => :model_instance_methods,
|
87
|
+
:@model_class_methods => :model_class_methods,
|
88
|
+
:@associations => :associations,
|
89
|
+
:@attributes => :attributes,
|
90
|
+
:@types => :types}.each do |var, value|
|
91
|
+
instance_variable_set(var, self.class.send(value).dup)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private :setup_instance_variables
|
96
|
+
|
97
|
+
def set_properties_block(attributes = {}, &block)
|
98
|
+
yield self if block_given?
|
99
|
+
set_properties(attributes)
|
100
|
+
end
|
101
|
+
private :set_properties_block
|
102
|
+
|
103
|
+
def update(attributes={})
|
104
|
+
set_properties(attributes)
|
105
|
+
end
|
106
|
+
|
107
|
+
def set_properties(attributes={})
|
108
|
+
attributes.each do |key, value|
|
109
|
+
begin
|
110
|
+
send "#{key}=", value
|
111
|
+
rescue NoMethodError
|
112
|
+
raise ActiveMock::RejectedParams, "{:#{key}=>#{value.inspect}} for #{self.class.name}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
private :set_properties
|
118
|
+
|
119
|
+
def save(*args)
|
120
|
+
unless self.class.exists?(self)
|
121
|
+
self.class.send(:insert, self)
|
122
|
+
end
|
123
|
+
true
|
124
|
+
end
|
125
|
+
|
126
|
+
alias save! save
|
127
|
+
|
128
|
+
def records
|
129
|
+
self.class.send(:records)
|
130
|
+
end
|
131
|
+
|
132
|
+
private :records
|
133
|
+
|
134
|
+
def delete
|
135
|
+
records.delete(self)
|
136
|
+
end
|
137
|
+
|
138
|
+
delegate :[], :[]=, to: :attributes
|
139
|
+
|
140
|
+
def new_record?
|
141
|
+
!records.new_record?(self)
|
142
|
+
end
|
143
|
+
|
144
|
+
def persisted?
|
145
|
+
records.persisted?(id)
|
146
|
+
end
|
147
|
+
|
148
|
+
def to_hash
|
149
|
+
attributes
|
150
|
+
end
|
151
|
+
|
152
|
+
def inspect
|
153
|
+
ObjectInspect.new(self.class.name, attributes)
|
154
|
+
end
|
155
|
+
|
156
|
+
def hash
|
157
|
+
attributes.hash
|
158
|
+
end
|
159
|
+
|
160
|
+
def ==(obj)
|
161
|
+
return false if obj.nil?
|
162
|
+
return hash == obj.attributes.hash if obj.respond_to?(:attributes)
|
163
|
+
hash == obj.hash if obj.respond_to?(:hash)
|
164
|
+
end
|
165
|
+
|
166
|
+
module PropertiesGetterAndSetter
|
167
|
+
|
168
|
+
def read_attribute(attr)
|
169
|
+
@attributes[attr]
|
170
|
+
end
|
171
|
+
|
172
|
+
def write_attribute(attr, value)
|
173
|
+
@attributes[attr] = types[attr].coerce(value)
|
174
|
+
end
|
175
|
+
|
176
|
+
def read_association(attr)
|
177
|
+
@associations[attr]
|
178
|
+
end
|
179
|
+
|
180
|
+
def write_association(attr, value)
|
181
|
+
@associations[attr] = value
|
182
|
+
end
|
183
|
+
|
184
|
+
protected :read_attribute, :write_attribute, :read_association, :write_association
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
include PropertiesGetterAndSetter
|
189
|
+
|
190
|
+
module MockAbilities
|
191
|
+
|
192
|
+
def self.included(base)
|
193
|
+
base.extend(ClassMethods)
|
194
|
+
end
|
195
|
+
|
196
|
+
module ClassMethods
|
197
|
+
def mock_instance_method(method, &block)
|
198
|
+
model_instance_methods[method.to_s] = block
|
199
|
+
end
|
200
|
+
|
201
|
+
def mock_class_method(method, &block)
|
202
|
+
model_class_methods[method.to_s] = block
|
203
|
+
end
|
204
|
+
|
205
|
+
def model_class_methods
|
206
|
+
@model_class_methods ||= HashWithIndifferentAccess.new
|
207
|
+
end
|
208
|
+
|
209
|
+
def model_instance_methods
|
210
|
+
@model_instance_methods ||= HashWithIndifferentAccess.new
|
211
|
+
end
|
212
|
+
|
213
|
+
private :model_class_methods, :model_instance_methods
|
214
|
+
|
215
|
+
|
216
|
+
def is_implemented(val, method)
|
217
|
+
raise "#{method} is not Implemented for Class: #{name}" if val == :not_implemented
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
def mock_instance_method(method, &block)
|
223
|
+
@model_instance_methods[method.to_s] = block
|
224
|
+
end
|
225
|
+
|
226
|
+
def model_instance_methods
|
227
|
+
class_level_mocks = self.class.send(:model_instance_methods)
|
228
|
+
merged_mocks = class_level_mocks.merge(@model_instance_methods)
|
229
|
+
return class_level_mocks if all_not_implemented?(merged_mocks) && !all_not_implemented?(class_level_mocks)
|
230
|
+
merged_mocks
|
231
|
+
end
|
232
|
+
|
233
|
+
def all_not_implemented?(hash)
|
234
|
+
hash.select { |k, v| v == :not_implemented }.count == hash.count
|
235
|
+
end
|
236
|
+
|
237
|
+
private :all_not_implemented?
|
238
|
+
|
239
|
+
def model_class_methods
|
240
|
+
self.class.send(:model_class_methods).merge(@model_class_methods)
|
241
|
+
end
|
242
|
+
|
243
|
+
private :model_class_methods, :model_instance_methods
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
include MockAbilities
|
248
|
+
|
249
|
+
end
|
250
|
+
end
|