ninja-model 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/.gitignore +1 -1
  2. data/Gemfile +1 -0
  3. data/Rakefile +6 -0
  4. data/lib/ninja_model.rb +23 -7
  5. data/lib/ninja_model/adapters.rb +25 -20
  6. data/lib/ninja_model/adapters/adapter_manager.rb +2 -0
  7. data/lib/ninja_model/adapters/adapter_pool.rb +2 -0
  8. data/lib/ninja_model/associations.rb +63 -101
  9. data/lib/ninja_model/associations/association.rb +146 -0
  10. data/lib/ninja_model/associations/association_scope.rb +39 -0
  11. data/lib/ninja_model/associations/belongs_to_association.rb +33 -21
  12. data/lib/ninja_model/associations/builder/association.rb +57 -0
  13. data/lib/ninja_model/associations/builder/belongs_to.rb +33 -0
  14. data/lib/ninja_model/associations/builder/collection_association.rb +60 -0
  15. data/lib/ninja_model/associations/builder/has_many.rb +11 -0
  16. data/lib/ninja_model/associations/builder/has_one.rb +20 -0
  17. data/lib/ninja_model/associations/builder/singular_association.rb +49 -0
  18. data/lib/ninja_model/associations/collection_association.rb +103 -0
  19. data/lib/ninja_model/associations/collection_proxy.rb +45 -0
  20. data/lib/ninja_model/associations/has_many_association.rb +19 -43
  21. data/lib/ninja_model/associations/has_one_association.rb +52 -6
  22. data/lib/ninja_model/associations/singular_association.rb +61 -0
  23. data/lib/ninja_model/attribute.rb +5 -2
  24. data/lib/ninja_model/attribute_methods.rb +35 -40
  25. data/lib/ninja_model/base.rb +31 -39
  26. data/lib/ninja_model/identity.rb +8 -6
  27. data/lib/ninja_model/rails_ext/active_record.rb +69 -225
  28. data/lib/ninja_model/railtie.rb +0 -9
  29. data/lib/ninja_model/reflection.rb +103 -20
  30. data/lib/ninja_model/relation.rb +2 -2
  31. data/lib/ninja_model/relation/query_methods.rb +16 -0
  32. data/lib/ninja_model/version.rb +1 -1
  33. data/ninja-model.gemspec +2 -1
  34. data/spec/db/schema.rb +15 -0
  35. data/spec/{ninja_model → lib/ninja_model}/adapters/abstract_adapter_spec.rb +0 -0
  36. data/spec/lib/ninja_model/adapters/adapter_manager_spec.rb +72 -0
  37. data/spec/lib/ninja_model/adapters/adapter_pool_spec.rb +230 -0
  38. data/spec/lib/ninja_model/adapters_spec.rb +120 -0
  39. data/spec/lib/ninja_model/associations/belongs_to_association_spec.rb +76 -0
  40. data/spec/lib/ninja_model/associations/has_many_association_spec.rb +118 -0
  41. data/spec/lib/ninja_model/associations/has_one_association_spec.rb +80 -0
  42. data/spec/{ninja_model → lib/ninja_model}/attribute_methods_spec.rb +2 -2
  43. data/spec/{ninja_model → lib/ninja_model}/attribute_spec.rb +4 -0
  44. data/spec/{ninja_model → lib/ninja_model}/base_spec.rb +0 -0
  45. data/spec/{ninja_model → lib/ninja_model}/identity_spec.rb +0 -0
  46. data/spec/{ninja_model → lib/ninja_model}/persistence_spec.rb +0 -0
  47. data/spec/{ninja_model → lib/ninja_model}/predicate_spec.rb +0 -0
  48. data/spec/{ninja_model → lib/ninja_model}/query_methods_spec.rb +0 -0
  49. data/spec/{ninja_model → lib/ninja_model}/reflection_spec.rb +7 -9
  50. data/spec/{ninja_model → lib/ninja_model}/relation_spec.rb +0 -0
  51. data/spec/{ninja_model → lib/ninja_model}/symbol_spec.rb +0 -0
  52. data/spec/{ninja_model → lib/ninja_model}/validation_spec.rb +0 -0
  53. data/spec/{ninja_model_spec.rb → lib/ninja_model_spec.rb} +0 -0
  54. data/spec/models/bio.rb +8 -0
  55. data/spec/models/body.rb +7 -0
  56. data/spec/models/category.rb +7 -0
  57. data/spec/models/email_address.rb +3 -0
  58. data/spec/models/post.rb +13 -0
  59. data/spec/models/tag.rb +3 -0
  60. data/spec/models/user.rb +4 -0
  61. data/spec/spec_helper.rb +38 -5
  62. data/spec/support/dummy_adapter/adapter.rb +48 -0
  63. data/spec/support/factories/bio.rb +11 -0
  64. data/spec/support/factories/body.rb +12 -0
  65. data/spec/support/factories/email_address.rb +5 -0
  66. data/spec/support/factories/post.rb +12 -0
  67. data/spec/support/factories/tag.rb +5 -0
  68. data/spec/support/factories/user.rb +5 -0
  69. metadata +121 -63
  70. data/spec/ninja_model/adapters/adapter_manager_spec.rb +0 -69
  71. data/spec/ninja_model/adapters/adapter_pool_spec.rb +0 -230
  72. data/spec/ninja_model/adapters_spec.rb +0 -85
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe NinjaModel::Associations::BelongsToAssociation do
4
+ let(:user) { Factory(:user) }
5
+ let(:post) { Factory(:post, :user_id => user.id) }
6
+
7
+ context 'with an ActiveRecord parent' do
8
+ describe 'accessing the association directly' do
9
+ it 'should return an instance' do
10
+ post.user.should eql(user)
11
+ end
12
+ end
13
+
14
+ describe 'assigning an instance' do
15
+ it 'should update the associated id' do
16
+ post.user = user
17
+ post.user_id.should eql(user.id)
18
+ end
19
+ end
20
+
21
+ it 'should raise an exception when trying to assign the wrong class' do
22
+ lambda { post.user = Tag.new }.should raise_error
23
+ end
24
+ end
25
+
26
+ context 'with a NinjaModel parent' do
27
+ context 'and an ActiveRecord child' do
28
+ describe 'accessing the association directly' do
29
+ it 'should trigger a fetch' do
30
+ NinjaModel::Relation.any_instance.expects(:to_a).returns(post)
31
+ t = Tag.new(:post_id => post.id)
32
+ t.post
33
+ end
34
+
35
+ it 'should assign the proper predicates' do
36
+ t = Tag.new(:post_id => post.id)
37
+ predicates = t.association(:post).scoped.predicates
38
+ predicates.count.should eql(1)
39
+ predicates.first.attribute.should eql(:id)
40
+ predicates.first.meth.should eql(:eq)
41
+ predicates.first.value.should eql(post.id)
42
+ end
43
+ end
44
+ end
45
+
46
+ context 'and a NinjaModel child' do
47
+ describe 'accessing the association directly' do
48
+ it 'should trigger a fetch' do
49
+ NinjaModel::Relation.any_instance.expects(:to_a).returns(post)
50
+ c = Category.new(:post_id => post.id)
51
+ c.post
52
+ end
53
+
54
+ it 'should assign the proper predicates' do
55
+ c = Category.new(:post_id => post.id)
56
+ predicates = c.association(:post).scoped.predicates
57
+ predicates.count.should eql(1)
58
+ predicates.first.attribute.should eql(:id)
59
+ predicates.first.meth.should eql(:eq)
60
+ predicates.first.value.should eql(post.id)
61
+ end
62
+ end
63
+ end
64
+
65
+ it 'should raise an exception when trying to assign the wrong class' do
66
+ t = Tag.new
67
+ lambda { t.post = user }.should raise_error
68
+ end
69
+
70
+ it 'should properly update the ids when assigning a new instance' do
71
+ t = Factory(:tag)
72
+ t.post = post
73
+ t.post_id.should eql(post.id)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe NinjaModel::Associations::HasManyAssociation do
4
+ let(:user) { Factory(:user) }
5
+
6
+ context 'with an ActiveRecord parent' do
7
+
8
+ describe 'accessing the association directly' do
9
+ it 'should return a CollectionProxy' do
10
+ user.posts.should be_kind_of(NinjaModel::Associations::CollectionProxy)
11
+ end
12
+ end
13
+
14
+ describe 'first' do
15
+ it 'should trigger a fetch' do
16
+ NinjaModel::Relation.any_instance.expects(:to_a).returns([])
17
+ user.posts.first.should
18
+ end
19
+ end
20
+
21
+ describe 'adding a scope' do
22
+ it 'should return a NinjaModel::Relation' do
23
+ user.posts.published.should be_kind_of(NinjaModel::Relation)
24
+ end
25
+ it 'should have the correct predicates' do
26
+ predicates = user.posts.published.predicates
27
+ predicates.count.should eql(2)
28
+
29
+ predicates.first.attribute.should eql(:user_id)
30
+ predicates.first.meth.should eql(:eq)
31
+ predicates.first.value.should eql(user.id)
32
+
33
+ predicates.last.attribute.should eql(:published)
34
+ predicates.last.meth.should eql(:eq)
35
+ predicates.last.value.should eql(true)
36
+ end
37
+ end
38
+
39
+ describe 'building an associated object' do
40
+ it 'should return an unitialized object' do
41
+ user.posts.build.should be_kind_of(Post)
42
+ end
43
+ it 'should already belong to the user' do
44
+ user.posts.build.user.id.should eql(user.id)
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'with a NinjaModel parent' do
50
+
51
+ let(:post) { Factory(:post) }
52
+ let!(:tags) { FactoryGirl.create_list(:tag, 2, :post_id => 1) }
53
+
54
+ context 'and an ActiveRecord association' do
55
+ it 'should return an Array' do
56
+ post.tags.should be_kind_of(Array)
57
+ end
58
+
59
+ it 'should retrieve the proper associated objects' do
60
+ post.tags.count.should be > 0
61
+ end
62
+
63
+ it 'should allow chaining queries' do
64
+ post.tags.where(:name => 'Tag 2').count.should eql(1)
65
+ end
66
+
67
+ describe 'building an associated object' do
68
+ it 'should return an unitialized object' do
69
+ post.tags.build.should be_kind_of(Tag)
70
+ end
71
+ it 'should already belong to the Post' do
72
+ post.tags.build.post_id.should eql(post.id)
73
+ end
74
+ end
75
+ end
76
+
77
+ context 'and a NinjaModel association' do
78
+ it 'should return a CollectionProxy' do
79
+ post.categories.should be_kind_of(NinjaModel::Associations::CollectionProxy)
80
+ end
81
+
82
+ it 'should assign the proper predicates' do
83
+ predicates = post.categories.scoped.predicates
84
+ predicates.count.should eql(1)
85
+ predicates.first.attribute.should eql(:post_id)
86
+ predicates.first.meth.should eql(:eq)
87
+ predicates.first.value.should eql(1)
88
+ end
89
+
90
+ it 'should allow chaining' do
91
+ predicates = post.categories.where(:name => 'foo').predicates
92
+ predicates.count.should eql(2)
93
+ predicates.first.attribute.should eql(:post_id)
94
+ predicates.first.meth.should eql(:eq)
95
+ predicates.first.value.should eql(1)
96
+ predicates.last.attribute.should eql(:name)
97
+ predicates.last.meth.should eql(:eq)
98
+ predicates.last.value.should eql('foo')
99
+ end
100
+
101
+ describe 'accessing the collection' do
102
+ it 'should trigger a fetch' do
103
+ NinjaModel::Relation.any_instance.expects(:to_a).returns([])
104
+ post.categories.count
105
+ end
106
+ end
107
+
108
+ describe 'building an associated object' do
109
+ it 'should return an unitialized object' do
110
+ post.categories.build.should be_kind_of(Category)
111
+ end
112
+ it 'should already belong to the user' do
113
+ post.categories.build.post_id.should eql(post.id)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe NinjaModel::Associations::HasOneAssociation do
4
+ let(:user) { Factory(:user) }
5
+ let(:post) { Factory(:post, :user_id => user.id) }
6
+ let(:bio) { Factory(:bio, :user_id => user.id) }
7
+
8
+ context 'with an ActiveRecord parent' do
9
+ describe 'accessing the association directly' do
10
+ it 'should trigger a fetch' do
11
+ NinjaModel::Relation.any_instance.expects(:to_a).returns([bio])
12
+ user.bio.name.should eql(bio.name)
13
+ end
14
+ end
15
+
16
+ it 'should assign the proper predicates' do
17
+ predicates = user.association(:bio).scoped.predicates
18
+ predicates.count.should eql(1)
19
+ predicates.first.attribute.should eql(:user_id)
20
+ predicates.first.meth.should eql(:eq)
21
+ predicates.first.value.should eql(user.id)
22
+ end
23
+ end
24
+
25
+ context 'with a NinjaModel parent' do
26
+ context 'and an ActiveRecord child' do
27
+ let!(:email_address) { Factory(:email_address, :bio_id => bio.id) }
28
+ describe 'accessing the association directly' do
29
+ it 'should retrieve the record' do
30
+ bio.email_address.should be_kind_of(EmailAddress)
31
+ end
32
+ end
33
+
34
+ it 'should allow building the association' do
35
+ email = bio.build_email_address
36
+ email.should be_kind_of(EmailAddress)
37
+ email.bio_id.should eql(bio.id)
38
+ end
39
+
40
+ it 'should allow creating the association' do
41
+ bio.create_email_address(:email => 'foo@bar.com').should be_true
42
+ end
43
+ end
44
+
45
+ context 'and a NinjaModel child' do
46
+ let(:body) { Factory(:body, :post_id => post.id) }
47
+ describe 'accessing the association directly' do
48
+ it 'should trigger a fetch' do
49
+ NinjaModel::Relation.any_instance.expects(:to_a).returns([body])
50
+ post.body
51
+ end
52
+
53
+ it 'should assign the proper predicates' do
54
+ predicates = post.association(:body).scoped.predicates
55
+ predicates.count.should eql(1)
56
+ predicates.first.attribute.should eql(:post_id)
57
+ predicates.first.meth.should eql(:eq)
58
+ predicates.first.value.should eql(post.id)
59
+ end
60
+ end
61
+
62
+ it 'should allow building the association' do
63
+ #
64
+ # We have to stub to_a here because building will try and retrieve the
65
+ # existing record to remove ownership
66
+ #
67
+ NinjaModel::Relation.any_instance.stubs(:to_a).returns([])
68
+ body = post.build_body
69
+ body.should be_kind_of(Body)
70
+ body.post_id.should eql(post.id)
71
+ end
72
+
73
+ it 'should allow creating the association' do
74
+ NinjaModel::Relation.any_instance.stubs(:to_a).returns([])
75
+ Body.any_instance.stubs(:create).returns(true)
76
+ post.create_body(:text => 'Foobar')
77
+ end
78
+ end
79
+ end
80
+ end
@@ -5,8 +5,8 @@ describe NinjaModel::AttributeMethods do
5
5
  attribute :test, :string
6
6
  end
7
7
  subject { AttributeModel.new }
8
- specify { subject.attribute_method?(:test).should be_true }
9
- specify { subject.attribute_method?(:invalid).should be_false }
8
+ specify { subject.send(:attribute_method?, 'test').should be_true }
9
+ specify { subject.send(:attribute_method?, 'invalid').should be_false }
10
10
 
11
11
  it 'should return a list of valid attribute names' do
12
12
  AttributeModel.attribute_names.should eql(['test'])
@@ -8,6 +8,10 @@ describe NinjaModel::Attribute do
8
8
  RSpec::Matchers.define :convert do |input|
9
9
  chain :to do |expected|
10
10
  @expected = expected
11
+
12
+ failure_message_for_should do |attr|
13
+ "convert(#{input.inspect}) should be #{@expected.inspect}, but got #{@actual.inspect}"
14
+ end
11
15
  end
12
16
 
13
17
  match do |attr|
File without changes
@@ -14,15 +14,13 @@ describe NinjaModel::Reflection do
14
14
 
15
15
  describe 'create_reflection' do
16
16
  it 'should store the reflections on the class' do
17
- @klass = Class.new(NinjaModel::Base)
18
- @reflection = @klass.create_reflection(:has_one, :target_model, {}, @klass)
19
- @klass.reflections.should eql(:target_model => @reflection)
17
+ @reflection = ReflectionModel.create_reflection(:has_one, :target_model, {}, TargetModel)
18
+ ReflectionModel.reflections.should eql(:target_model => @reflection)
20
19
  end
21
20
 
22
21
  it 'should return a reflection for a particular association' do
23
- @klass = Class.new(NinjaModel::Base)
24
- @reflection = @klass.create_reflection(:has_one, :target_model, {}, @klass)
25
- @klass.reflect_on_association(:target_model).should eql(@reflection)
22
+ @reflection = ReflectionModel.create_reflection(:has_one, :target_model, {}, TargetModel)
23
+ ReflectionModel.reflect_on_association(:target_model).should eql(@reflection)
26
24
  end
27
25
 
28
26
  context 'has_one' do
@@ -30,7 +28,7 @@ describe NinjaModel::Reflection do
30
28
  its(:class_name) { should eql('TargetModel') }
31
29
  its(:klass) { should eql(TargetModel) }
32
30
  its(:collection?) { should be_false }
33
- its(:primary_key_name) { should eql('reflection_model_id') }
31
+ its(:foreign_key) { should eql('reflection_model_id') }
34
32
  its(:association_foreign_key) { should eql('target_model_id') }
35
33
  its(:belongs_to?) { should be_false }
36
34
  end
@@ -40,7 +38,7 @@ describe NinjaModel::Reflection do
40
38
  its(:class_name) { should eql('TargetModel') }
41
39
  its(:klass) { should eql(TargetModel) }
42
40
  its(:collection?) { should be_true }
43
- its(:primary_key_name) { should eql('reflection_model_id') }
41
+ its(:foreign_key) { should eql('reflection_model_id') }
44
42
  its(:association_foreign_key) { should eql('target_model_id') }
45
43
  its(:belongs_to?) { should be_false }
46
44
  end
@@ -50,7 +48,7 @@ describe NinjaModel::Reflection do
50
48
  its(:class_name) { should eql('ReflectionModel') }
51
49
  its(:klass) { should eql(ReflectionModel) }
52
50
  its(:collection?) { should be_false }
53
- its(:primary_key_name) { should eql('reflection_model_id') }
51
+ its(:foreign_key) { should eql('reflection_model_id') }
54
52
  its(:association_foreign_key) { should eql('reflection_model_id') }
55
53
  its(:belongs_to?) { should be_true }
56
54
  end
@@ -0,0 +1,8 @@
1
+ class Bio < NinjaModel::Base
2
+ attribute :id, :integer
3
+ attribute :user_id, :integer
4
+ attribute :name, :string
5
+
6
+ belongs_to :user
7
+ has_one :email_address
8
+ end
@@ -0,0 +1,7 @@
1
+ class Body < NinjaModel::Base
2
+ attribute :id, :integer, :primary_key => true
3
+ attribute :post_id, :integer
4
+ attribute :text, :string
5
+
6
+ belongs_to :post
7
+ end
@@ -0,0 +1,7 @@
1
+ class Category < NinjaModel::Base
2
+ attribute :id, :integer, :primary_key => true
3
+ attribute :post_id, :integer
4
+ attribute :name, :string
5
+
6
+ belongs_to :post
7
+ end
@@ -0,0 +1,3 @@
1
+ class EmailAddress < ActiveRecord::Base
2
+ belongs_to :bio
3
+ end
@@ -0,0 +1,13 @@
1
+ class Post < NinjaModel::Base
2
+ attribute :id, :integer, :primary_key => true
3
+ attribute :user_id, :integer
4
+ attribute :title, :string
5
+ attribute :published, :boolean
6
+
7
+ scope :published, where(:published => true)
8
+
9
+ belongs_to :user
10
+ has_one :body
11
+ has_many :tags
12
+ has_many :categories
13
+ end
@@ -0,0 +1,3 @@
1
+ class Tag < ActiveRecord::Base
2
+ belongs_to :post
3
+ end
@@ -0,0 +1,4 @@
1
+ class User < ActiveRecord::Base
2
+ has_many :posts
3
+ has_one :bio
4
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,22 +1,55 @@
1
+ require 'rubygems'
2
+ require 'bundler'
1
3
  require 'simplecov'
2
4
  SimpleCov.start do
3
5
  add_filter 'spec'
4
6
  end
5
- #Bundler.require(:default)
6
- require 'rspec/core'
7
- require 'ninja-model'
8
-
9
- Dir[File.join(File.expand_path('../', __FILE__), 'support/**/*.rb')].each { |f| require f }
7
+ Bundler.require(:default, :test)
10
8
 
11
9
  RSpec.configure do |config|
12
10
  config.mock_with :mocha
13
11
  end
14
12
 
13
+
14
+ def init_active_record
15
+ db_root = File.join(File.expand_path('../', __FILE__), 'db')
16
+ ActiveRecord::Base.configurations['test'] = {
17
+ 'adapter' => 'sqlite3',
18
+ 'database' => File.join(db_root, 'test.sqlite3'),
19
+ 'pool' => 5,
20
+ 'timeout' => 5000
21
+ }
22
+ ActiveRecord::Base.establish_connection(:test)
23
+
24
+ schema_file = File.join(File.expand_path('../', __FILE__), 'db', 'schema.rb')
25
+ load schema_file
26
+ end
27
+
28
+ init_active_record
29
+
15
30
  class DummyLogger
16
31
  def debug(*args)
32
+ #puts "*** DEBUG ***"
33
+ #puts args
17
34
  end
18
35
  def warn(*args)
36
+ puts "*** WARNING ***"
37
+ puts args
38
+ end
39
+
40
+ def error(*args)
41
+ puts "*** ERROR ***"
42
+ puts args
43
+ end
44
+
45
+ def debug?
46
+ true
19
47
  end
20
48
  end
21
49
 
22
50
  NinjaModel.set_logger(DummyLogger.new)
51
+ ActiveRecord::Base.logger = DummyLogger.new
52
+
53
+ ActiveSupport::Dependencies.autoload_paths << File.join(File.expand_path('../', __FILE__), 'models')
54
+
55
+ Dir[File.join(File.expand_path('../', __FILE__), 'support/**/*.rb')].each { |f| require f }