ninja-model 0.8.1 → 0.9.0
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.
- 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
|