memory_model 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +5 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +31 -0
  8. data/Rakefile +1 -0
  9. data/lib/concerned_inheritance/class_methods.rb +28 -0
  10. data/lib/concerned_inheritance/delegator.rb +21 -0
  11. data/lib/concerned_inheritance/module_methods.rb +11 -0
  12. data/lib/concerned_inheritance.rb +27 -0
  13. data/lib/memory_model/base/actionable.rb +95 -0
  14. data/lib/memory_model/base/attributable.rb +76 -0
  15. data/lib/memory_model/base/collectable.rb +22 -0
  16. data/lib/memory_model/base/comparable.rb +16 -0
  17. data/lib/memory_model/base/fieldable/field.rb +35 -0
  18. data/lib/memory_model/base/fieldable/field_set.rb +74 -0
  19. data/lib/memory_model/base/fieldable.rb +45 -0
  20. data/lib/memory_model/base/persistence.rb +15 -0
  21. data/lib/memory_model/base/versionable.rb +17 -0
  22. data/lib/memory_model/base.rb +51 -0
  23. data/lib/memory_model/collection.rb +80 -0
  24. data/lib/memory_model/core_ext/object.rb +5 -0
  25. data/lib/memory_model/version.rb +3 -0
  26. data/lib/memory_model.rb +13 -0
  27. data/memory_model.gemspec +30 -0
  28. data/spec/concerned_inheritance/class_methods_spec.rb +57 -0
  29. data/spec/concerned_inheritance/delegator_spec.rb +52 -0
  30. data/spec/concerned_inheritance/module_methods_spec.rb +27 -0
  31. data/spec/memory_model/base/actionable_spec.rb +359 -0
  32. data/spec/memory_model/base/attributable_spec.rb +143 -0
  33. data/spec/memory_model/base/collectable_spec.rb +24 -0
  34. data/spec/memory_model/base/comparable_spec.rb +155 -0
  35. data/spec/memory_model/base/fieldable/field_set_spec.rb +160 -0
  36. data/spec/memory_model/base/fieldable/field_spec.rb +96 -0
  37. data/spec/memory_model/base/fieldable_spec.rb +23 -0
  38. data/spec/memory_model/base/persistence_spec.rb +37 -0
  39. data/spec/memory_model/base/versionable_spec.rb +31 -0
  40. data/spec/memory_model/base_spec.rb +52 -0
  41. data/spec/memory_model/collection_spec.rb +216 -0
  42. data/spec/memory_model/concerned_inheritance_spec.rb +24 -0
  43. data/spec/memory_model/core_ext/object_spec.rb +12 -0
  44. data/spec/spec_helper.rb +12 -0
  45. data/spec/support/active_model_lint.rb +16 -0
  46. metadata +253 -0
@@ -0,0 +1,80 @@
1
+ class MemoryModel::Collection
2
+
3
+ class InvalidTypeError < StandardError;
4
+ end
5
+
6
+ class << self
7
+ attr_accessor :all
8
+ end
9
+
10
+ self.all = []
11
+
12
+ def initialize(model=Class.new)
13
+ @model = model
14
+ @records = []
15
+ self.class.all << self
16
+ end
17
+
18
+ def all
19
+ unique.reject(&:deleted?)
20
+ end
21
+
22
+ def deleted
23
+ unique.select(&:deleted?)
24
+ end
25
+
26
+ def find(id, options={ })
27
+ version = options[:version] || 0
28
+ return_deleted = !!options[:deleted]
29
+ record = sorted.select { |r| r.id == id }[version]
30
+ return nil unless record
31
+ if !record.deleted? || (return_deleted && record.deleted?)
32
+ record
33
+ else
34
+ raise MemoryModel::RecordNotFoundError
35
+ end
36
+ end
37
+
38
+ def insert(record)
39
+ raise InvalidTypeError unless record.is_a? @model
40
+ record = record.dup
41
+ record.freeze unless record.frozen?
42
+ @records << record
43
+ self
44
+ end
45
+
46
+ alias :<< :insert
47
+
48
+ def inspect
49
+ self.all.inspect
50
+ end
51
+
52
+ def records(dup = true)
53
+ if dup
54
+ @records.map do |record|
55
+ record.deleted? ? record : record.dup
56
+ end
57
+ else
58
+ @records
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def method_missing(m, *args, &block)
65
+ all.respond_to?(m) ? all.send(m, *args, &block) : super
66
+ end
67
+
68
+ def respond_to_missing?(m, include_private=false)
69
+ all.respond_to?(m, include_private)
70
+ end
71
+
72
+ def sorted(records=self.records)
73
+ records.sort { |b, a| a.timestamp <=> b.timestamp }
74
+ end
75
+
76
+ def unique(records=self.records)
77
+ sorted(records).uniq(&:id)
78
+ end
79
+
80
+ end
@@ -0,0 +1,5 @@
1
+ class Object
2
+ def not_a?(klass)
3
+ !is_a?(klass)
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module MemoryModel
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,13 @@
1
+ require "memory_model/version"
2
+ require "memory_model/core_ext/object"
3
+
4
+ module MemoryModel
5
+ autoload :Collection, 'memory_model/collection'
6
+ autoload :Base, 'memory_model/base'
7
+
8
+ class InvalidCollectionError < StandardError ; end
9
+ class InvalidFieldError < StandardError ; end
10
+ class ReadonlyFieldError < StandardError ; end
11
+ class RecordNotFoundError < StandardError ; end
12
+
13
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'memory_model/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "memory_model"
8
+ gem.version = MemoryModel::VERSION
9
+ gem.authors = ["Jason Waldrip"]
10
+ gem.email = ["jason@waldrip.net"]
11
+ gem.description = %q{An in memory, ORM, Built on top of ActiveModel. Allows for in memory models, great for rspec testing.}
12
+ gem.summary = %q{An in memory, ORM}
13
+ gem.homepage = "http://github.com/jwaldrip/memory_model"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = %w{lib}
19
+
20
+ gem.add_development_dependency 'activesupport'
21
+ gem.add_development_dependency 'activemodel'
22
+ gem.add_development_dependency 'guard-rspec'
23
+ gem.add_development_dependency 'rb-fsevent'
24
+ gem.add_development_dependency 'rspec'
25
+ gem.add_development_dependency 'simplecov'
26
+ gem.add_development_dependency 'terminal-notifier-guard'
27
+ gem.add_development_dependency 'ice_nine'
28
+ gem.add_development_dependency 'test-unit'
29
+
30
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+ require 'concerned_inheritance'
3
+
4
+ describe ConcernedInheritance::ClassMethods do
5
+
6
+ let(:klass) do
7
+ Class.new do
8
+ extend ConcernedInheritance
9
+ end
10
+ end
11
+
12
+ describe '.inherited' do
13
+
14
+ context 'with a subclass' do
15
+ it 'should call run inherited callbacks' do
16
+ mock_subclass = mock
17
+ klass.should_receive(:run_inherited_callbacks).with(mock_subclass)
18
+ klass.send :inherited, mock_subclass
19
+ end
20
+ end
21
+
22
+ context 'with a block' do
23
+ it 'should call define_inherited_callback' do
24
+ block = Proc.new { }
25
+ klass.should_receive(:define_inherited_callback).with(&block)
26
+ klass.send :inherited, &block
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ describe '.inherited_callbacks' do
33
+ it 'should call off to its ancestors' do
34
+ mock_ancestor = mock
35
+ mock_callback = proc { "Bar" }
36
+ mock_ancestor.instance_variable_set :@inherited_callbacks, [mock_callback]
37
+ klass.singleton_class.stub(:ancestors).and_return([mock_ancestor])
38
+ klass.stub(:ancestors).and_return([mock_ancestor])
39
+ klass.inherited_callbacks.size.should > 0
40
+ klass.inherited_callbacks.each do |callback|
41
+ callback.should == mock_callback
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '.run_inherited_callbacks' do
47
+ it 'should initialize an InheritanceDelegator' do
48
+ mock_subclass = mock
49
+ mock_callback = proc { }
50
+ ConcernedInheritance::Delegator.should_receive(:new).with(klass, mock_subclass, mock_callback)
51
+ klass.send :define_inherited_callback, &mock_callback
52
+ klass.send :run_inherited_callbacks, mock_subclass
53
+ end
54
+ end
55
+
56
+ end
57
+
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+ require 'concerned_inheritance'
3
+
4
+ describe ConcernedInheritance::Delegator do
5
+
6
+ let(:baseclass) { Class.new }
7
+ let(:subclass) { Class.new }
8
+
9
+ describe '.new' do
10
+ context 'given a proc with no arity' do
11
+ let(:callback) do
12
+ proc { 'foo' }
13
+ end
14
+ it "should should call a subclass' instance method" do
15
+ expect { ConcernedInheritance::Delegator.new(baseclass, subclass, callback) }.to_not raise_error ArgumentError
16
+ end
17
+
18
+ it 'should instance_eval the callback' do
19
+ callback = proc { foo }
20
+ subclass.should_receive(:foo)
21
+ ConcernedInheritance::Delegator.new(baseclass, subclass, callback)
22
+ end
23
+ end
24
+
25
+ context 'given a proc with arity' do
26
+ let(:callback) do
27
+ proc { |foo| foo }
28
+ end
29
+ it 'should raise an error' do
30
+ expect { ConcernedInheritance::Delegator.new(baseclass, subclass, callback) }.to raise_error ArgumentError
31
+ end
32
+ end
33
+
34
+ context 'when not given a proc' do
35
+ it 'should raise an error' do
36
+ expect { ConcernedInheritance::Delegator.new(baseclass, subclass, Object.new) }.to raise_error ArgumentError
37
+ end
38
+ end
39
+ end
40
+
41
+ describe '#method_missing' do
42
+ let(:callback) { proc { } }
43
+ it 'should call a method on the subclass' do
44
+ block = proc { }
45
+ subclass.should_receive(:foo).with('bar', 'baz', &block)
46
+ delegator = ConcernedInheritance::Delegator.new(baseclass, subclass, callback)
47
+ delegator.foo 'bar', 'baz', &block
48
+ end
49
+ end
50
+
51
+ end
52
+
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'concerned_inheritance'
3
+
4
+ describe ConcernedInheritance::ModuleMethods do
5
+ let(:the_module) do
6
+ Module.new do
7
+ extend ConcernedInheritance
8
+ end
9
+ end
10
+
11
+ describe '.define_inherited_callback' do
12
+ it 'should include the callback' do
13
+ block = Proc.new { }
14
+ the_module.send :define_inherited_callback, &block
15
+ the_module.inherited_callbacks.should include block
16
+ end
17
+ end
18
+
19
+ describe '.inherited' do
20
+ it 'should call define_inherited_callback' do
21
+ block = Proc.new { }
22
+ the_module.should_receive(:define_inherited_callback).with(&block)
23
+ the_module.send :inherited, &block
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,359 @@
1
+ require 'spec_helper'
2
+
3
+ describe MemoryModel::Base::Actionable do
4
+ let(:model) do
5
+ Class.new(MemoryModel::Base) do
6
+ field :foo
7
+ end
8
+ end
9
+ let(:value) { 'bar' }
10
+ subject(:instance) { model.new(foo: value) }
11
+ before(:each) do
12
+ stub_const('MyModel', model)
13
+ end
14
+
15
+ describe '.create' do
16
+ it 'should be persisted' do
17
+ instance = model.create
18
+ instance.should be_persisted
19
+ end
20
+
21
+ it 'should call the save method' do
22
+ model.any_instance.should_receive(:save)
23
+ model.create
24
+ end
25
+ end
26
+
27
+ describe '.delete_all' do
28
+ it 'should call delete on each item' do
29
+ collection_mock = 10.times.map { mock }
30
+ model.stub(:all).and_return(collection_mock)
31
+ model.all.each do |instance|
32
+ instance.should_receive(:delete).and_return(instance)
33
+ instance.should_receive(:deleted?).and_return(true)
34
+ end
35
+ model.delete_all
36
+ end
37
+ it 'should return true' do
38
+ 10.times.each { model.create }
39
+ model.all.should be_present
40
+ model.delete_all.should be_true
41
+ end
42
+ end
43
+
44
+ describe '.destroy_all' do
45
+ it 'should call delete on each item' do
46
+ collection_mock = 10.times.map { mock }
47
+ model.stub(:all).and_return(collection_mock)
48
+ model.all.each do |instance|
49
+ instance.should_receive(:destroy).and_return(instance)
50
+ instance.should_receive(:deleted?).and_return(true)
51
+ end
52
+ model.destroy_all
53
+ end
54
+ it 'should return true' do
55
+ 10.times.each { model.create }
56
+ model.all.should be_present
57
+ model.destroy_all.should be_true
58
+ end
59
+ end
60
+
61
+ describe '#commit' do
62
+ it 'should save to the collection' do
63
+ expect { instance.commit }.to change { model.all }
64
+ end
65
+
66
+ it 'should always be the latest record' do
67
+ instance.commit
68
+ instance.commit.timestamp.should == model.find(instance.id).timestamp
69
+ end
70
+
71
+ it 'should have a timestamp' do
72
+ instance.commit
73
+ instance.timestamp.should be_present
74
+ end
75
+
76
+ it 'should have unfrozen attributes' do
77
+ instance.commit
78
+ instance.instance_variable_get(:@attributes).should_not be_frozen
79
+ end
80
+ end
81
+
82
+ describe '#delete' do
83
+ it 'should be frozen' do
84
+ instance.commit.delete.should be_frozen
85
+ end
86
+ end
87
+
88
+ describe '#deleted_at' do
89
+ context 'when deleted' do
90
+ it 'should have a timestamp' do
91
+ deleted_instance = instance.commit.delete
92
+ instance.deleted_at.should == deleted_instance.timestamp
93
+ end
94
+ end
95
+
96
+ context 'when not deleted' do
97
+ it 'should be nil' do
98
+ instance.commit
99
+ instance.deleted_at.should be_nil
100
+ end
101
+ end
102
+ end
103
+
104
+ describe '#destroy' do
105
+ it 'should call delete' do
106
+ instance.should_receive(:delete)
107
+ instance.destroy
108
+ end
109
+
110
+ context 'with a before_destroy callback' do
111
+ it 'should run the callback' do
112
+ model.before_destroy :test_method
113
+ instance.should_receive(:test_method) do
114
+ instance.should_receive(:delete).and_return(true)
115
+ end
116
+ instance.destroy
117
+ end
118
+
119
+ it 'should execution if the callback returns false' do
120
+ model.before_destroy :test_method
121
+ instance.should_receive(:test_method).and_return(false)
122
+ instance.should_not_receive(:delete)
123
+ instance.destroy
124
+ end
125
+ end
126
+
127
+ context 'with an after_destroy callback' do
128
+ it 'should run the callback' do
129
+ model.after_destroy :test_method
130
+ instance.should_receive(:delete) do
131
+ instance.should_receive(:test_method)
132
+ end
133
+ instance.destroy
134
+ end
135
+ end
136
+
137
+ context 'with an around_destroy callback' do
138
+ it 'should run the callback' do
139
+ model.around_destroy :around_test
140
+ model.send :define_method, :around_test do |&block|
141
+ test_method_a
142
+ block.call
143
+ test_method_b
144
+ end
145
+ instance.should_receive(:test_method_a) do
146
+ instance.should_receive(:delete) do
147
+ instance.should_receive(:test_method_b)
148
+ end
149
+ end
150
+ instance.destroy
151
+ end
152
+ end
153
+ end
154
+
155
+ describe '#dup' do
156
+ it 'should perform a deep_dup' do
157
+ instance.should_receive(:deep_dup)
158
+ instance.dup
159
+ end
160
+ end
161
+
162
+ describe '#deep_dup' do
163
+ it 'should not be frozen' do
164
+ dup = instance.freeze.deep_dup
165
+ instance.should be_frozen
166
+ dup.should_not be_frozen
167
+ end
168
+
169
+ it 'should return a new object' do
170
+ dup = instance.deep_dup
171
+ dup.object_id.should_not == instance.object_id
172
+ end
173
+ end
174
+
175
+ describe '#freeze' do
176
+ it 'should remove invalid ivars' do
177
+ ivar = :@foo
178
+ instance.instance_variable_set ivar, 'bar'
179
+ instance.freeze
180
+ instance.instance_variables.should_not include ivar
181
+ end
182
+
183
+ it 'should be frozen' do
184
+ instance.freeze
185
+ instance.should be_frozen
186
+ end
187
+ end
188
+
189
+ describe '#restore' do
190
+ it 'should not be deleted' do
191
+ instance.commit.delete
192
+ restored_instance = instance.restore
193
+ restored_instance.should_not be_deleted
194
+ end
195
+ end
196
+
197
+ describe '#save' do
198
+ it 'should call delete' do
199
+ instance.should_receive(:commit)
200
+ instance.save
201
+ end
202
+
203
+ context 'with a before_save callback' do
204
+ it 'should run the callback' do
205
+ model.before_save :test_method
206
+ instance.should_receive(:test_method) do
207
+ instance.should_receive(:commit).and_return(true)
208
+ end
209
+ instance.save
210
+ end
211
+
212
+ it 'should execution if the callback returns false' do
213
+ model.before_save :test_method
214
+ instance.should_receive(:test_method).and_return(false)
215
+ instance.should_not_receive(:commit)
216
+ instance.save
217
+ end
218
+ end
219
+
220
+ context 'with an after_save callback' do
221
+ it 'should run the callback' do
222
+ model.after_save :test_method
223
+ instance.should_receive(:commit) do
224
+ instance.should_receive(:test_method)
225
+ end
226
+ instance.save
227
+ end
228
+ end
229
+
230
+ context 'with an around_save callback' do
231
+ it 'should run the callback' do
232
+ model.around_save :around_test
233
+ model.send :define_method, :around_test do |&block|
234
+ test_method_a
235
+ block.call
236
+ test_method_b
237
+ end
238
+ instance.should_receive(:test_method_a) do
239
+ instance.should_receive(:commit) do
240
+ instance.should_receive(:test_method_b)
241
+ end
242
+ end
243
+ instance.save
244
+ end
245
+ end
246
+
247
+ context 'with a new record' do
248
+ context 'with a before_create callback' do
249
+ it 'should run the callback' do
250
+ model.before_create :test_method
251
+ instance.should_receive(:test_method) do
252
+ instance.should_receive(:commit).and_return(true)
253
+ end
254
+ instance.save
255
+ end
256
+
257
+ it 'should execution if the callback returns false' do
258
+ model.before_create :test_method
259
+ instance.should_receive(:test_method).and_return(false)
260
+ instance.should_not_receive(:commit)
261
+ instance.save
262
+ end
263
+ end
264
+
265
+ context 'with an after_create callback' do
266
+ it 'should run the callback' do
267
+ model.after_create :test_method
268
+ instance.should_receive(:commit) do
269
+ instance.should_receive(:test_method)
270
+ end
271
+ instance.save
272
+ end
273
+ end
274
+
275
+ context 'with an around_create callback' do
276
+ it 'should run the callback' do
277
+ model.around_create :around_test
278
+ model.send :define_method, :around_test do |&block|
279
+ test_method_a
280
+ block.call
281
+ test_method_b
282
+ end
283
+ instance.should_receive(:test_method_a) do
284
+ instance.should_receive(:commit) do
285
+ instance.should_receive(:test_method_b)
286
+ end
287
+ end
288
+ instance.save
289
+ end
290
+ end
291
+
292
+ context 'it should not call an update callback' do
293
+ it 'should run the callback' do
294
+ model.before_update :test_method
295
+ instance.should_receive(:commit)
296
+ instance.should_not_receive(:test_method)
297
+ instance.save
298
+ end
299
+ end
300
+ end
301
+
302
+ context 'with an existing record' do
303
+ before(:each) { instance.commit }
304
+ context 'with a before_update callback' do
305
+ it 'should run the callback' do
306
+ model.before_update :test_method
307
+ instance.should_receive(:test_method) do
308
+ instance.should_receive(:commit).and_return(true)
309
+ end
310
+ instance.save
311
+ end
312
+
313
+ it 'should execution if the callback returns false' do
314
+ model.before_update :test_method
315
+ instance.should_receive(:test_method).and_return(false)
316
+ instance.should_not_receive(:commit)
317
+ instance.save
318
+ end
319
+ end
320
+
321
+ context 'with an after_update callback' do
322
+ it 'should run the callback' do
323
+ model.after_update :test_method
324
+ instance.should_receive(:commit) do
325
+ instance.should_receive(:test_method)
326
+ end
327
+ instance.save
328
+ end
329
+ end
330
+
331
+ context 'with an around_update callback' do
332
+ it 'should run the callback' do
333
+ model.around_update :around_test
334
+ model.send :define_method, :around_test do |&block|
335
+ test_method_a
336
+ block.call
337
+ test_method_b
338
+ end
339
+ instance.should_receive(:test_method_a) do
340
+ instance.should_receive(:commit) do
341
+ instance.should_receive(:test_method_b)
342
+ end
343
+ end
344
+ instance.save
345
+ end
346
+ end
347
+
348
+ context 'it should not call an create callback' do
349
+ it 'should run the callback' do
350
+ model.before_create :test_method
351
+ instance.should_receive(:commit)
352
+ instance.should_not_receive(:test_method)
353
+ instance.save
354
+ end
355
+ end
356
+ end
357
+ end
358
+
359
+ end