active_mocker 1.3.2 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/zeisler/active_mocker.png?branch=master)](https://travis-ci.org/zeisler/active_mocker)
|
3
|
+
[![Code Climate](https://codeclimate.com/github/zeisler/active_mocker.png)](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
|