memory_model 0.0.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.
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