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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/README.md +136 -24
  4. data/Rakefile +8 -2
  5. data/active_mocker.gemspec +3 -3
  6. data/lib/active_mock/association.rb +7 -0
  7. data/lib/active_mock/base.rb +250 -0
  8. data/lib/active_mock/collection.rb +52 -0
  9. data/lib/active_mock/creators.rb +25 -0
  10. data/lib/active_mock/do_nothing_active_record_methods.rb +51 -0
  11. data/lib/active_mock/has_and_belongs_to_many.rb +7 -0
  12. data/lib/active_mock/has_many.rb +54 -0
  13. data/lib/active_mock/next_id.rb +16 -0
  14. data/lib/active_mock/object_inspect.rb +39 -0
  15. data/lib/{active_mocker/collection → active_mock}/queries.rb +26 -19
  16. data/lib/active_mock/records.rb +81 -0
  17. data/lib/active_mock/relation.rb +8 -0
  18. data/lib/active_mocker.rb +3 -1
  19. data/lib/active_mocker/active_mock.rb +26 -0
  20. data/lib/active_mocker/active_record.rb +18 -0
  21. data/lib/active_mocker/active_record/relationships.rb +57 -6
  22. data/lib/active_mocker/active_record/schema.rb +1 -1
  23. data/lib/active_mocker/active_record/scope.rb +1 -1
  24. data/lib/active_mocker/active_record/unknown_class_method.rb +1 -1
  25. data/lib/active_mocker/active_record/unknown_module.rb +10 -9
  26. data/lib/active_mocker/db_to_ruby_type.rb +26 -0
  27. data/lib/active_mocker/field.rb +16 -8
  28. data/lib/active_mocker/generate.rb +19 -174
  29. data/lib/active_mocker/loaded_mocks.rb +48 -8
  30. data/lib/active_mocker/logger.rb +2 -0
  31. data/lib/active_mocker/mock_template.erb +123 -53
  32. data/lib/active_mocker/model_reader.rb +42 -13
  33. data/lib/active_mocker/model_schema.rb +279 -0
  34. data/lib/active_mocker/model_schema/generate.rb +175 -0
  35. data/lib/active_mocker/reparameterize.rb +23 -3
  36. data/lib/active_mocker/table.rb +2 -2
  37. data/lib/active_mocker/version.rb +1 -1
  38. data/sample_app_rails_4/Gemfile +1 -1
  39. data/sample_app_rails_4/app/models/micropost.rb +13 -1
  40. data/sample_app_rails_4/db/schema.rb +1 -1
  41. data/sample_app_rails_4/spec/compare_mocker_and_record_spec.rb +194 -7
  42. data/sample_app_rails_4/spec/micropost_mock_spec.rb +145 -0
  43. data/sample_app_rails_4/spec/mocks/micropost_mock.rb +81 -55
  44. data/sample_app_rails_4/spec/mocks/relationship_mock.rb +85 -54
  45. data/sample_app_rails_4/spec/mocks/user_mock.rb +71 -72
  46. data/sample_app_rails_4/spec/reload_spec.rb +1 -1
  47. data/sample_app_rails_4/spec/user_mock_spec.rb +25 -7
  48. data/spec/lib/acitve_mock/queriable_spec.rb +207 -0
  49. data/spec/lib/active_mocker/db_to_ruby_type_spec.rb +124 -0
  50. data/spec/lib/active_mocker/loaded_mocks_spec.rb +167 -0
  51. data/spec/lib/active_mocker/logger_spec.rb +32 -0
  52. data/spec/lib/active_mocker/model_reader_spec.rb +79 -28
  53. data/spec/lib/active_mocker/model_schema/generate_spec.rb +111 -0
  54. data/spec/lib/active_mocker/model_schema_spec.rb +145 -0
  55. data/spec/lib/model.rb +2 -1
  56. data/spec/lib/reparameterize_spec.rb +202 -0
  57. data/spec/unit_logger.rb +2 -2
  58. metadata +55 -35
  59. data/lib/active_hash/ar_api.rb +0 -77
  60. data/lib/active_hash/init.rb +0 -32
  61. data/lib/active_mocker/collection/association.rb +0 -12
  62. data/lib/active_mocker/collection/base.rb +0 -65
  63. data/lib/active_mocker/collection/relation.rb +0 -11
  64. data/lib/active_mocker/mock_class_methods.rb +0 -92
  65. data/lib/active_mocker/mock_instance_methods.rb +0 -84
  66. data/lib/active_mocker/mock_requires.rb +0 -10
  67. data/spec/lib/active_mocker/collection.rb +0 -94
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 21a518671785c535883e4fa2f3fe1aeb95538c07
4
- data.tar.gz: b3fef1e6496ce8e5fcbe686d8c75441481a378ea
3
+ metadata.gz: 6dee4f02b727c2c89ef5d4c74de74b6fa308b1cf
4
+ data.tar.gz: 4943e1fc8b417dfd2afbd4239a2d50bed8cca98f
5
5
  SHA512:
6
- metadata.gz: e4d11855ab91ea06085f8eb42b4af4ff9d1926b1ca1e732846200cc610ba9dd6bab82fcf1939cedd9b268e00e1737363a881b6d1a12c8570f2d3bfa7132713c4
7
- data.tar.gz: 23e84bfeaaaf244364ab54e1219a2c2d5483e847739a7a613a9c233004ff259532ab8240f2cccc65e1551a3c4ff509bed18c08e566ea17590bbab1a7edacf0e4
6
+ metadata.gz: 8a844dd82570b6c67170723113f883c0270c273bf89467b349c99141fe28b00be4bbf8093228b4dbd72f10534e400a0960bef18430a0158dfeb3e6cc8bbc9b52
7
+ data.tar.gz: 44e29ca9c8da2cb8db4901bd92993ca1ab0ecd45196a29d1e79cfec05688e3c99595601cb5b41a4abab9dfa91d08d5cff0af8141710d120e604111fb486d051a
data/.travis.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "2.0.0"
4
3
  - "2.1.0"
5
4
  - "2.1.1"
5
+ - "2.1.2"
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
- Creates mocks from Active Record models. Allows your test suite to run very fast by not loading Rails or hooking to a database. It parse the schema definition and the defined methods on a model then saves a ruby file that can be included with 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
+ 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
- ### Setup
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
- db/schema.rb
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
- spec/models/person_spec.rb
113
+ #person_spec.rb
85
114
 
86
- load 'spec/mocks/person_mock.rb'
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: "Dustin", last_name: "Zeisler", account: ActiveMocker.mock('Account').new)
92
- => "#<PersonMock id: nil, account_id: nil, first_name: \"Dustin\", last_name: \"Zeisler\", address: nil, city: nil>"
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
- db/schema.rb
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
- ### Mocking instance and class methods
156
+ ### Class Methods
118
157
 
119
- person_mock.bar('baz')
158
+ PersonMock.bar('baz')
120
159
  => RuntimeError: ::bar is not Implemented for Class: PersonMock
121
160
 
122
- person_mock.mock_instance_method(:bar) do |name, type=nil|
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
- person_mock.new.bar('foo', 'type')
166
+ ### Instance Methods
167
+
168
+ PersonMock.new.bar('foo', 'type')
127
169
  => "Now implemented with foo and type"
128
170
 
129
- person_mock.mock_class_method(:bar) do
130
- "Now implemented"
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
- ### When the model changes, the mock fails
180
+ #### When the model changes, the mock fails
181
+ (Requires a regeneration of the mocks files.)
134
182
 
135
- app/models/person.rb
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 - (private, can be used within an included module)
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 on initalization coerced by Virtus. If coercsion fails the passed value will be retained.
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
- * Queries will not call other mocks classes, for example when using `where` all attributes must reside inside of each record.
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 'rspec'
19
- raise "Tests Failed" unless system 'cd sample_app_rails_4 && rspec'
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
@@ -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", "~>2.14"
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", "~>2"
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,7 @@
1
+ module ActiveMock
2
+ class Association < Collection
3
+
4
+ include Queries
5
+
6
+ end
7
+ end
@@ -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