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.
- data/.gitignore +1 -1
- data/Gemfile +1 -0
- data/Rakefile +6 -0
- data/lib/ninja_model.rb +23 -7
- data/lib/ninja_model/adapters.rb +25 -20
- data/lib/ninja_model/adapters/adapter_manager.rb +2 -0
- data/lib/ninja_model/adapters/adapter_pool.rb +2 -0
- data/lib/ninja_model/associations.rb +63 -101
- data/lib/ninja_model/associations/association.rb +146 -0
- data/lib/ninja_model/associations/association_scope.rb +39 -0
- data/lib/ninja_model/associations/belongs_to_association.rb +33 -21
- data/lib/ninja_model/associations/builder/association.rb +57 -0
- data/lib/ninja_model/associations/builder/belongs_to.rb +33 -0
- data/lib/ninja_model/associations/builder/collection_association.rb +60 -0
- data/lib/ninja_model/associations/builder/has_many.rb +11 -0
- data/lib/ninja_model/associations/builder/has_one.rb +20 -0
- data/lib/ninja_model/associations/builder/singular_association.rb +49 -0
- data/lib/ninja_model/associations/collection_association.rb +103 -0
- data/lib/ninja_model/associations/collection_proxy.rb +45 -0
- data/lib/ninja_model/associations/has_many_association.rb +19 -43
- data/lib/ninja_model/associations/has_one_association.rb +52 -6
- data/lib/ninja_model/associations/singular_association.rb +61 -0
- data/lib/ninja_model/attribute.rb +5 -2
- data/lib/ninja_model/attribute_methods.rb +35 -40
- data/lib/ninja_model/base.rb +31 -39
- data/lib/ninja_model/identity.rb +8 -6
- data/lib/ninja_model/rails_ext/active_record.rb +69 -225
- data/lib/ninja_model/railtie.rb +0 -9
- data/lib/ninja_model/reflection.rb +103 -20
- data/lib/ninja_model/relation.rb +2 -2
- data/lib/ninja_model/relation/query_methods.rb +16 -0
- data/lib/ninja_model/version.rb +1 -1
- data/ninja-model.gemspec +2 -1
- data/spec/db/schema.rb +15 -0
- data/spec/{ninja_model → lib/ninja_model}/adapters/abstract_adapter_spec.rb +0 -0
- data/spec/lib/ninja_model/adapters/adapter_manager_spec.rb +72 -0
- data/spec/lib/ninja_model/adapters/adapter_pool_spec.rb +230 -0
- data/spec/lib/ninja_model/adapters_spec.rb +120 -0
- data/spec/lib/ninja_model/associations/belongs_to_association_spec.rb +76 -0
- data/spec/lib/ninja_model/associations/has_many_association_spec.rb +118 -0
- data/spec/lib/ninja_model/associations/has_one_association_spec.rb +80 -0
- data/spec/{ninja_model → lib/ninja_model}/attribute_methods_spec.rb +2 -2
- data/spec/{ninja_model → lib/ninja_model}/attribute_spec.rb +4 -0
- data/spec/{ninja_model → lib/ninja_model}/base_spec.rb +0 -0
- data/spec/{ninja_model → lib/ninja_model}/identity_spec.rb +0 -0
- data/spec/{ninja_model → lib/ninja_model}/persistence_spec.rb +0 -0
- data/spec/{ninja_model → lib/ninja_model}/predicate_spec.rb +0 -0
- data/spec/{ninja_model → lib/ninja_model}/query_methods_spec.rb +0 -0
- data/spec/{ninja_model → lib/ninja_model}/reflection_spec.rb +7 -9
- data/spec/{ninja_model → lib/ninja_model}/relation_spec.rb +0 -0
- data/spec/{ninja_model → lib/ninja_model}/symbol_spec.rb +0 -0
- data/spec/{ninja_model → lib/ninja_model}/validation_spec.rb +0 -0
- data/spec/{ninja_model_spec.rb → lib/ninja_model_spec.rb} +0 -0
- data/spec/models/bio.rb +8 -0
- data/spec/models/body.rb +7 -0
- data/spec/models/category.rb +7 -0
- data/spec/models/email_address.rb +3 -0
- data/spec/models/post.rb +13 -0
- data/spec/models/tag.rb +3 -0
- data/spec/models/user.rb +4 -0
- data/spec/spec_helper.rb +38 -5
- data/spec/support/dummy_adapter/adapter.rb +48 -0
- data/spec/support/factories/bio.rb +11 -0
- data/spec/support/factories/body.rb +12 -0
- data/spec/support/factories/email_address.rb +5 -0
- data/spec/support/factories/post.rb +12 -0
- data/spec/support/factories/tag.rb +5 -0
- data/spec/support/factories/user.rb +5 -0
- metadata +121 -63
- data/spec/ninja_model/adapters/adapter_manager_spec.rb +0 -69
- data/spec/ninja_model/adapters/adapter_pool_spec.rb +0 -230
- data/spec/ninja_model/adapters_spec.rb +0 -85
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -8,6 +8,12 @@ end
|
|
8
8
|
require 'rspec/core/rake_task'
|
9
9
|
require 'yard'
|
10
10
|
|
11
|
+
#task :prepare_db do
|
12
|
+
# db_root = File.join(File.expand_path('../', __FILE__), 'spec', 'db')
|
13
|
+
# db_file = File.join(db_root, 'test.sqlite3')
|
14
|
+
# sh "rm #{db_file}" if File.exists?(db_file)
|
15
|
+
# sh %Q{sqlite3 "#{db_file}" "create table a (a integer); drop table a;"}
|
16
|
+
#end
|
11
17
|
|
12
18
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
13
19
|
t.verbose = false
|
data/lib/ninja_model.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
require 'active_model'
|
2
|
+
require 'active_record'
|
2
3
|
require 'active_support/core_ext'
|
3
4
|
|
4
5
|
module NinjaModel
|
6
|
+
extend ActiveSupport::Autoload
|
7
|
+
|
5
8
|
class NinjaModelError < StandardError; end
|
6
9
|
|
7
10
|
class << self
|
@@ -12,10 +15,10 @@ module NinjaModel
|
|
12
15
|
end
|
13
16
|
|
14
17
|
def ninja_model?(symbol)
|
15
|
-
klass = symbol.to_s.camelize
|
16
|
-
klass = klass.singularize
|
17
|
-
klass =
|
18
|
-
|
18
|
+
#klass = symbol.to_s.camelize
|
19
|
+
#klass = klass.singularize
|
20
|
+
#klass = symbol.constantize
|
21
|
+
symbol.ancestors.include?(NinjaModel::Base)
|
19
22
|
end
|
20
23
|
|
21
24
|
def configuration
|
@@ -23,12 +26,25 @@ module NinjaModel
|
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
26
|
-
|
29
|
+
autoload :Attribute
|
30
|
+
autoload :AttributeMethods
|
31
|
+
autoload :Associations
|
32
|
+
autoload :Adapters
|
33
|
+
autoload :Base
|
34
|
+
autoload :Callbacks
|
35
|
+
autoload :Identity
|
36
|
+
autoload :Persistence
|
37
|
+
autoload :Predicate
|
38
|
+
autoload :Reflection
|
39
|
+
autoload :Relation
|
40
|
+
autoload :Validation
|
41
|
+
|
42
|
+
ActiveSupport.on_load(:active_record) do
|
43
|
+
require 'ninja_model/rails_ext/active_record'
|
44
|
+
include ActiveRecord::NinjaModelExtensions::ReflectionExt
|
27
45
|
end
|
28
46
|
end
|
29
47
|
|
30
|
-
require 'ninja_model/base'
|
31
|
-
require 'ninja_model/core_ext/symbol'
|
32
48
|
if defined?(Rails)
|
33
49
|
require 'ninja_model/railtie'
|
34
50
|
end
|
data/lib/ninja_model/adapters.rb
CHANGED
@@ -1,47 +1,52 @@
|
|
1
|
-
require 'ninja_model/adapters/abstract_adapter'
|
2
|
-
require 'ninja_model/adapters/adapter_manager'
|
3
|
-
require 'ninja_model/adapters/adapter_pool'
|
4
|
-
require 'ninja_model/adapters/adapter_specification'
|
5
|
-
|
6
1
|
module NinjaModel
|
2
|
+
class AdapterNotSpecified < StandardError; end
|
3
|
+
class InvalidAdapter < StandardError; end
|
4
|
+
class InvalidSpecification < StandardError; end
|
7
5
|
|
8
6
|
module Adapters
|
9
|
-
|
10
|
-
class InvalidAdapter < StandardError; end
|
11
|
-
class InvalidSpecification < StandardError; end
|
12
|
-
end
|
7
|
+
extend ActiveSupport::Concern
|
13
8
|
|
14
|
-
|
15
|
-
|
16
|
-
|
9
|
+
autoload :AbstractAdapter, 'ninja_model/adapters/abstract_adapter'
|
10
|
+
autoload :AdapterManager, 'ninja_model/adapters/adapter_manager'
|
11
|
+
autoload :AdapterPool, 'ninja_model/adapters/adapter_pool'
|
12
|
+
autoload :AdapterSpecification, 'ninja_model/adapters/adapter_specification'
|
13
|
+
|
14
|
+
included do
|
15
|
+
class_attribute :adapter_manager
|
16
|
+
self.adapter_manager = NinjaModel::Adapters::AdapterManager.new
|
17
|
+
end
|
17
18
|
|
18
19
|
def adapter
|
19
20
|
self.class.retrieve_adapter
|
20
21
|
end
|
21
22
|
|
22
|
-
|
23
|
+
module ClassMethods
|
23
24
|
def register_adapter(name, klass)
|
24
|
-
Adapters::
|
25
|
+
if klass.ancestors.include?(NinjaModel::Adapters::AbstractAdapter)
|
26
|
+
Adapters::AdapterManager.register_adapter_class(name, klass)
|
27
|
+
else
|
28
|
+
raise InvalidAdapter, "Invalid adapter: #{klass}"
|
29
|
+
end
|
25
30
|
end
|
26
31
|
|
27
32
|
def set_adapter(spec = nil)
|
28
33
|
case spec
|
29
34
|
when nil
|
30
|
-
raise
|
35
|
+
raise AdapterNotSpecified unless defined?(Rails.env)
|
31
36
|
set_adapter(Rails.env)
|
32
|
-
when Adapters::AdapterSpecification
|
33
|
-
self.adapter_manager.create_adapter(name, spec)
|
34
37
|
when Symbol, String
|
35
38
|
if config = NinjaModel.configuration.specs[spec.to_s]
|
36
39
|
set_adapter(config)
|
37
40
|
else
|
38
|
-
raise
|
41
|
+
raise InvalidSpecification, "#{spec} is not configured"
|
39
42
|
end
|
43
|
+
when Adapters::AdapterSpecification
|
44
|
+
self.adapter_manager.create_adapter(name, spec)
|
40
45
|
else
|
41
46
|
spec = spec.symbolize_keys
|
42
|
-
raise
|
47
|
+
raise AdapterNotSpecified, "configuration does not specify adapter: #{spec}" unless spec.key?(:adapter)
|
43
48
|
adapter_name = spec[:adapter]
|
44
|
-
raise
|
49
|
+
raise InvalidAdapter, "configuration does not specify adapter" unless Adapters::AdapterManager.registered?(adapter_name)
|
45
50
|
shutdown_adapter
|
46
51
|
set_adapter(Adapters::AdapterSpecification.new(spec, adapter_name))
|
47
52
|
end
|
@@ -1,125 +1,87 @@
|
|
1
1
|
module NinjaModel
|
2
|
+
module Associations
|
3
|
+
extend ActiveSupport::Concern
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def has_many(association_id, options = {})
|
23
|
-
reflection = create_has_many_reflection(association_id, options)
|
24
|
-
collection_accessor_methods(reflection, Associations::HasManyAssociation)
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
5
|
+
autoload :Association, 'ninja_model/associations/association'
|
6
|
+
autoload :AssociationProxy, 'ninja_model/associations/association_proxy'
|
7
|
+
autoload :AssociationScope, 'ninja_model/associations/association_scope'
|
8
|
+
autoload :BelongsToAssociation, 'ninja_model/associations/belongs_to_association'
|
9
|
+
autoload :CollectionAssociation, 'ninja_model/associations/collection_association'
|
10
|
+
autoload :CollectionProxy, 'ninja_model/associations/collection_proxy'
|
11
|
+
autoload :HasOneAssociation, 'ninja_model/associations/has_one_association'
|
12
|
+
autoload :HasManyAssociation, 'ninja_model/associations/has_many_association'
|
13
|
+
autoload :SingularAssociation, 'ninja_model/associations/singular_association'
|
14
|
+
|
15
|
+
module Builder
|
16
|
+
autoload :Association, 'ninja_model/associations/builder/association'
|
17
|
+
autoload :SingularAssociation, 'ninja_model/associations/builder/singular_association'
|
18
|
+
autoload :CollectionAssociation, 'ninja_model/associations/builder/collection_association'
|
19
|
+
autoload :HasMany, 'ninja_model/associations/builder/has_many'
|
20
|
+
autoload :BelongsTo, 'ninja_model/associations/builder/belongs_to'
|
21
|
+
autoload :HasOne, 'ninja_model/associations/builder/has_one'
|
22
|
+
end
|
28
23
|
|
29
|
-
|
30
|
-
create_reflection(:has_one, association, options, self)
|
31
|
-
end
|
24
|
+
attr_reader :association_cache
|
32
25
|
|
33
|
-
|
34
|
-
|
35
|
-
end
|
26
|
+
def association(name)
|
27
|
+
association = association_instance_get(name)
|
36
28
|
|
37
|
-
|
38
|
-
|
29
|
+
if association.nil?
|
30
|
+
reflection = self.class.reflect_on_association(name)
|
31
|
+
association = reflection.association_class.new(self, reflection)
|
32
|
+
association_instance_set(name, association)
|
39
33
|
end
|
34
|
+
association
|
35
|
+
end
|
40
36
|
|
41
|
-
|
42
|
-
redefine_method(reflection.name) do |*params|
|
43
|
-
association = association_instance_get(reflection.name)
|
44
|
-
|
45
|
-
if association.nil?
|
46
|
-
association = association_proxy_class.new(self, reflection)
|
47
|
-
retval = association.reload
|
48
|
-
if retval.nil? and association_proxy_class == Associations::BelongsToAssociation
|
49
|
-
association_instance_set(reflection.name, nil)
|
50
|
-
return nil
|
51
|
-
end
|
52
|
-
association_instance_set(reflection.name, association)
|
53
|
-
end
|
54
|
-
association.target.nil? ? nil : association
|
55
|
-
end
|
37
|
+
private
|
56
38
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
redefine_method("#{reflection.name}=") do |new_value|
|
63
|
-
association = association_instance_get(reflection.name)
|
64
|
-
if association.nil? || association.target != new_value
|
65
|
-
association = association_proxy_class.new(self, reflection)
|
66
|
-
end
|
39
|
+
def association_instance_get(name)
|
40
|
+
@association_cache[name.to_sym]
|
41
|
+
end
|
67
42
|
|
68
|
-
|
69
|
-
|
70
|
-
|
43
|
+
def association_instance_set(name, association)
|
44
|
+
@association_cache[name.to_sym] = association
|
45
|
+
end
|
71
46
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
47
|
+
module ClassMethods
|
48
|
+
def add_autosave_association_callbacks(reflection)
|
49
|
+
end
|
50
|
+
def has_one(name, options = {})
|
51
|
+
klass = compute_klass(name, :has_one, options)
|
52
|
+
if NinjaModel.ninja_model?(klass)
|
53
|
+
Builder::HasOne.build(self, name, options)
|
54
|
+
else
|
55
|
+
ActiveRecord::Associations::Builder::HasOne.build(self, name, options)
|
77
56
|
end
|
78
57
|
end
|
79
58
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
association.replace(new_value)
|
87
|
-
association
|
88
|
-
end
|
59
|
+
def belongs_to(name, options = {}, &extension)
|
60
|
+
klass = compute_klass(name, :belongs_to, options)
|
61
|
+
if NinjaModel.ninja_model?(klass)
|
62
|
+
Builder::BelongsTo.build(self, name, options)
|
63
|
+
else
|
64
|
+
ActiveRecord::Associations::Builder::BelongsTo.build(self, name, options)
|
89
65
|
end
|
90
66
|
end
|
91
67
|
|
92
|
-
def
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
99
|
-
association_instance_set(reflection.name, association)
|
100
|
-
association
|
68
|
+
def has_many(name, options = {}, &extension)
|
69
|
+
klass = compute_klass(name, :has_many, options)
|
70
|
+
if NinjaModel.ninja_model?(klass)
|
71
|
+
Builder::HasMany.build(self, name, options)
|
72
|
+
else
|
73
|
+
ActiveRecord::Associations::Builder::HasMany.build(self, name, options)
|
101
74
|
end
|
102
75
|
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
module Associations
|
107
76
|
|
108
|
-
|
109
|
-
autoload :HasOneAssociation, 'ninja_model/associations/has_one_association'
|
110
|
-
autoload :HasManyAssociation, 'ninja_model/associations/has_many_association'
|
111
|
-
autoload :BelongsToAssociation, 'ninja_model/associations/belongs_to_association'
|
77
|
+
private
|
112
78
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
79
|
+
def compute_klass(name, macro, options)
|
80
|
+
klass = options[:class_name] || name
|
81
|
+
klass = klass.to_s.camelize
|
82
|
+
klass = klass.singularize if macro.eql?(:has_many)
|
83
|
+
klass = compute_type(klass)
|
118
84
|
end
|
119
85
|
end
|
120
|
-
|
121
|
-
def association_instance_set(name, association)
|
122
|
-
instance_variable_set("@#{name}", association)
|
123
|
-
end
|
124
86
|
end
|
125
87
|
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
module Associations
|
3
|
+
class Association
|
4
|
+
|
5
|
+
attr_reader :owner, :target, :reflection
|
6
|
+
|
7
|
+
delegate :options, :to => :reflection
|
8
|
+
|
9
|
+
def initialize(owner, reflection)
|
10
|
+
@target = nil
|
11
|
+
@owner, @reflection = owner, reflection
|
12
|
+
|
13
|
+
reset
|
14
|
+
end
|
15
|
+
|
16
|
+
def reset
|
17
|
+
@loaded = false
|
18
|
+
@target = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def reload
|
22
|
+
reset
|
23
|
+
reset_scope
|
24
|
+
load_target
|
25
|
+
self unless target.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
def loaded?
|
29
|
+
@loaded
|
30
|
+
end
|
31
|
+
|
32
|
+
def loaded!
|
33
|
+
@loaded = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def stale_target?
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
def target=(target)
|
41
|
+
@target = target
|
42
|
+
loaded!
|
43
|
+
end
|
44
|
+
|
45
|
+
def scoped
|
46
|
+
target_scope.merge(association_scope)
|
47
|
+
end
|
48
|
+
|
49
|
+
def target_scope
|
50
|
+
klass.scoped
|
51
|
+
end
|
52
|
+
|
53
|
+
def association_scope
|
54
|
+
if klass
|
55
|
+
@association_scope ||= AssociationScope.new(self).scope
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def reset_scope
|
60
|
+
@association_scope = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def set_inverse_instance(record)
|
64
|
+
if record && invertible_for?(record)
|
65
|
+
inverse = record.association(inverse_reflection_for(record).name)
|
66
|
+
inverse.target = owner
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def klass
|
71
|
+
reflection.klass
|
72
|
+
end
|
73
|
+
|
74
|
+
def target_scope
|
75
|
+
klass.scoped
|
76
|
+
end
|
77
|
+
|
78
|
+
def load_target
|
79
|
+
if find_target?
|
80
|
+
@target ||= find_target
|
81
|
+
end
|
82
|
+
loaded! unless loaded?
|
83
|
+
target
|
84
|
+
rescue NinjaModel::RecordNotFound
|
85
|
+
reset
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def find_target?
|
91
|
+
!loaded? && (!owner.new_record? || foreign_key_present?) && klass
|
92
|
+
end
|
93
|
+
|
94
|
+
def build_record(attributes, options)
|
95
|
+
record = reflection.build_association(attributes, options) do |r|
|
96
|
+
attrs = create_scope.except(*r.changed)
|
97
|
+
r.assign_attributes(
|
98
|
+
create_scope.except(*r.changed)
|
99
|
+
)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def creation_attributes
|
104
|
+
attributes = {}
|
105
|
+
if options[:through]
|
106
|
+
raise NotImplementedError, "NinjaModel does not support through associations yet."
|
107
|
+
else
|
108
|
+
if reflection.macro.in?([:has_one, :has_many])
|
109
|
+
attributes[reflection.foreign_key] = owner[reflection.ninja_model_primary_key]
|
110
|
+
if reflection.options[:as]
|
111
|
+
attributes[reflection.type] = owner.class.base_class.name
|
112
|
+
end
|
113
|
+
end
|
114
|
+
attributes
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def set_owner_attributes(record)
|
119
|
+
creation_attributes.each { |key, value| record[key] = value }
|
120
|
+
end
|
121
|
+
|
122
|
+
def foreign_key_present?
|
123
|
+
false
|
124
|
+
end
|
125
|
+
|
126
|
+
def raise_on_type_mismatch(record)
|
127
|
+
unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
|
128
|
+
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
|
129
|
+
raise ActiveRecord::AssociationTypeMismatch, message
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def inverse_reflection_for(record)
|
134
|
+
reflection.inverse_of
|
135
|
+
end
|
136
|
+
|
137
|
+
def invertible_for?(record)
|
138
|
+
inverse_reflection_for(record)
|
139
|
+
end
|
140
|
+
|
141
|
+
def association_class
|
142
|
+
@reflection.klass
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|