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
@@ -0,0 +1,39 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
module Associations
|
3
|
+
class AssociationScope
|
4
|
+
|
5
|
+
attr_reader :association
|
6
|
+
|
7
|
+
delegate :klass, :owner, :reflection, :to => :association
|
8
|
+
delegate :chain, :conditions, :options, :ninja_model, :active_record, :to => :reflection
|
9
|
+
|
10
|
+
def initialize(association)
|
11
|
+
@association = association
|
12
|
+
end
|
13
|
+
|
14
|
+
def scope
|
15
|
+
scope = klass.unscoped
|
16
|
+
scope = scope.extending(*Array.wrap(options[:extend]))
|
17
|
+
scope = scope.apply_finder_options(
|
18
|
+
options.slice(
|
19
|
+
:order, :limit, :joins, :group, :having, :offset
|
20
|
+
)
|
21
|
+
)
|
22
|
+
|
23
|
+
add_constraints(scope)
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_constraints(scope)
|
27
|
+
if reflection.source_macro == :belongs_to
|
28
|
+
key = reflection.association_primary_key
|
29
|
+
foreign_key = reflection.foreign_key
|
30
|
+
else
|
31
|
+
key = reflection.foreign_key
|
32
|
+
foreign_key = reflection.active_record_primary_key
|
33
|
+
end
|
34
|
+
|
35
|
+
scope = scope.where(key => owner[foreign_key])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,38 +1,50 @@
|
|
1
1
|
module NinjaModel
|
2
2
|
module Associations
|
3
|
-
class BelongsToAssociation <
|
4
|
-
def initialize(owner, reflection)
|
5
|
-
@owner, @reflection = owner, reflection
|
6
|
-
end
|
3
|
+
class BelongsToAssociation < SingularAssociation
|
7
4
|
|
8
5
|
def replace(record)
|
9
|
-
if record
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
6
|
+
raise_on_type_mismatch(record) if record
|
7
|
+
|
8
|
+
replace_keys(record)
|
9
|
+
|
10
|
+
@updated = true if record
|
11
|
+
|
12
|
+
self.target = record
|
13
|
+
end
|
14
|
+
|
15
|
+
def updated?
|
16
|
+
@updated
|
18
17
|
end
|
19
18
|
|
20
19
|
private
|
21
20
|
|
22
|
-
def find_target
|
23
|
-
|
24
|
-
|
21
|
+
def find_target?
|
22
|
+
!loaded? && foreign_key_present? && klass
|
23
|
+
end
|
24
|
+
|
25
|
+
def different_target?(record)
|
26
|
+
record.nil? && owner[reflection.foreign_key] ||
|
27
|
+
record && record.id != owner[reflection.foreign_key]
|
28
|
+
end
|
29
|
+
|
30
|
+
def replace_keys(record)
|
31
|
+
if record
|
32
|
+
owner[reflection.foreign_key] = record[reflection.association_primary_key(record.class)]
|
25
33
|
else
|
26
|
-
|
34
|
+
owner[reflection.foreign_key] = nil
|
27
35
|
end
|
28
36
|
end
|
29
37
|
|
30
|
-
def
|
31
|
-
|
38
|
+
def foreign_key_present?
|
39
|
+
owner[reflection.foreign_key]
|
32
40
|
end
|
33
41
|
|
34
|
-
def
|
35
|
-
|
42
|
+
def target_id
|
43
|
+
if options[:primary_key]
|
44
|
+
owner.send(reflection.name).try(:id)
|
45
|
+
else
|
46
|
+
owner[reflection.foreign_key]
|
47
|
+
end
|
36
48
|
end
|
37
49
|
end
|
38
50
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
module Associations
|
3
|
+
module Builder
|
4
|
+
class Association
|
5
|
+
class_attribute :valid_options
|
6
|
+
|
7
|
+
self.valid_options = [:class_name, :foreign_key, :select, :conditions, :extend]
|
8
|
+
|
9
|
+
class_attribute :macro
|
10
|
+
|
11
|
+
attr_reader :model, :name, :options, :reflection
|
12
|
+
|
13
|
+
def self.build(model, name, options)
|
14
|
+
new(model, name, options).build
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(model, name, options)
|
18
|
+
@model, @name, @options = model, name, options
|
19
|
+
end
|
20
|
+
|
21
|
+
def build
|
22
|
+
validate_options
|
23
|
+
reflection = model.create_reflection(self.class.macro, name, options, model)
|
24
|
+
define_accessors
|
25
|
+
reflection
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def validate_options
|
31
|
+
options.assert_valid_keys(self.class.valid_options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def define_accessors
|
35
|
+
define_readers
|
36
|
+
define_writers
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_readers
|
40
|
+
name = self.name
|
41
|
+
|
42
|
+
model.redefine_method(name) do |*params|
|
43
|
+
association(name).reader(*params)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def define_writers
|
48
|
+
name = self.name
|
49
|
+
|
50
|
+
model.redefine_method("#{name}=") do |value|
|
51
|
+
association(name).writer(value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
module Associations
|
3
|
+
module Builder
|
4
|
+
class BelongsTo < SingularAssociation
|
5
|
+
self.macro = :belongs_to
|
6
|
+
|
7
|
+
def build
|
8
|
+
reflection = super
|
9
|
+
configure_dependency
|
10
|
+
reflection
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def configure_dependency
|
16
|
+
if options[:dependent]
|
17
|
+
unless options[:dependenc].in?([:destroy, :delete])
|
18
|
+
raise ArgumentError, "The :dependent option expects either :destroy or delete (#{options[:dependent].inspect})"
|
19
|
+
end
|
20
|
+
|
21
|
+
method_name = "belongs_to_dependent_#{options[:dependent]}_for_#{name}"
|
22
|
+
model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
|
23
|
+
def #{method_name}
|
24
|
+
association = #{name}
|
25
|
+
association.#{options[:dependent]} if association
|
26
|
+
end
|
27
|
+
eoruby
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
module Associations
|
3
|
+
module Builder
|
4
|
+
class CollectionAssociation < Association
|
5
|
+
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
|
6
|
+
|
7
|
+
self.valid_options += [
|
8
|
+
:order, :group, :limit, :offset, :before_add, :after_add,
|
9
|
+
:before_remove, :after_remove
|
10
|
+
]
|
11
|
+
|
12
|
+
def self.build(model, name, options, &extension)
|
13
|
+
new(model, name, options, &extension).build
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(model, name, options, &extension)
|
17
|
+
super(model, name, options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def build
|
21
|
+
reflection = super
|
22
|
+
CALLBACKS.each { |callback_name| define_callback(callback_name) }
|
23
|
+
reflection
|
24
|
+
end
|
25
|
+
|
26
|
+
def writable?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def define_callback(callback_name)
|
33
|
+
full_callback_name = "#{callback_name}_for_#{name}"
|
34
|
+
|
35
|
+
model.class_attribute full_callback_name.to_sym unless model.method_defined?(full_callback_name)
|
36
|
+
|
37
|
+
model.send("#{full_callback_name}=", Array.wrap(options[callback_name.to_sym]))
|
38
|
+
end
|
39
|
+
|
40
|
+
def define_readers
|
41
|
+
super
|
42
|
+
|
43
|
+
name = self.name
|
44
|
+
model.redefine_method("#{name.to_s.singularize}_ids") do
|
45
|
+
association(name).ids_reader
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def define_writers
|
50
|
+
super
|
51
|
+
|
52
|
+
name = self.name
|
53
|
+
model.redefine_method("#{name.to_s.singularize}_ids=") do |ids|
|
54
|
+
association(name).ids_writer(ids)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
module Associations
|
3
|
+
module Builder
|
4
|
+
class HasOne < SingularAssociation
|
5
|
+
self.macro = :has_one
|
6
|
+
|
7
|
+
def constructable?
|
8
|
+
true
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def validate_options
|
14
|
+
valid_options = self.class.valid_options
|
15
|
+
options.assert_valid_keys(valid_options)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
module Associations
|
3
|
+
module Builder
|
4
|
+
class SingularAssociation < Association
|
5
|
+
self.valid_options += [:dependent, :primary_key]
|
6
|
+
|
7
|
+
def constructable?
|
8
|
+
true
|
9
|
+
end
|
10
|
+
|
11
|
+
def define_accessors
|
12
|
+
super
|
13
|
+
define_constructors if constructable?
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def define_readers
|
19
|
+
super
|
20
|
+
name = self.name
|
21
|
+
|
22
|
+
model.redefine_method("#{name}_loaded?") do
|
23
|
+
ActiveSupport::Deprecation.warn(
|
24
|
+
"Calling obj.#{name}_loaded? is deprecated. Please use " \
|
25
|
+
"obj.association(:#{name}).loaded? instead."
|
26
|
+
)
|
27
|
+
association(name).loaded?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def define_constructors
|
32
|
+
name = self.name
|
33
|
+
|
34
|
+
model.redefine_method("build_#{name}") do |*params, &block|
|
35
|
+
association(name).build(*params, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
model.redefine_method("create_#{name}") do |*params, &block|
|
39
|
+
association(name).create(*params, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
model.redefine_method("create_#{name}!") do |*params, &block|
|
43
|
+
association(name).create!(*params, &block)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
module Associations
|
3
|
+
class CollectionAssociation < Association
|
4
|
+
attr_reader :proxy
|
5
|
+
delegate :select, :find, :first, :last, :count, :size, :length,
|
6
|
+
:empty?, :any?, :to => :scoped
|
7
|
+
|
8
|
+
def initialize(owner, reflection)
|
9
|
+
super
|
10
|
+
@proxy = CollectionProxy.new(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def reader(force_reload = false)
|
14
|
+
if force_reload
|
15
|
+
klass.uncached { reload }
|
16
|
+
end
|
17
|
+
proxy
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset
|
21
|
+
@loaded = false
|
22
|
+
@target = []
|
23
|
+
end
|
24
|
+
|
25
|
+
#def find(*args)
|
26
|
+
# if block_given?
|
27
|
+
# load_target.find(*args) { |*block_args| yield(*block_args) }
|
28
|
+
# else
|
29
|
+
# scoped.find(*args)
|
30
|
+
# end
|
31
|
+
#end
|
32
|
+
|
33
|
+
def first(*args)
|
34
|
+
first_or_last(:first, *args)
|
35
|
+
end
|
36
|
+
|
37
|
+
def last(*args)
|
38
|
+
first_or_last(:last, *args)
|
39
|
+
end
|
40
|
+
|
41
|
+
def build(attributes = {}, options = {}, &block)
|
42
|
+
if attributes.is_a?(Array)
|
43
|
+
attributes.collect { |attr| build(attr, options, &block) }
|
44
|
+
else
|
45
|
+
add_to_target(build_record(attributes, options)) do |record|
|
46
|
+
yield(record) if block_given?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#def create(attributes = {}, options = {}, &block)
|
52
|
+
# create_record(attributes, options, &block)
|
53
|
+
#end
|
54
|
+
|
55
|
+
#def create!(attributes = {}, options = {}, &block)
|
56
|
+
# create_record(attributes, options, true, &block)
|
57
|
+
#end
|
58
|
+
|
59
|
+
def add_to_target(record)
|
60
|
+
yield(record) if block_given?
|
61
|
+
@target << record
|
62
|
+
record
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def find_target
|
68
|
+
puts "find_target for #{self}"
|
69
|
+
scoped.all
|
70
|
+
end
|
71
|
+
|
72
|
+
#def create_record(attributes, options, raise = false, &block)
|
73
|
+
# unless owner.persisted?
|
74
|
+
# raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
|
75
|
+
# end
|
76
|
+
|
77
|
+
# if attributes.is_a?(Array)
|
78
|
+
# attributes.collect { |attr| create_record(attr, options, raise, &block) }
|
79
|
+
# else
|
80
|
+
# add_to_target(build_record(attributes, options)) do |record|
|
81
|
+
# yield(record) if block_given?
|
82
|
+
# insert_record(record, true, raise)
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#end
|
86
|
+
|
87
|
+
#def insert_record(record, validate = true, raise = false)
|
88
|
+
# raise NotImplementedError
|
89
|
+
#end
|
90
|
+
|
91
|
+
def create_scope
|
92
|
+
scoped.scope_for_create.stringify_keys
|
93
|
+
end
|
94
|
+
|
95
|
+
def first_or_last(type, *args)
|
96
|
+
args.shift if args.first.is_a?(Hash) && args.first.empty?
|
97
|
+
|
98
|
+
collection = scoped.all
|
99
|
+
collection.send(type, *args)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|