datamappify 0.60.0 → 0.70.0.beta1
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.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/CHANGELOG.md +20 -0
- data/README.md +120 -3
- data/datamappify.gemspec +3 -3
- data/lib/datamappify/data/criteria/active_record/count.rb +0 -2
- data/lib/datamappify/data/criteria/active_record/criteria.rb +10 -0
- data/lib/datamappify/data/criteria/active_record/criteria_method.rb +65 -0
- data/lib/datamappify/data/criteria/active_record/find.rb +10 -2
- data/lib/datamappify/data/criteria/active_record/find_by_key.rb +4 -2
- data/lib/datamappify/data/criteria/active_record/limit.rb +10 -0
- data/lib/datamappify/data/criteria/active_record/match.rb +40 -0
- data/lib/datamappify/data/criteria/active_record/order.rb +41 -0
- data/lib/datamappify/data/criteria/active_record/rollback.rb +13 -0
- data/lib/datamappify/data/criteria/active_record/save.rb +1 -4
- data/lib/datamappify/data/criteria/active_record/save_by_key.rb +0 -1
- data/lib/datamappify/data/criteria/active_record/where.rb +18 -0
- data/lib/datamappify/data/criteria/common.rb +5 -2
- data/lib/datamappify/data/criteria/concerns/update_primary_record.rb +2 -0
- data/lib/datamappify/data/criteria/relational/concerns/find.rb +35 -0
- data/lib/datamappify/data/criteria/relational/concerns/find_by_key.rb +19 -0
- data/lib/datamappify/data/criteria/relational/criteria.rb +29 -0
- data/lib/datamappify/data/criteria/relational/{find_multiple.rb → criteria_base_method.rb} +2 -28
- data/lib/datamappify/data/criteria/relational/criteria_method.rb +44 -0
- data/lib/datamappify/data/criteria/relational/limit.rb +13 -0
- data/lib/datamappify/data/criteria/relational/save.rb +12 -0
- data/lib/datamappify/data/criteria/sequel/count.rb +0 -2
- data/lib/datamappify/data/criteria/sequel/criteria.rb +10 -0
- data/lib/datamappify/data/criteria/sequel/criteria_method.rb +51 -0
- data/lib/datamappify/data/criteria/sequel/find.rb +10 -2
- data/lib/datamappify/data/criteria/sequel/find_by_key.rb +4 -2
- data/lib/datamappify/data/criteria/sequel/limit.rb +10 -0
- data/lib/datamappify/data/criteria/sequel/match.rb +22 -0
- data/lib/datamappify/data/criteria/sequel/order.rb +22 -0
- data/lib/datamappify/data/criteria/sequel/rollback.rb +13 -0
- data/lib/datamappify/data/criteria/sequel/save.rb +1 -4
- data/lib/datamappify/data/criteria/sequel/save_by_key.rb +0 -1
- data/lib/datamappify/data/criteria/sequel/where.rb +18 -0
- data/lib/datamappify/data/errors.rb +8 -9
- data/lib/datamappify/data/mapper/attribute.rb +13 -33
- data/lib/datamappify/data/mapper.rb +13 -2
- data/lib/datamappify/data/provider/active_record.rb +8 -6
- data/lib/datamappify/data/provider/common_provider.rb +2 -2
- data/lib/datamappify/data/provider/sequel.rb +9 -6
- data/lib/datamappify/entity/association/reference.rb +63 -0
- data/lib/datamappify/entity/association/validation.rb +59 -0
- data/lib/datamappify/entity/association.rb +44 -0
- data/lib/datamappify/entity/{active_model/compatibility.rb → compatibility/active_model.rb} +2 -2
- data/lib/datamappify/entity/compatibility/active_record.rb +22 -0
- data/lib/datamappify/entity/compatibility/association/active_record.rb +43 -0
- data/lib/datamappify/entity/composable/attributes.rb +8 -16
- data/lib/datamappify/entity.rb +8 -6
- data/lib/datamappify/lazy/attributes_handler.rb +1 -1
- data/lib/datamappify/lazy.rb +0 -1
- data/lib/datamappify/repository/mapping_dsl.rb +9 -0
- data/lib/datamappify/repository/query_method/criteria.rb +21 -0
- data/lib/datamappify/repository/query_method/destroy.rb +1 -1
- data/lib/datamappify/repository/query_method/exists.rb +1 -1
- data/lib/datamappify/repository/query_method/find.rb +1 -9
- data/lib/datamappify/repository/query_method/method/multi_result_blender.rb +24 -0
- data/lib/datamappify/repository/query_method/method/reference_handler.rb +60 -0
- data/lib/datamappify/repository/query_method/method/source_attributes_walker.rb +19 -4
- data/lib/datamappify/repository/query_method/method.rb +58 -17
- data/lib/datamappify/repository/query_method/save.rb +4 -4
- data/lib/datamappify/repository/query_method/where_or_match.rb +24 -0
- data/lib/datamappify/repository/query_methods.rb +21 -5
- data/lib/datamappify/repository/unit_of_work/persistent_states/object.rb +2 -2
- data/lib/datamappify/version.rb +1 -1
- data/spec/entity/{relation_spec.rb → association/reference_spec.rb} +2 -2
- data/spec/repository/associations/has_many_spec.rb +259 -0
- data/spec/repository/dirty_tracking_spec.rb +68 -57
- data/spec/repository/finders/all_spec.rb +29 -0
- data/spec/repository/finders/criteria_spec.rb +61 -0
- data/spec/repository/finders/different_providers_spec.rb +81 -0
- data/spec/repository/finders/find_spec.rb +21 -0
- data/spec/repository/finders/match_spec.rb +28 -0
- data/spec/repository/finders/where_spec.rb +18 -0
- data/spec/repository/reverse_mapped_spec.rb +20 -4
- data/spec/support/entities/group.rb +4 -0
- data/spec/support/entities/super_user.rb +9 -0
- data/spec/support/entities/user.rb +5 -0
- data/spec/support/entities/user_info.rb +5 -0
- data/spec/support/repositories/active_record/group_repository.rb +4 -0
- data/spec/support/repositories/active_record/super_user_repository.rb +23 -0
- data/spec/support/repositories/active_record/user_repository.rb +6 -4
- data/spec/support/repositories/sequel/group_repository.rb +4 -0
- data/spec/support/repositories/sequel/super_user_repository.rb +23 -0
- data/spec/support/repositories/sequel/user_repository.rb +6 -4
- data/spec/support/shared/contexts.rb +8 -1
- data/spec/support/shared/examples/repository/finders/where_or_match.rb +130 -0
- data/spec/support/tables/active_record.rb +20 -10
- data/spec/support/tables/sequel.rb +20 -7
- data/spec/unit/entity/{relation_spec.rb → association/reference_spec.rb} +5 -5
- data/spec/unit/repository/query_method_spec.rb +9 -2
- metadata +74 -42
- data/lib/datamappify/data/criteria/active_record/find_multiple.rb +0 -40
- data/lib/datamappify/data/criteria/relational/find.rb +0 -25
- data/lib/datamappify/data/criteria/relational/find_by_key.rb +0 -18
- data/lib/datamappify/data/criteria/sequel/find_multiple.rb +0 -43
- data/lib/datamappify/entity/relation.rb +0 -61
- data/lib/datamappify/repository/query_method/find_multiple.rb +0 -28
- data/spec/repository/finders_spec.rb +0 -211
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Datamappify
|
|
2
|
+
module Entity
|
|
3
|
+
module Association
|
|
4
|
+
# Passes referenced validation errors onto the main entity
|
|
5
|
+
module Validation
|
|
6
|
+
def self.included(klass)
|
|
7
|
+
klass.class_eval do
|
|
8
|
+
attr_accessor :referenced_errors
|
|
9
|
+
|
|
10
|
+
validate :association_validation
|
|
11
|
+
|
|
12
|
+
def referenced_errors
|
|
13
|
+
@referenced_errors ||= {}
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @param context [Symbol]
|
|
19
|
+
# e.g. :create or :update
|
|
20
|
+
#
|
|
21
|
+
# @return [Boolean]
|
|
22
|
+
def valid?(context = nil)
|
|
23
|
+
aggregate_validation_errors(context)
|
|
24
|
+
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# @param (see #valid?)
|
|
31
|
+
#
|
|
32
|
+
# @return [void]
|
|
33
|
+
def aggregate_validation_errors(context)
|
|
34
|
+
self.associations.each do |association|
|
|
35
|
+
self.send(association).reject(&:destroy?).each do |entity|
|
|
36
|
+
entity.invalid?(context) && referenced_entity_validation_errors(entity)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @param (see #valid?)
|
|
42
|
+
#
|
|
43
|
+
# @return [void]
|
|
44
|
+
def referenced_entity_validation_errors(entity)
|
|
45
|
+
entity.errors.messages.each do |attr, msgs|
|
|
46
|
+
self.referenced_errors["#{entity.class.model_name.element}__#{attr}"] = msgs
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @return [void]
|
|
51
|
+
def association_validation
|
|
52
|
+
self.referenced_errors.each do |name, msgs|
|
|
53
|
+
self.errors.add name, msgs.join(', ')
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'datamappify/entity/compatibility/association/active_record'
|
|
2
|
+
require 'datamappify/entity/association/reference'
|
|
3
|
+
require 'datamappify/entity/association/validation'
|
|
4
|
+
|
|
5
|
+
module Datamappify
|
|
6
|
+
module Entity
|
|
7
|
+
module Association
|
|
8
|
+
def self.included(klass)
|
|
9
|
+
klass.class_eval do
|
|
10
|
+
cattr_accessor :associations
|
|
11
|
+
self.associations = []
|
|
12
|
+
|
|
13
|
+
include Reference
|
|
14
|
+
include Validation
|
|
15
|
+
extend DSL
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module DSL
|
|
20
|
+
prepend Compatibility::Association::ActiveRecord
|
|
21
|
+
|
|
22
|
+
# @param name [Symbol, String]
|
|
23
|
+
#
|
|
24
|
+
# @param options [Hash]
|
|
25
|
+
#
|
|
26
|
+
# @return [void]
|
|
27
|
+
def belongs_to(name, options = {})
|
|
28
|
+
references name
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @param name [Symbol, String]
|
|
32
|
+
#
|
|
33
|
+
# @param options [Hash]
|
|
34
|
+
#
|
|
35
|
+
# @return [void]
|
|
36
|
+
def has_many(name, options = {})
|
|
37
|
+
attribute name, Array[options[:via]]
|
|
38
|
+
|
|
39
|
+
self.associations << name
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Datamappify
|
|
2
|
+
module Entity
|
|
3
|
+
module Compatibility
|
|
4
|
+
# Add non-entity related stuff so that an entity can be used in say, forms
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
# @return [Array<Symbol>]
|
|
7
|
+
IGNORED_ATTRIBUTE_NAMES = [:_destroy]
|
|
8
|
+
|
|
9
|
+
def self.included(klass)
|
|
10
|
+
klass.class_eval do
|
|
11
|
+
attribute :_destroy, Virtus::Attribute::Boolean, :default => false
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @return [Boolean]
|
|
16
|
+
def destroy?
|
|
17
|
+
!!_destroy
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Datamappify
|
|
2
|
+
module Entity
|
|
3
|
+
module Compatibility
|
|
4
|
+
module Association
|
|
5
|
+
# Compatibility layer for ActionRecord
|
|
6
|
+
module ActiveRecord
|
|
7
|
+
# Adds `association_name_attributes` as required by
|
|
8
|
+
# nested attributes assignment
|
|
9
|
+
#
|
|
10
|
+
# @param (see DSL#has_many)
|
|
11
|
+
def has_many(name, options = {})
|
|
12
|
+
super
|
|
13
|
+
|
|
14
|
+
self.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
|
15
|
+
def #{name}_attributes=(params = {})
|
|
16
|
+
new_entites = params.map do |index, attributes|
|
|
17
|
+
attributes.delete('id') if attributes['id'].blank?
|
|
18
|
+
#{options[:via]}.new(attributes)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# resets the association attribute if nil,
|
|
22
|
+
# this will reset it back to an empty array
|
|
23
|
+
self.#{name} ||= nil
|
|
24
|
+
|
|
25
|
+
self.#{name} = self.#{name}.map do |entity|
|
|
26
|
+
new_entites.detect { |e| e.id == entity.id } || entity
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
new_entites.each do |entity|
|
|
30
|
+
entity_is_already_added = entity.id && self.#{name}.map(&:id).include?(entity.id)
|
|
31
|
+
|
|
32
|
+
unless entity_is_already_added
|
|
33
|
+
self.#{name} << entity
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
CODE
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -17,35 +17,27 @@ module Datamappify
|
|
|
17
17
|
def build_attributes
|
|
18
18
|
@entity_class.attribute_set.each do |attribute|
|
|
19
19
|
unless excluded_attributes.include?(attribute.name)
|
|
20
|
-
@entity.attribute_set << build_attribute(attribute
|
|
20
|
+
@entity.attribute_set << build_attribute(attribute)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
# @return [Array]
|
|
26
26
|
def excluded_attributes
|
|
27
|
-
@excluded_attributes ||= @entity_class.reference_keys
|
|
27
|
+
@excluded_attributes ||= @entity_class.reference_keys + [:id, *@entity_class::IGNORED_ATTRIBUTE_NAMES]
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
# @param
|
|
31
|
-
#
|
|
32
|
-
# @param attribute_writer [Virtus::Attribute::Writer]
|
|
30
|
+
# @param attribute [Virtus::Attribute]
|
|
33
31
|
#
|
|
34
32
|
# @return [Virtus::Attribute]
|
|
35
|
-
def build_attribute(
|
|
36
|
-
|
|
37
|
-
Attribute.prefix(
|
|
33
|
+
def build_attribute(attribute)
|
|
34
|
+
attribute_name = if @options[:prefix_with]
|
|
35
|
+
Attribute.prefix(attribute.name, @options[:prefix_with])
|
|
38
36
|
else
|
|
39
|
-
|
|
37
|
+
attribute.name
|
|
40
38
|
end
|
|
41
39
|
|
|
42
|
-
Virtus::Attribute.build(
|
|
43
|
-
name,
|
|
44
|
-
attribute_writer.primitive,
|
|
45
|
-
:default => attribute_writer.default_value,
|
|
46
|
-
:visibility => attribute_writer.visibility,
|
|
47
|
-
:coercer => attribute_writer.coercer
|
|
48
|
-
)
|
|
40
|
+
Virtus::Attribute.build(attribute.type, attribute.options.merge(:name => attribute_name))
|
|
49
41
|
end
|
|
50
42
|
end
|
|
51
43
|
end
|
data/lib/datamappify/entity.rb
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
require 'observer'
|
|
2
2
|
require 'virtus'
|
|
3
|
-
require 'datamappify/entity/active_model
|
|
3
|
+
require 'datamappify/entity/compatibility/active_model'
|
|
4
|
+
require 'datamappify/entity/compatibility/active_record'
|
|
4
5
|
require 'datamappify/entity/lazy_checking'
|
|
5
6
|
require 'datamappify/entity/composable'
|
|
6
|
-
require 'datamappify/entity/
|
|
7
|
+
require 'datamappify/entity/association'
|
|
7
8
|
require 'datamappify/entity/inspectable'
|
|
8
9
|
|
|
9
10
|
module Datamappify
|
|
@@ -11,13 +12,14 @@ module Datamappify
|
|
|
11
12
|
def self.included(klass)
|
|
12
13
|
klass.class_eval do
|
|
13
14
|
include Observable
|
|
15
|
+
include Virtus.model
|
|
16
|
+
include Equalizer.new(*klass.attribute_set)
|
|
14
17
|
include ::ActiveModel::Model
|
|
15
|
-
include ActiveModel
|
|
16
|
-
include
|
|
17
|
-
include Virtus::Equalizer.new(inspect)
|
|
18
|
+
include Compatibility::ActiveModel
|
|
19
|
+
include Compatibility::ActiveRecord
|
|
18
20
|
include LazyChecking
|
|
19
21
|
include Composable
|
|
20
|
-
include
|
|
22
|
+
include Association
|
|
21
23
|
include Inspectable
|
|
22
24
|
|
|
23
25
|
attribute :id, Integer
|
|
@@ -8,7 +8,7 @@ module Datamappify
|
|
|
8
8
|
# @param entity [Entity]
|
|
9
9
|
def initialize(entity)
|
|
10
10
|
@entity = entity
|
|
11
|
-
@uncached_attributes = entity.attributes.keys
|
|
11
|
+
@uncached_attributes = entity.attributes.keys - entity.class::IGNORED_ATTRIBUTE_NAMES
|
|
12
12
|
|
|
13
13
|
entity.add_observer(self)
|
|
14
14
|
end
|
data/lib/datamappify/lazy.rb
CHANGED
|
@@ -43,6 +43,15 @@ module Datamappify
|
|
|
43
43
|
|
|
44
44
|
self.current_group_options = {}
|
|
45
45
|
end
|
|
46
|
+
|
|
47
|
+
# @param name [Symbol, String]
|
|
48
|
+
#
|
|
49
|
+
# @param options [Hash]
|
|
50
|
+
#
|
|
51
|
+
# @return [void]
|
|
52
|
+
def references(name, options = {})
|
|
53
|
+
data_mapper.references[name.to_sym] = options
|
|
54
|
+
end
|
|
46
55
|
end
|
|
47
56
|
end
|
|
48
57
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Datamappify
|
|
2
|
+
module Repository
|
|
3
|
+
module QueryMethod
|
|
4
|
+
class Criteria < Method
|
|
5
|
+
# @return [Array<Entity>]
|
|
6
|
+
def perform
|
|
7
|
+
results = dispatch_criteria_to_default_source(
|
|
8
|
+
:Criteria, data_mapper.entity_class, criteria, data_mapper.attributes
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
MultiResultBlender.new(self).blend(results)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @see Method#reader?
|
|
15
|
+
def reader?
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -2,18 +2,10 @@ module Datamappify
|
|
|
2
2
|
module Repository
|
|
3
3
|
module QueryMethod
|
|
4
4
|
class Find < Method
|
|
5
|
-
# @param options (see Method#initialize)
|
|
6
|
-
#
|
|
7
|
-
# @param id [Integer]
|
|
8
|
-
def initialize(options, id)
|
|
9
|
-
super
|
|
10
|
-
@id = id
|
|
11
|
-
end
|
|
12
|
-
|
|
13
5
|
# @return [Entity, nil]
|
|
14
6
|
def perform
|
|
15
7
|
entity = data_mapper.entity_class.new
|
|
16
|
-
entity.id =
|
|
8
|
+
entity.id = criteria
|
|
17
9
|
|
|
18
10
|
if dispatch_criteria_to_default_source(:Exists, entity)
|
|
19
11
|
dispatch_criteria_to_providers(:FindByKey, entity)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Datamappify
|
|
2
|
+
module Repository
|
|
3
|
+
module QueryMethod
|
|
4
|
+
class Method
|
|
5
|
+
class MultiResultBlender
|
|
6
|
+
# @param method_instance [Method]
|
|
7
|
+
def initialize(method_instance)
|
|
8
|
+
@method = method_instance
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @param results [Array<Entity>]
|
|
12
|
+
#
|
|
13
|
+
# @return [Array<Entity>]
|
|
14
|
+
def blend(results)
|
|
15
|
+
results.each do |entity|
|
|
16
|
+
@method.states.find(entity)
|
|
17
|
+
@method.walk_references(:Find, entity)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Datamappify
|
|
2
|
+
module Repository
|
|
3
|
+
module QueryMethod
|
|
4
|
+
class Method
|
|
5
|
+
class ReferenceHandler
|
|
6
|
+
def initialize(options = {})
|
|
7
|
+
@entity = options[:entity]
|
|
8
|
+
@criteria_name = options[:criteria_name]
|
|
9
|
+
@reference_name = options[:reference_name]
|
|
10
|
+
@repository = options[:reference_options][:via]
|
|
11
|
+
@query_method = options[:query_method]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @return [void]
|
|
15
|
+
def execute
|
|
16
|
+
perform_read if @query_method.reader?
|
|
17
|
+
perform_write if @query_method.writer?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
# @return [void]
|
|
23
|
+
def perform_read
|
|
24
|
+
@entity.send("#{@reference_name}=", fetch_referenced_entities)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [void]
|
|
28
|
+
def perform_write
|
|
29
|
+
referenced_entities.each do |entity|
|
|
30
|
+
if entity.destroy?
|
|
31
|
+
if entity.persisted?
|
|
32
|
+
@repository.destroy!(entity)
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
entity.send("#{reference_key}=", @entity.id)
|
|
36
|
+
@repository.states.mark_as_dirty(entity)
|
|
37
|
+
@repository.save!(entity)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [Array]
|
|
43
|
+
def fetch_referenced_entities
|
|
44
|
+
@repository.where(reference_key => @entity.id)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [Array]
|
|
48
|
+
def referenced_entities
|
|
49
|
+
@entity.send(@reference_name)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return (see Data::Mapper::Attribute#reference_key)
|
|
53
|
+
def reference_key
|
|
54
|
+
@query_method.data_mapper.attributes.first.reference_key
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -27,16 +27,31 @@ module Datamappify
|
|
|
27
27
|
#
|
|
28
28
|
# @return [void]
|
|
29
29
|
def execute(&block)
|
|
30
|
-
@
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
walk_performed(attributes)
|
|
30
|
+
if @query_method && @query_method.writer?
|
|
31
|
+
@attributes.classify(&:source_class).each do |source_class, attributes|
|
|
32
|
+
perform_walk(source_class, attributes, &block)
|
|
34
33
|
end
|
|
34
|
+
else
|
|
35
|
+
perform_walk(@attributes.first.source_class, @attributes, &block)
|
|
35
36
|
end
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
private
|
|
39
40
|
|
|
41
|
+
# @param source_class [Class]
|
|
42
|
+
#
|
|
43
|
+
# @param attributes [Set<Data::Mapper::Attribute>]
|
|
44
|
+
#
|
|
45
|
+
# @yield (see #execute)
|
|
46
|
+
#
|
|
47
|
+
# @return [void]
|
|
48
|
+
def perform_walk(source_class, attributes, &block)
|
|
49
|
+
if do_walk?(source_class, attributes)
|
|
50
|
+
block.call(@provider_name, source_class, attributes)
|
|
51
|
+
walk_performed(attributes)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
40
55
|
# Whether it is necessary to do the walk
|
|
41
56
|
#
|
|
42
57
|
# @param source_class [Class]
|
|
@@ -11,17 +11,24 @@ module Datamappify
|
|
|
11
11
|
# @return [UnitOfWork::PersistentStates]
|
|
12
12
|
attr_reader :states
|
|
13
13
|
|
|
14
|
+
# @return [Entity, Hash, nil]
|
|
15
|
+
attr_reader :entity
|
|
16
|
+
|
|
17
|
+
# @return [Entity, Hash, nil]
|
|
18
|
+
attr_reader :criteria
|
|
19
|
+
|
|
14
20
|
# @param options [Hash]
|
|
15
21
|
# a hash containing required items like data_mapper and states
|
|
16
22
|
#
|
|
17
|
-
# @param
|
|
23
|
+
# @param entity_or_criteria [Entity, Hash]
|
|
18
24
|
#
|
|
19
25
|
# @param args [any]
|
|
20
|
-
def initialize(options,
|
|
26
|
+
def initialize(options, entity_or_criteria = nil, *args)
|
|
21
27
|
@data_mapper = options[:data_mapper]
|
|
22
28
|
@states = options[:states]
|
|
23
29
|
@lazy_load = options[:lazy_load?]
|
|
24
|
-
|
|
30
|
+
|
|
31
|
+
@entity = @criteria = entity_or_criteria
|
|
25
32
|
end
|
|
26
33
|
|
|
27
34
|
# Should the method be aware of the dirty state?
|
|
@@ -52,8 +59,6 @@ module Datamappify
|
|
|
52
59
|
false
|
|
53
60
|
end
|
|
54
61
|
|
|
55
|
-
private
|
|
56
|
-
|
|
57
62
|
# Dispatches a {Criteria} according to
|
|
58
63
|
# the {Data::Mapper data mapper}'s default provider and default source class
|
|
59
64
|
#
|
|
@@ -74,10 +79,28 @@ module Datamappify
|
|
|
74
79
|
#
|
|
75
80
|
# @return [void]
|
|
76
81
|
def dispatch_criteria_to_providers(criteria_name, entity)
|
|
82
|
+
UnitOfWork::Transaction.new(data_mapper) do
|
|
83
|
+
walk_attributes(criteria_name, entity)
|
|
84
|
+
walk_references(criteria_name, entity)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Walks through the references and performs actions on them
|
|
89
|
+
#
|
|
90
|
+
# @param (see #dispatch_criteria_to_providers)
|
|
91
|
+
#
|
|
92
|
+
# @return [void]
|
|
93
|
+
def walk_attributes(criteria_name, entity)
|
|
77
94
|
_primary_record = nil
|
|
78
95
|
|
|
79
96
|
attributes_walker(entity) do |provider_name, source_class, attributes|
|
|
80
|
-
|
|
97
|
+
walk_path = if reader?
|
|
98
|
+
{ nil => attributes }
|
|
99
|
+
else
|
|
100
|
+
attributes.classify { |attr| attr.options[:via] }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
walk_path.each do |via, attrs|
|
|
81
104
|
record = data_mapper.provider(provider_name).build_criteria(
|
|
82
105
|
criteria_name,
|
|
83
106
|
source_class,
|
|
@@ -106,20 +129,38 @@ module Datamappify
|
|
|
106
129
|
#
|
|
107
130
|
# @see SourceAttributesWalker#execute
|
|
108
131
|
def attributes_walker(entity, &block)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
132
|
+
data_mapper.classified_attributes.each do |provider_name, attributes|
|
|
133
|
+
source_attributes_walker.new({
|
|
134
|
+
:entity => entity,
|
|
135
|
+
:provider_name => provider_name,
|
|
136
|
+
:attributes => attributes,
|
|
137
|
+
:dirty_aware? => dirty_aware?,
|
|
138
|
+
:dirty_attributes => states.find(entity).changed,
|
|
139
|
+
:query_method => self
|
|
140
|
+
}).execute(&block)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Walks through the references and performs actions on them
|
|
145
|
+
#
|
|
146
|
+
# @param (see #dispatch_criteria_to_providers)
|
|
147
|
+
#
|
|
148
|
+
# @return [void]
|
|
149
|
+
def walk_references(criteria_name, entity)
|
|
150
|
+
data_mapper.references.each do |reference_name, options|
|
|
151
|
+
ReferenceHandler.new({
|
|
152
|
+
:entity => entity,
|
|
153
|
+
:criteria_name => criteria_name,
|
|
154
|
+
:reference_name => reference_name,
|
|
155
|
+
:reference_options => options,
|
|
156
|
+
:query_method => self
|
|
157
|
+
}).execute
|
|
120
158
|
end
|
|
121
159
|
end
|
|
122
160
|
|
|
161
|
+
private
|
|
162
|
+
|
|
163
|
+
# return [Class]
|
|
123
164
|
def source_attributes_walker
|
|
124
165
|
if @lazy_load
|
|
125
166
|
Lazy::SourceAttributesWalker
|
|
@@ -4,11 +4,11 @@ module Datamappify
|
|
|
4
4
|
class Save < Method
|
|
5
5
|
# @return [Entity, false]
|
|
6
6
|
def perform
|
|
7
|
-
states.update(
|
|
8
|
-
create_or_update(
|
|
7
|
+
states.update(entity) do
|
|
8
|
+
create_or_update(entity)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
entity
|
|
12
12
|
rescue Data::EntityInvalid
|
|
13
13
|
false
|
|
14
14
|
end
|
|
@@ -31,7 +31,7 @@ module Datamappify
|
|
|
31
31
|
#
|
|
32
32
|
# @return [Entity]
|
|
33
33
|
def create_or_update(entity)
|
|
34
|
-
raise Data::EntityInvalid.new(entity) if entity.invalid?(context)
|
|
34
|
+
raise Data::EntityInvalid.new(entity.errors) if entity.invalid?(context)
|
|
35
35
|
|
|
36
36
|
dispatch_criteria_to_providers(:SaveByKey, entity)
|
|
37
37
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Datamappify
|
|
2
|
+
module Repository
|
|
3
|
+
module QueryMethod
|
|
4
|
+
class WhereOrMatch < Method
|
|
5
|
+
# @return [Array<Entity>]
|
|
6
|
+
def perform
|
|
7
|
+
results = dispatch_criteria_to_default_source(
|
|
8
|
+
self.class.name.demodulize.to_sym, data_mapper.entity_class, criteria, data_mapper.attributes
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
MultiResultBlender.new(self).blend(results)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @see Method#reader?
|
|
15
|
+
def reader?
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class Where < WhereOrMatch; end
|
|
21
|
+
class Match < WhereOrMatch; end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|