ninja-model 0.4.2 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +19 -0
  2. data/Rakefile +0 -7
  3. data/autotest/discover.rb +1 -0
  4. data/lib/ninja_model.rb +22 -26
  5. data/lib/ninja_model/adapters.rb +33 -43
  6. data/lib/ninja_model/adapters/abstract_adapter.rb +2 -10
  7. data/lib/ninja_model/adapters/adapter_manager.rb +17 -10
  8. data/lib/ninja_model/adapters/adapter_pool.rb +15 -17
  9. data/lib/ninja_model/adapters/adapter_specification.rb +3 -3
  10. data/lib/ninja_model/associations.rb +25 -106
  11. data/lib/ninja_model/associations/association_proxy.rb +119 -1
  12. data/lib/ninja_model/associations/belongs_to_association.rb +5 -1
  13. data/lib/ninja_model/attribute.rb +130 -0
  14. data/lib/ninja_model/{attributes.rb → attribute_methods.rb} +21 -42
  15. data/lib/ninja_model/base.rb +23 -20
  16. data/lib/ninja_model/callbacks.rb +2 -11
  17. data/lib/ninja_model/identity.rb +6 -11
  18. data/lib/ninja_model/persistence.rb +15 -30
  19. data/lib/ninja_model/predicate.rb +4 -4
  20. data/lib/ninja_model/rails_ext/active_record.rb +187 -0
  21. data/lib/ninja_model/railtie.rb +14 -8
  22. data/lib/ninja_model/reflection.rb +7 -14
  23. data/lib/ninja_model/relation.rb +5 -3
  24. data/lib/ninja_model/relation/finder_methods.rb +4 -8
  25. data/lib/ninja_model/relation/spawn_methods.rb +1 -1
  26. data/lib/ninja_model/validation.rb +6 -23
  27. data/lib/ninja_model/version.rb +1 -1
  28. data/ninja-model.gemspec +28 -0
  29. data/spec/ninja_model/adapters/abstract_adapter_spec.rb +45 -0
  30. data/spec/ninja_model/adapters/adapter_manager_spec.rb +69 -0
  31. data/spec/ninja_model/adapters/adapter_pool_spec.rb +210 -48
  32. data/spec/ninja_model/adapters_spec.rb +77 -0
  33. data/spec/ninja_model/attribute_methods_spec.rb +95 -0
  34. data/spec/ninja_model/attribute_spec.rb +129 -0
  35. data/spec/ninja_model/base_spec.rb +4 -52
  36. data/spec/ninja_model/identity_spec.rb +16 -32
  37. data/spec/ninja_model/persistence_spec.rb +130 -4
  38. data/spec/ninja_model/predicate_spec.rb +40 -6
  39. data/spec/ninja_model/query_methods_spec.rb +76 -74
  40. data/spec/ninja_model/reflection_spec.rb +63 -0
  41. data/spec/ninja_model/relation_spec.rb +213 -20
  42. data/spec/ninja_model/symbol_spec.rb +19 -0
  43. data/spec/ninja_model/validation_spec.rb +18 -0
  44. data/spec/spec_helper.rb +9 -0
  45. data/spec/support/matchers/convert.rb +30 -0
  46. metadata +85 -63
  47. data/lib/ninja_model/associations/active_record_proxy.rb +0 -53
  48. data/lib/ninja_model/associations/ninja_model_proxy.rb +0 -46
  49. data/lib/ninja_model/configuration.rb +0 -20
  50. data/lib/ninja_model/errors.rb +0 -5
  51. data/lib/ninja_model/log_subscriber.rb +0 -18
  52. data/lib/ninja_model/scoping.rb +0 -50
  53. data/spec/ninja_model/attributes_spec.rb +0 -85
  54. data/spec/ninja_model/scoping_spec.rb +0 -40
@@ -29,7 +29,7 @@ module NinjaModel
29
29
  end
30
30
  merged_relation.predicates = merged_predicates
31
31
 
32
- Relation::SINGLE_VALUE_ATTRS.reject { |m| m == :lock }.each do |method|
32
+ Relation::SINGLE_VALUE_ATTRS.each do |method|
33
33
  value = r.send(:"#{method}_value")
34
34
  merged_relation.send(:"#{method}_value=", value) unless value.nil?
35
35
  end
@@ -1,13 +1,14 @@
1
- require 'active_model'
2
-
3
1
  module NinjaModel
4
- module Validation
5
- extend ActiveSupport::Concern
2
+ class Base
6
3
  include ActiveModel::Validations
4
+ define_model_callbacks :validation
5
+ end
6
+
7
+ module Validation
7
8
 
8
9
  def save(options={})
9
10
  run_callbacks :validation do
10
- perform_validations(options) ? super : false
11
+ valid?(options.is_a?(Hash) ? options[:context] : nil) ? super : false
11
12
  end
12
13
  end
13
14
 
@@ -16,23 +17,5 @@ module NinjaModel
16
17
  output = super(context)
17
18
  errors.empty? && output
18
19
  end
19
-
20
- protected
21
-
22
- def perform_validations(options={})
23
- perform_validation = case options
24
- when Hash
25
- options[:validate] != false
26
- else
27
- ActiveSupport::Deprecation.warn "save(#{options}) is deprecated, please give save(:validate => #{options}) instead", caller
28
- options
29
- end
30
-
31
- if perform_validation
32
- valid?(options.is_a?(Hash) ? options[:context] : nil)
33
- else
34
- true
35
- end
36
- end
37
20
  end
38
21
  end
@@ -1,3 +1,3 @@
1
1
  module NinjaModel
2
- VERSION = "0.4.2"
2
+ VERSION = "0.5.1"
3
3
  end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ninja_model/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = %q{ninja-model}
7
+ s.version = NinjaModel::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.author = %q{Josh Williams}
10
+ s.email = %q{theprime@codingprime.com}
11
+ s.homepage = %q{http://github.com/t3hpr1m3/ninja-model.git}
12
+ s.summary = %q{Pseudo-ORM for Ruby}
13
+ s.description = %q{Pseudo-ORM for Ruby/Rails with an ActiveRecord-like interface}
14
+
15
+ s.rubyforge_project = s.name
16
+
17
+ s.files = `git ls-files`.split("\n") - ["Gemfile.lock"]
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'activerecord', '~> 3.0.3'
22
+
23
+ s.add_development_dependency 'rspec', '~> 2.2.0'
24
+ s.add_development_dependency 'mocha', '~> 0.9.8'
25
+ s.add_development_dependency 'rcov', '~> 0.9.9'
26
+ s.add_development_dependency 'nokogiri', '~> 1.4.4'
27
+ s.add_development_dependency 'autotest', '~> 4.4.6'
28
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe NinjaModel::Adapters::AbstractAdapter do
4
+ subject { NinjaModel::Adapters::AbstractAdapter.new({}) }
5
+ it { should respond_to(:reconnect!) }
6
+ it { should respond_to(:disconnect!) }
7
+ it { should respond_to(:reset!) }
8
+ it { should respond_to(:verify!) }
9
+ it { should respond_to(:create) }
10
+ it { should respond_to(:read) }
11
+ it { should respond_to(:update) }
12
+ it { should respond_to(:destroy) }
13
+ its(:adapter_name) { should eql('Abstract') }
14
+ its(:persistent_connection?) { should be_true }
15
+ its(:active?) { should be_false }
16
+ specify { subject.create({}).should be_false }
17
+ specify { subject.read({}).should be_nil }
18
+ specify { subject.update({}).should be_false }
19
+ specify { subject.destroy({}).should be_false }
20
+
21
+ it 'should be active after reconnect' do
22
+ subject.reconnect!
23
+ subject.active?.should be_true
24
+ end
25
+
26
+ describe 'verify!' do
27
+ it 'should call reconnect! when inactive' do
28
+ subject.stubs(:active?).returns(false)
29
+ subject.expects(:reconnect!)
30
+ subject.verify!
31
+ end
32
+
33
+ it 'should not call reconnect! when active' do
34
+ subject.stubs(:active?).returns(true)
35
+ subject.expects(:reconnect!).never
36
+ subject.verify!
37
+ end
38
+ end
39
+
40
+ it 'should be inactive after disconnect' do
41
+ subject.reconnect!
42
+ subject.disconnect!
43
+ subject.active?.should be_false
44
+ end
45
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ describe NinjaModel::Adapters::AdapterManager do
4
+ class DummyAdapter < NinjaModel::Adapters::AbstractAdapter; end
5
+ class TestModel < NinjaModel::Base; end
6
+ class ChildModel < TestModel; end
7
+
8
+ before {
9
+ @manager = NinjaModel::Adapters::AdapterManager.new
10
+ @manager.class.register_adapter_class(:dummy, DummyAdapter)
11
+ }
12
+ subject { @manager }
13
+
14
+ it 'should remember registered adapters' do
15
+ subject.class.registered?(:dummy).should be_true
16
+ end
17
+
18
+ describe 'creating an adapter' do
19
+ before { @manager.create_adapter('TestModel', NinjaModel::Adapters::AdapterSpecification.new({}, :dummy)) }
20
+ it 'should add the adapter to the pool' do
21
+ @manager.retrieve_adapter_pool(TestModel).spec.name.should eql(:dummy)
22
+ end
23
+ it 'should return the adapter for descendent classes' do
24
+ @manager.retrieve_adapter_pool(ChildModel).spec.name.should eql(:dummy)
25
+ end
26
+ end
27
+
28
+ describe 'removing an adapter' do
29
+ before {
30
+ @spec = NinjaModel::Adapters::AdapterSpecification.new({:foo => 'bar'}, :dummy)
31
+ @manager.create_adapter('TestModel', @spec)
32
+ }
33
+ it 'should remove the adapter from the pool' do
34
+ @manager.remove_adapter(TestModel)
35
+ @manager.retrieve_adapter_pool(TestModel).should be_nil
36
+ end
37
+ it 'should cause retrieve_adapter to raise an error' do
38
+ @manager.remove_adapter(TestModel)
39
+ lambda { @manager.retrieve_adapter(TestModel) }.should raise_error(StandardError)
40
+ end
41
+ it 'should return the spec that created it' do
42
+ @manager.remove_adapter(TestModel).should eql({:foo => 'bar'})
43
+ end
44
+ end
45
+
46
+ describe 'release_active_adapters!' do
47
+ before {
48
+ @spec = NinjaModel::Adapters::AdapterSpecification.new({:foo => 'bar'}, :dummy)
49
+ @manager.create_adapter('TestModel', @spec)
50
+ }
51
+ it 'should call release_instance on all pools' do
52
+ @pool = @manager.adapter_pools['TestModel']
53
+ @pool.expects(:release_instance)
54
+ @manager.release_active_adapters!
55
+ end
56
+ end
57
+
58
+ describe 'release_all_adapters!' do
59
+ before {
60
+ @spec = NinjaModel::Adapters::AdapterSpecification.new({:foo => 'bar'}, :dummy)
61
+ @manager.create_adapter('TestModel', @spec)
62
+ }
63
+ it 'should call shutdown! on all pools' do
64
+ @pool = @manager.adapter_pools['TestModel']
65
+ @pool.expects(:shutdown!)
66
+ @manager.release_all_adapters!
67
+ end
68
+ end
69
+ end
@@ -1,72 +1,234 @@
1
1
  require 'spec_helper'
2
- require 'ninja_model'
3
2
 
4
3
  describe NinjaModel::Adapters::AdapterPool do
5
- before(:each) do
6
- @spec = mock('AdapterSpecification') do
7
- stubs(:adapter_method).returns(:dummy_adapter)
8
- stubs(:config)
9
- end
4
+ class DummyAdapter < NinjaModel::Adapters::AbstractAdapter; end
5
+ before {
6
+ @spec = NinjaModel::Adapters::AdapterSpecification.new({}, :dummy)
7
+ NinjaModel::Base.register_adapter(:dummy, DummyAdapter)
8
+ @pool = NinjaModel::Adapters::AdapterPool.new(@spec)
9
+ }
10
+ subject { @pool }
11
+ its(:spec) { should eql(@spec) }
12
+ its(:instances) { should be_empty }
13
+ its(:connected?) { should be_false }
14
+ its(:current_instance_id) { should eql(Thread.current.object_id) }
10
15
 
11
- @adapter = mock('AbstractAdapter') do
12
- stubs(:verify!)
16
+ describe 'obtaining an adapter instance' do
17
+ it 'should store the instance for a given thread' do
18
+ subject.instance
19
+ subject.expects(:checkout).never
20
+ subject.instance
13
21
  end
22
+ end
14
23
 
15
- @pool = NinjaModel::Adapters::AdapterPool.new(@spec)
16
- @thread = mock('thread') do
17
- stubs('object_id').returns(123)
24
+ describe 'releasing an adapter instance' do
25
+ it 'should call checkout when obtaining another instance' do
26
+ subject.expects(:checkout).twice
27
+ subject.instance
28
+ subject.release_instance
29
+ subject.instance
18
30
  end
19
- Thread.stubs(:current).returns(@thread)
20
- NinjaModel::Base.stubs(:dummy_adapter).returns(@adapter)
21
31
  end
22
32
 
23
- it 'should properly respond to connected?' do
24
- @pool.connected?.should be_false
25
- @pool.instance
26
- @pool.connected?.should be_true
33
+ describe 'with_instance' do
34
+ it 'should yield an instance' do
35
+ @inst = nil
36
+ subject.with_instance do |inst|
37
+ @inst = inst
38
+ end
39
+ @inst.should be_kind_of(DummyAdapter)
40
+ end
41
+ it 'should release the instance when complete' do
42
+ subject.expects(:release_instance).with(subject.send :current_instance_id)
43
+ subject.with_instance do |inst|
44
+ end
45
+ end
27
46
  end
28
47
 
29
- it 'should initialize from an AdapterSpecification' do
30
- spec = mock('AdapterSpecification')
31
- NinjaModel::Adapters::AdapterPool.new(spec).spec.should eql(spec)
48
+ describe 'shutdown!' do
49
+ it 'should checkin all assigned instances' do
50
+ @inst = subject.instance
51
+ subject.expects(:checkin).with(@inst)
52
+ subject.shutdown!
53
+ end
54
+ it 'should call disconnect on all instances' do
55
+ @inst = subject.instance
56
+ @inst.expects(:disconnect!)
57
+ subject.shutdown!
58
+ end
32
59
  end
33
60
 
34
- it 'should return a valid instance_id' do
35
- (@pool.send :current_instance_id).should eql(@thread.object_id)
61
+ describe 'clear_stale_cached_instances!' do
62
+ it 'should reap any instances for dead threads' do
63
+ @thread = mock('Thread') do
64
+ stubs(:object_id).returns(Thread.current.object_id)
65
+ stubs(:alive?).returns(false)
66
+ end
67
+ Thread.stubs(:list).returns([@thread])
68
+ @inst = subject.instance
69
+ subject.expects(:checkin).with(@inst)
70
+ subject.send :clear_stale_cached_instances!
71
+ end
72
+ it 'should not reap instances for live threads' do
73
+ @thread = mock('Thread') do
74
+ stubs(:object_id).returns(Thread.current.object_id)
75
+ stubs(:alive?).returns(true)
76
+ end
77
+ Thread.stubs(:list).returns([@thread])
78
+ @inst = subject.instance
79
+ subject.expects(:checkin).with(@inst).never
80
+ subject.send :clear_stale_cached_instances!
81
+ end
36
82
  end
37
83
 
38
- describe 'when creating an instance' do
39
- it 'should create a valid instance' do
40
- (@pool.send :new_instance).should eql(@adapter)
84
+ describe 'checkout' do
85
+ subject { NinjaModel::Adapters::AdapterPool.new(@spec) }
86
+ context 'with no current instances' do
87
+ it 'should checkout a new instance' do
88
+ subject.expects(:checkout_new_instance).returns('foo')
89
+ subject.send(:checkout)
90
+ end
91
+ it 'should store the instance' do
92
+ subject.send(:checkout)
93
+ subject.instances.count.should eql(1)
94
+ end
41
95
  end
42
96
 
43
- it 'should store the instance' do
44
- @pool.stubs(:new_instance).returns(@adapter)
45
- @pool.instance
46
- @pool.instances.should include(@adapter)
97
+ context 'with 1 existing instance' do
98
+ context 'that is already assigned' do
99
+ it 'should checkout a new instance' do
100
+ threads = []
101
+ subject.expects(:new_instance).twice.returns(NinjaModel::Adapters::AdapterManager.registered[subject.spec.name].new(subject.spec.config))
102
+ 2.times do |idx|
103
+ threads << Thread.new do
104
+ subject.send(:checkout)
105
+ end
106
+ end
107
+ threads.each { |t| t.join }
108
+ end
109
+ end
110
+ context 'that is not assigned' do
111
+ it 'should checkout the existing instance' do
112
+ subject.expects(:checkout_existing_instance).returns('existing')
113
+ subject.instance
114
+ subject.release_instance
115
+ subject.instances.count.should eql(1)
116
+ subject.send(:checkout)
117
+ end
118
+ end
47
119
  end
48
120
 
49
- it 'should assign the instance to the current_thread' do
50
- @pool.stubs(:new_instance).returns(@adapter)
51
- @pool.instance
52
- @pool.stubs(:checkout).returns("new_instance")
53
- @pool.instance.should_not eql("new_instance")
54
- @pool.instance.should eql(@adapter)
55
- end
121
+ context 'with the maximum number of instances' do
122
+ context 'with an available instance' do
123
+ it 'should successfully check out an instance' do
124
+ subject.instance
125
+ threads = []
126
+ 4.times do |idx|
127
+ threads << Thread.new do
128
+ subject.instance
129
+ end
130
+ end
131
+ threads.each { |t| t.join }
132
+ subject.release_instance
133
+ subject.send(:checkout).should_not be_nil
134
+ end
135
+ end
56
136
 
57
- it 'should reuse an unallocated instance' do
58
- @pool.stubs(:current_instance_id).returns(123)
59
- inst = @pool.instance
60
- @pool.release_instance(123)
61
- @pool.instance.should eql(inst)
62
- end
63
- end
137
+ context 'with no available instances' do
138
+ it 'should raise a ConnectionTimeoutError' do
139
+ threads = []
140
+ 5.times do |idx|
141
+ threads << Thread.new do
142
+ subject.instance
143
+ end
144
+ end
145
+ threads.each { |t| t.join }
146
+ subject.stubs(:clear_stale_cached_instances!)
147
+ lambda { subject.instance }.should raise_error(NinjaModel::ConnectionTimeoutError)
148
+ end
149
+ end
64
150
 
65
- it 'should properly allocate/deallocate instances' do
66
- @pool.stubs(:current_instance_id).returns(123)
67
- @pool.expects(:checkout).returns(@adapter)
68
- @pool.expects(:checkin)
69
- @pool.with_instance do |instance|
151
+ context 'with instances that can be reaped' do
152
+ it 'should return an existing instance' do
153
+ threads = []
154
+ 5.times do |idx|
155
+ threads << Thread.new do
156
+ subject.instance
157
+ end
158
+ end
159
+ threads.each { |t| t.join }
160
+ subject.expects(:checkout_existing_instance).returns('foo')
161
+ subject.instance
162
+ end
163
+ end
70
164
  end
165
+
71
166
  end
167
+ # before(:each) do
168
+ # @spec = mock('AdapterSpecification') do
169
+ # stubs(:adapter_method).returns(:dummy_adapter)
170
+ # stubs(:config)
171
+ # end
172
+ #
173
+ # @adapter = mock('AbstractAdapter') do
174
+ # stubs(:verify!)
175
+ # end
176
+ #
177
+ # @pool = NinjaModel::Adapters::AdapterPool.new(@spec)
178
+ # @thread = mock('thread') do
179
+ # stubs('object_id').returns(123)
180
+ # end
181
+ # Thread.stubs(:current).returns(@thread)
182
+ # NinjaModel::Base.stubs(:dummy_adapter).returns(@adapter)
183
+ # end
184
+ #
185
+ # it 'should properly respond to connected?' do
186
+ # @pool.connected?.should be_false
187
+ # @pool.instance
188
+ # @pool.connected?.should be_true
189
+ # end
190
+ #
191
+ # it 'should initialize from an AdapterSpecification' do
192
+ # spec = mock('AdapterSpecification')
193
+ # NinjaModel::Adapters::AdapterPool.new(spec).spec.should eql(spec)
194
+ # end
195
+ #
196
+ # it 'should return a valid instance_id' do
197
+ # (@pool.send :current_instance_id).should eql(@thread.object_id)
198
+ # end
199
+ #
200
+ # describe 'when creating an instance' do
201
+ # it 'should create a valid instance' do
202
+ # (@pool.send :new_instance).should eql(@adapter)
203
+ # end
204
+ #
205
+ # it 'should store the instance' do
206
+ # @pool.stubs(:new_instance).returns(@adapter)
207
+ # @pool.instance
208
+ # @pool.instances.should include(@adapter)
209
+ # end
210
+ #
211
+ # it 'should assign the instance to the current_thread' do
212
+ # @pool.stubs(:new_instance).returns(@adapter)
213
+ # @pool.instance
214
+ # @pool.stubs(:checkout).returns("new_instance")
215
+ # @pool.instance.should_not eql("new_instance")
216
+ # @pool.instance.should eql(@adapter)
217
+ # end
218
+ #
219
+ # it 'should reuse an unallocated instance' do
220
+ # @pool.stubs(:current_instance_id).returns(123)
221
+ # inst = @pool.instance
222
+ # @pool.release_instance(123)
223
+ # @pool.instance.should eql(inst)
224
+ # end
225
+ # end
226
+ #
227
+ # it 'should properly allocate/deallocate instances' do
228
+ # @pool.stubs(:current_instance_id).returns(123)
229
+ # @pool.expects(:checkout).returns(@adapter)
230
+ # @pool.expects(:checkin)
231
+ # @pool.with_instance do |instance|
232
+ # end
233
+ # end
72
234
  end