datamappify 0.60.0 → 0.70.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -1
  3. data/CHANGELOG.md +20 -0
  4. data/README.md +120 -3
  5. data/datamappify.gemspec +3 -3
  6. data/lib/datamappify/data/criteria/active_record/count.rb +0 -2
  7. data/lib/datamappify/data/criteria/active_record/criteria.rb +10 -0
  8. data/lib/datamappify/data/criteria/active_record/criteria_method.rb +65 -0
  9. data/lib/datamappify/data/criteria/active_record/find.rb +10 -2
  10. data/lib/datamappify/data/criteria/active_record/find_by_key.rb +4 -2
  11. data/lib/datamappify/data/criteria/active_record/limit.rb +10 -0
  12. data/lib/datamappify/data/criteria/active_record/match.rb +40 -0
  13. data/lib/datamappify/data/criteria/active_record/order.rb +41 -0
  14. data/lib/datamappify/data/criteria/active_record/rollback.rb +13 -0
  15. data/lib/datamappify/data/criteria/active_record/save.rb +1 -4
  16. data/lib/datamappify/data/criteria/active_record/save_by_key.rb +0 -1
  17. data/lib/datamappify/data/criteria/active_record/where.rb +18 -0
  18. data/lib/datamappify/data/criteria/common.rb +5 -2
  19. data/lib/datamappify/data/criteria/concerns/update_primary_record.rb +2 -0
  20. data/lib/datamappify/data/criteria/relational/concerns/find.rb +35 -0
  21. data/lib/datamappify/data/criteria/relational/concerns/find_by_key.rb +19 -0
  22. data/lib/datamappify/data/criteria/relational/criteria.rb +29 -0
  23. data/lib/datamappify/data/criteria/relational/{find_multiple.rb → criteria_base_method.rb} +2 -28
  24. data/lib/datamappify/data/criteria/relational/criteria_method.rb +44 -0
  25. data/lib/datamappify/data/criteria/relational/limit.rb +13 -0
  26. data/lib/datamappify/data/criteria/relational/save.rb +12 -0
  27. data/lib/datamappify/data/criteria/sequel/count.rb +0 -2
  28. data/lib/datamappify/data/criteria/sequel/criteria.rb +10 -0
  29. data/lib/datamappify/data/criteria/sequel/criteria_method.rb +51 -0
  30. data/lib/datamappify/data/criteria/sequel/find.rb +10 -2
  31. data/lib/datamappify/data/criteria/sequel/find_by_key.rb +4 -2
  32. data/lib/datamappify/data/criteria/sequel/limit.rb +10 -0
  33. data/lib/datamappify/data/criteria/sequel/match.rb +22 -0
  34. data/lib/datamappify/data/criteria/sequel/order.rb +22 -0
  35. data/lib/datamappify/data/criteria/sequel/rollback.rb +13 -0
  36. data/lib/datamappify/data/criteria/sequel/save.rb +1 -4
  37. data/lib/datamappify/data/criteria/sequel/save_by_key.rb +0 -1
  38. data/lib/datamappify/data/criteria/sequel/where.rb +18 -0
  39. data/lib/datamappify/data/errors.rb +8 -9
  40. data/lib/datamappify/data/mapper/attribute.rb +13 -33
  41. data/lib/datamappify/data/mapper.rb +13 -2
  42. data/lib/datamappify/data/provider/active_record.rb +8 -6
  43. data/lib/datamappify/data/provider/common_provider.rb +2 -2
  44. data/lib/datamappify/data/provider/sequel.rb +9 -6
  45. data/lib/datamappify/entity/association/reference.rb +63 -0
  46. data/lib/datamappify/entity/association/validation.rb +59 -0
  47. data/lib/datamappify/entity/association.rb +44 -0
  48. data/lib/datamappify/entity/{active_model/compatibility.rb → compatibility/active_model.rb} +2 -2
  49. data/lib/datamappify/entity/compatibility/active_record.rb +22 -0
  50. data/lib/datamappify/entity/compatibility/association/active_record.rb +43 -0
  51. data/lib/datamappify/entity/composable/attributes.rb +8 -16
  52. data/lib/datamappify/entity.rb +8 -6
  53. data/lib/datamappify/lazy/attributes_handler.rb +1 -1
  54. data/lib/datamappify/lazy.rb +0 -1
  55. data/lib/datamappify/repository/mapping_dsl.rb +9 -0
  56. data/lib/datamappify/repository/query_method/criteria.rb +21 -0
  57. data/lib/datamappify/repository/query_method/destroy.rb +1 -1
  58. data/lib/datamappify/repository/query_method/exists.rb +1 -1
  59. data/lib/datamappify/repository/query_method/find.rb +1 -9
  60. data/lib/datamappify/repository/query_method/method/multi_result_blender.rb +24 -0
  61. data/lib/datamappify/repository/query_method/method/reference_handler.rb +60 -0
  62. data/lib/datamappify/repository/query_method/method/source_attributes_walker.rb +19 -4
  63. data/lib/datamappify/repository/query_method/method.rb +58 -17
  64. data/lib/datamappify/repository/query_method/save.rb +4 -4
  65. data/lib/datamappify/repository/query_method/where_or_match.rb +24 -0
  66. data/lib/datamappify/repository/query_methods.rb +21 -5
  67. data/lib/datamappify/repository/unit_of_work/persistent_states/object.rb +2 -2
  68. data/lib/datamappify/version.rb +1 -1
  69. data/spec/entity/{relation_spec.rb → association/reference_spec.rb} +2 -2
  70. data/spec/repository/associations/has_many_spec.rb +259 -0
  71. data/spec/repository/dirty_tracking_spec.rb +68 -57
  72. data/spec/repository/finders/all_spec.rb +29 -0
  73. data/spec/repository/finders/criteria_spec.rb +61 -0
  74. data/spec/repository/finders/different_providers_spec.rb +81 -0
  75. data/spec/repository/finders/find_spec.rb +21 -0
  76. data/spec/repository/finders/match_spec.rb +28 -0
  77. data/spec/repository/finders/where_spec.rb +18 -0
  78. data/spec/repository/reverse_mapped_spec.rb +20 -4
  79. data/spec/support/entities/group.rb +4 -0
  80. data/spec/support/entities/super_user.rb +9 -0
  81. data/spec/support/entities/user.rb +5 -0
  82. data/spec/support/entities/user_info.rb +5 -0
  83. data/spec/support/repositories/active_record/group_repository.rb +4 -0
  84. data/spec/support/repositories/active_record/super_user_repository.rb +23 -0
  85. data/spec/support/repositories/active_record/user_repository.rb +6 -4
  86. data/spec/support/repositories/sequel/group_repository.rb +4 -0
  87. data/spec/support/repositories/sequel/super_user_repository.rb +23 -0
  88. data/spec/support/repositories/sequel/user_repository.rb +6 -4
  89. data/spec/support/shared/contexts.rb +8 -1
  90. data/spec/support/shared/examples/repository/finders/where_or_match.rb +130 -0
  91. data/spec/support/tables/active_record.rb +20 -10
  92. data/spec/support/tables/sequel.rb +20 -7
  93. data/spec/unit/entity/{relation_spec.rb → association/reference_spec.rb} +5 -5
  94. data/spec/unit/repository/query_method_spec.rb +9 -2
  95. metadata +74 -42
  96. data/lib/datamappify/data/criteria/active_record/find_multiple.rb +0 -40
  97. data/lib/datamappify/data/criteria/relational/find.rb +0 -25
  98. data/lib/datamappify/data/criteria/relational/find_by_key.rb +0 -18
  99. data/lib/datamappify/data/criteria/sequel/find_multiple.rb +0 -43
  100. data/lib/datamappify/entity/relation.rb +0 -61
  101. data/lib/datamappify/repository/query_method/find_multiple.rb +0 -28
  102. 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
@@ -1,8 +1,8 @@
1
1
  module Datamappify
2
2
  module Entity
3
- module ActiveModel
3
+ module Compatibility
4
4
  # Add non-entity related stuff so that an entity can be used in say, forms
5
- module Compatibility
5
+ module ActiveModel
6
6
  # @return [Boolean]
7
7
  def persisted?
8
8
  !id.blank?
@@ -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.name, attribute.writer)
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 << :id
27
+ @excluded_attributes ||= @entity_class.reference_keys + [:id, *@entity_class::IGNORED_ATTRIBUTE_NAMES]
28
28
  end
29
29
 
30
- # @param attribute_name [Symbol]
31
- #
32
- # @param attribute_writer [Virtus::Attribute::Writer]
30
+ # @param attribute [Virtus::Attribute]
33
31
  #
34
32
  # @return [Virtus::Attribute]
35
- def build_attribute(attribute_name, attribute_writer)
36
- name = if @options[:prefix_with]
37
- Attribute.prefix(attribute_name, @options[:prefix_with])
33
+ def build_attribute(attribute)
34
+ attribute_name = if @options[:prefix_with]
35
+ Attribute.prefix(attribute.name, @options[:prefix_with])
38
36
  else
39
- attribute_name
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
@@ -1,9 +1,10 @@
1
1
  require 'observer'
2
2
  require 'virtus'
3
- require 'datamappify/entity/active_model/compatibility'
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/relation'
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::Compatibility
16
- include Virtus
17
- include Virtus::Equalizer.new(inspect)
18
+ include Compatibility::ActiveModel
19
+ include Compatibility::ActiveRecord
18
20
  include LazyChecking
19
21
  include Composable
20
- include Relation
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
@@ -1,5 +1,4 @@
1
1
  require 'datamappify/lazy/source_attributes_walker'
2
- require 'datamappify/lazy/source_attributes_walker'
3
2
  require 'datamappify/lazy/attributes_handler'
4
3
 
5
4
  module Datamappify
@@ -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
@@ -4,7 +4,7 @@ module Datamappify
4
4
  class Destroy < Method
5
5
  # @return [void, false]
6
6
  def perform
7
- dispatch_criteria_to_default_source(:Destroy, @entity.id)
7
+ dispatch_criteria_to_default_source(:Destroy, entity.id)
8
8
  end
9
9
 
10
10
  # @see Method#writer?
@@ -4,7 +4,7 @@ module Datamappify
4
4
  class Exists < Method
5
5
  # @return [Boolean]
6
6
  def perform
7
- dispatch_criteria_to_default_source(:Exists, @entity)
7
+ dispatch_criteria_to_default_source(:Exists, entity)
8
8
  end
9
9
 
10
10
  # @see Method#reader?
@@ -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 = @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
- @attributes.classify(&:source_class).each do |source_class, attributes|
31
- if do_walk?(source_class, attributes)
32
- block.call(@provider_name, source_class, attributes)
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 entity [Entity]
23
+ # @param entity_or_criteria [Entity, Hash]
18
24
  #
19
25
  # @param args [any]
20
- def initialize(options, entity = nil, *args)
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
- @entity = entity
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
- attributes.classify { |attr| attr.options[:via] }.each do |via, attrs|
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
- UnitOfWork::Transaction.new(data_mapper) do
110
- data_mapper.classified_attributes.each do |provider_name, attributes|
111
- source_attributes_walker.new({
112
- :entity => entity,
113
- :provider_name => provider_name,
114
- :attributes => attributes,
115
- :dirty_aware? => dirty_aware?,
116
- :dirty_attributes => states.find(entity).changed,
117
- :query_method => self
118
- }).execute(&block)
119
- end
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(@entity) do
8
- create_or_update(@entity)
7
+ states.update(entity) do
8
+ create_or_update(entity)
9
9
  end
10
10
 
11
- @entity
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