datamappify 0.70.0.beta1 → 0.70.0.beta2
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/CHANGELOG.md +9 -0
- data/README.md +55 -3
- data/datamappify.gemspec +4 -2
- data/lib/datamappify/data/criteria/active_record/limit.rb +7 -1
- data/lib/datamappify/data/criteria/active_record/save.rb +7 -1
- data/lib/datamappify/data/criteria/common.rb +4 -4
- data/lib/datamappify/data/criteria/relational/count.rb +4 -2
- data/lib/datamappify/data/criteria/sequel/limit.rb +4 -1
- data/lib/datamappify/data/criteria/sequel/save.rb +7 -1
- data/lib/datamappify/entity/association.rb +11 -0
- data/lib/datamappify/entity/association/validation.rb +1 -1
- data/lib/datamappify/entity/compatibility/active_record.rb +13 -0
- data/lib/datamappify/entity/compatibility/association/active_record.rb +19 -3
- data/lib/datamappify/entity/composable/attributes.rb +1 -1
- data/lib/datamappify/extensions/kaminari/collection_methods.rb +16 -0
- data/lib/datamappify/extensions/kaminari/criteria_processor.rb +36 -0
- data/lib/datamappify/lazy/attributes_handler.rb +2 -2
- data/lib/datamappify/repository.rb +9 -0
- data/lib/datamappify/repository/query_method/callbacks.rb +33 -6
- data/lib/datamappify/repository/query_method/count.rb +3 -1
- data/lib/datamappify/repository/query_method/method.rb +8 -5
- data/lib/datamappify/repository/query_method/method/reference_handler.rb +27 -4
- data/lib/datamappify/repository/query_method/method/source_attributes_walker.rb +3 -1
- data/lib/datamappify/repository/query_method/save.rb +1 -6
- data/lib/datamappify/repository/query_method/update.rb +4 -0
- data/lib/datamappify/repository/query_methods.rb +42 -11
- data/lib/datamappify/repository/unit_of_work/persistent_states/object.rb +2 -2
- data/lib/datamappify/version.rb +1 -1
- data/spec/extensions/kaminari_spec.rb +27 -0
- data/spec/repository/associations/has_many_spec.rb +9 -42
- data/spec/repository/associations/has_one_spec.rb +185 -0
- data/spec/repository/callbacks_spec.rb +55 -14
- data/spec/repository/dirty_tracking_spec.rb +0 -1
- data/spec/repository/finders/criteria_spec.rb +1 -0
- data/spec/repository/persistence_spec.rb +21 -0
- data/spec/repository_spec.rb +18 -2
- data/spec/spec_helper.rb +0 -2
- data/spec/support/entities/group.rb +2 -1
- data/spec/support/repositories/active_record/group_repository.rb +2 -1
- data/spec/support/repositories/hero_user_repository.rb +12 -0
- data/spec/support/repositories/sequel/group_repository.rb +2 -1
- data/spec/support/shared/contexts.rb +1 -1
- data/spec/support/shared/examples/data/association_data_records.rb +28 -0
- data/spec/unit/entity/association/reference_spec.rb +4 -4
- data/spec/unit/entity/compatibility/active_record_spec.rb +19 -0
- metadata +46 -17
- data/lib/datamappify/data/criteria/relational/limit.rb +0 -13
- data/spec/support/monkey_patches/database_cleaner.rb +0 -12
@@ -8,7 +8,10 @@ module Datamappify
|
|
8
8
|
klass.class_eval do
|
9
9
|
include Hooks
|
10
10
|
|
11
|
-
define_hooks :
|
11
|
+
define_hooks :before_init, :after_init,
|
12
|
+
:before_load, :after_load,
|
13
|
+
:before_find, :after_find,
|
14
|
+
:before_create, :after_create,
|
12
15
|
:before_update, :after_update,
|
13
16
|
:before_save, :after_save,
|
14
17
|
:before_destroy, :after_destroy,
|
@@ -18,21 +21,45 @@ module Datamappify
|
|
18
21
|
|
19
22
|
# @param entity [Entity]
|
20
23
|
#
|
24
|
+
# @param types (see #_run_callbacks)
|
25
|
+
#
|
26
|
+
# @yield (see #_run_callbacks)
|
27
|
+
#
|
28
|
+
# @return [void]
|
29
|
+
def run_callbacks(entity, *types, &block)
|
30
|
+
_run_callbacks(false, entity, *types, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param criteria [any]
|
34
|
+
#
|
35
|
+
# @param types (see #_run_callbacks)
|
36
|
+
#
|
37
|
+
# @yield (see #_run_callbacks)
|
38
|
+
#
|
39
|
+
# @return [void]
|
40
|
+
def run_callbacks_and_return_entity(criteria, *types, &block)
|
41
|
+
_run_callbacks(true, criteria, *types, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# @param return_yield_value [Boolean]
|
47
|
+
#
|
48
|
+
# @param input [any]
|
49
|
+
#
|
21
50
|
# @param types [Symbol]
|
22
51
|
# e.g. :create, :update, :save or :destroy
|
23
52
|
#
|
24
53
|
# @yield callback
|
25
54
|
#
|
26
55
|
# @return [void]
|
27
|
-
def
|
28
|
-
run_hooks(types, :before,
|
56
|
+
def _run_callbacks(return_yield_value, input, *types, &block)
|
57
|
+
run_hooks(types, :before, input) &&
|
29
58
|
(yield_value = block.call) &&
|
30
|
-
run_hooks(types.reverse, :after,
|
59
|
+
run_hooks(types.reverse, :after, (return_yield_value ? yield_value : input)) &&
|
31
60
|
yield_value
|
32
61
|
end
|
33
62
|
|
34
|
-
private
|
35
|
-
|
36
63
|
# @param types [Array<Symbol]
|
37
64
|
# an array of types (e.g. :create, :update, :save or :destroy)
|
38
65
|
#
|
@@ -4,7 +4,9 @@ module Datamappify
|
|
4
4
|
class Count < Method
|
5
5
|
# @return [Integer]
|
6
6
|
def perform
|
7
|
-
dispatch_criteria_to_default_source(
|
7
|
+
dispatch_criteria_to_default_source(
|
8
|
+
:Count, data_mapper.entity_class, criteria, data_mapper.attributes
|
9
|
+
)
|
8
10
|
end
|
9
11
|
|
10
12
|
# @see Method#reader?
|
@@ -29,6 +29,8 @@ module Datamappify
|
|
29
29
|
@lazy_load = options[:lazy_load?]
|
30
30
|
|
31
31
|
@entity = @criteria = entity_or_criteria
|
32
|
+
|
33
|
+
@criteria.symbolize_keys! if @criteria.is_a?(Hash)
|
32
34
|
end
|
33
35
|
|
34
36
|
# Should the method be aware of the dirty state?
|
@@ -93,11 +95,11 @@ module Datamappify
|
|
93
95
|
def walk_attributes(criteria_name, entity)
|
94
96
|
_primary_record = nil
|
95
97
|
|
96
|
-
attributes_walker(entity) do |provider_name, source_class,
|
98
|
+
attributes_walker(entity) do |provider_name, source_class, options|
|
97
99
|
walk_path = if reader?
|
98
|
-
{ nil => attributes }
|
100
|
+
{ nil => options[:attributes] }
|
99
101
|
else
|
100
|
-
attributes.classify { |attr| attr.options[:via] }
|
102
|
+
options[:attributes].classify { |attr| attr.options[:via] }
|
101
103
|
end
|
102
104
|
|
103
105
|
walk_path.each do |via, attrs|
|
@@ -106,8 +108,9 @@ module Datamappify
|
|
106
108
|
source_class,
|
107
109
|
entity,
|
108
110
|
attrs,
|
109
|
-
:via
|
110
|
-
:primary_record
|
111
|
+
:via => via,
|
112
|
+
:primary_record => _primary_record,
|
113
|
+
:dirty_attributes => options[:dirty_attributes]
|
111
114
|
)
|
112
115
|
|
113
116
|
_primary_record ||= record
|
@@ -21,7 +21,12 @@ module Datamappify
|
|
21
21
|
|
22
22
|
# @return [void]
|
23
23
|
def perform_read
|
24
|
-
|
24
|
+
case reference_type
|
25
|
+
when :one
|
26
|
+
@entity.send("#{@reference_name}=", fetch_referenced_entity)
|
27
|
+
when :many
|
28
|
+
@entity.send("#{@reference_name}=", fetch_referenced_entities)
|
29
|
+
end
|
25
30
|
end
|
26
31
|
|
27
32
|
# @return [void]
|
@@ -39,14 +44,32 @@ module Datamappify
|
|
39
44
|
end
|
40
45
|
end
|
41
46
|
|
42
|
-
# @return [
|
47
|
+
# @return [Symbol]
|
48
|
+
# :one or :many
|
49
|
+
def reference_type
|
50
|
+
reference = @entity.send(:attribute_set).detect do |attr|
|
51
|
+
attr.name == @reference_name
|
52
|
+
end
|
53
|
+
|
54
|
+
case reference
|
55
|
+
when Virtus::Attribute::EmbeddedValue then :one
|
56
|
+
when Virtus::Attribute::Collection then :many
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [Entity]
|
61
|
+
def fetch_referenced_entity
|
62
|
+
@repository.where(reference_key => @entity.id).first
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Array<Entity>]
|
43
66
|
def fetch_referenced_entities
|
44
67
|
@repository.where(reference_key => @entity.id)
|
45
68
|
end
|
46
69
|
|
47
|
-
# @return [Array]
|
70
|
+
# @return [Array<Entity>]
|
48
71
|
def referenced_entities
|
49
|
-
@entity.send(@reference_name)
|
72
|
+
Array.wrap(@entity.send(@reference_name))
|
50
73
|
end
|
51
74
|
|
52
75
|
# @return (see Data::Mapper::Attribute#reference_key)
|
@@ -12,6 +12,8 @@ module Datamappify
|
|
12
12
|
@dirty_aware = options[:dirty_aware?]
|
13
13
|
@dirty_attributes = options[:dirty_attributes]
|
14
14
|
@query_method = options[:query_method]
|
15
|
+
|
16
|
+
@options = options
|
15
17
|
end
|
16
18
|
|
17
19
|
# @yield [provider_name, source_class, attributes]
|
@@ -47,7 +49,7 @@ module Datamappify
|
|
47
49
|
# @return [void]
|
48
50
|
def perform_walk(source_class, attributes, &block)
|
49
51
|
if do_walk?(source_class, attributes)
|
50
|
-
block.call(@provider_name, source_class, attributes)
|
52
|
+
block.call(@provider_name, source_class, @options.merge(:attributes => attributes))
|
51
53
|
walk_performed(attributes)
|
52
54
|
end
|
53
55
|
end
|
@@ -13,11 +13,6 @@ module Datamappify
|
|
13
13
|
false
|
14
14
|
end
|
15
15
|
|
16
|
-
# @see Method#dirty_aware?
|
17
|
-
def dirty_aware?
|
18
|
-
true
|
19
|
-
end
|
20
|
-
|
21
16
|
# @see Method#writer?
|
22
17
|
def writer?
|
23
18
|
true
|
@@ -39,7 +34,7 @@ module Datamappify
|
|
39
34
|
end
|
40
35
|
|
41
36
|
def context
|
42
|
-
self.class.name.demodulize.
|
37
|
+
self.class.name.demodulize.downcase.to_sym
|
43
38
|
end
|
44
39
|
end
|
45
40
|
end
|
@@ -8,6 +8,22 @@ module Datamappify
|
|
8
8
|
def self.included(klass)
|
9
9
|
klass.class_eval do
|
10
10
|
include QueryMethod::Callbacks
|
11
|
+
|
12
|
+
if defined?(Kaminari)
|
13
|
+
require 'datamappify/extensions/kaminari/criteria_processor'
|
14
|
+
include Extensions::Kaminari::CriteriaProcessor
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Initialises the entity so hooks can be attached
|
20
|
+
#
|
21
|
+
# @param entity_class [Class]
|
22
|
+
#
|
23
|
+
# @return [Entity]
|
24
|
+
def init(entity_class)
|
25
|
+
run_callbacks_and_return_entity entity_class, :init do
|
26
|
+
entity_class.new
|
11
27
|
end
|
12
28
|
end
|
13
29
|
|
@@ -17,7 +33,9 @@ module Datamappify
|
|
17
33
|
#
|
18
34
|
# @return [Boolean]
|
19
35
|
def exists?(entity)
|
20
|
-
|
36
|
+
run_callbacks entity, :load, :find do
|
37
|
+
QueryMethod::Exists.new(query_options, entity).perform
|
38
|
+
end
|
21
39
|
end
|
22
40
|
|
23
41
|
# @param criteria [Integer, String]
|
@@ -25,7 +43,9 @@ module Datamappify
|
|
25
43
|
#
|
26
44
|
# @return [Entity, nil]
|
27
45
|
def find(criteria)
|
28
|
-
|
46
|
+
run_callbacks_and_return_entity criteria, :load, :find do
|
47
|
+
QueryMethod::Find.new(query_options, criteria).perform
|
48
|
+
end
|
29
49
|
end
|
30
50
|
|
31
51
|
# @param criteria [Hash]
|
@@ -33,7 +53,9 @@ module Datamappify
|
|
33
53
|
#
|
34
54
|
# @return [Entity]
|
35
55
|
def where(criteria)
|
36
|
-
QueryMethod::Where.new(query_options, criteria).perform
|
56
|
+
QueryMethod::Where.new(query_options, criteria).perform.map do |entity|
|
57
|
+
run_callbacks(entity, :load, :find) { entity }
|
58
|
+
end
|
37
59
|
end
|
38
60
|
|
39
61
|
# @param criteria [Hash]
|
@@ -41,14 +63,18 @@ module Datamappify
|
|
41
63
|
#
|
42
64
|
# @return [Entity]
|
43
65
|
def match(criteria)
|
44
|
-
QueryMethod::Match.new(query_options, criteria).perform
|
66
|
+
QueryMethod::Match.new(query_options, criteria).perform.map do |entity|
|
67
|
+
run_callbacks(entity, :load, :find) { entity }
|
68
|
+
end
|
45
69
|
end
|
46
70
|
|
47
71
|
# Returns a collection of all the entities in the repository
|
48
72
|
#
|
49
73
|
# @return [Array<Entity>]
|
50
74
|
def all
|
51
|
-
QueryMethod::Where.new(query_options, {}).perform
|
75
|
+
QueryMethod::Where.new(query_options, {}).perform.map do |entity|
|
76
|
+
run_callbacks(entity, :load, :find) { entity }
|
77
|
+
end
|
52
78
|
end
|
53
79
|
|
54
80
|
# @param criteria [Hash]
|
@@ -56,7 +82,9 @@ module Datamappify
|
|
56
82
|
#
|
57
83
|
# @return [Array<Entity>]
|
58
84
|
def criteria(criteria)
|
59
|
-
QueryMethod::Criteria.new(query_options, criteria).perform
|
85
|
+
QueryMethod::Criteria.new(query_options, criteria).perform.map do |entity|
|
86
|
+
run_callbacks(entity, :load, :find) { entity }
|
87
|
+
end
|
60
88
|
end
|
61
89
|
|
62
90
|
# @param entity [Entity]
|
@@ -64,7 +92,7 @@ module Datamappify
|
|
64
92
|
#
|
65
93
|
# @return [Entity, false]
|
66
94
|
def create(entity)
|
67
|
-
run_callbacks entity, :save, :create do
|
95
|
+
run_callbacks entity, :load, :save, :create do
|
68
96
|
QueryMethod::Create.new(query_options, entity).perform
|
69
97
|
end
|
70
98
|
end
|
@@ -83,7 +111,7 @@ module Datamappify
|
|
83
111
|
#
|
84
112
|
# @return [Entity, false]
|
85
113
|
def update(entity)
|
86
|
-
run_callbacks entity, :save, :update do
|
114
|
+
run_callbacks entity, :load, :save, :update do
|
87
115
|
QueryMethod::Update.new(query_options, entity).perform
|
88
116
|
end
|
89
117
|
end
|
@@ -118,7 +146,7 @@ module Datamappify
|
|
118
146
|
#
|
119
147
|
# @return [void, false]
|
120
148
|
def destroy(entity)
|
121
|
-
run_callbacks entity, :destroy do
|
149
|
+
run_callbacks entity, :load, :destroy do
|
122
150
|
QueryMethod::Destroy.new(query_options, entity).perform
|
123
151
|
end
|
124
152
|
end
|
@@ -132,9 +160,12 @@ module Datamappify
|
|
132
160
|
destroy(entity) || raise(Data::EntityNotDestroyed.new(entity.errors))
|
133
161
|
end
|
134
162
|
|
163
|
+
# @param criteria [Hash]
|
164
|
+
# a hash containing criteria
|
165
|
+
#
|
135
166
|
# @return [Integer]
|
136
|
-
def count
|
137
|
-
QueryMethod::Count.new(query_options).perform
|
167
|
+
def count(criteria = {})
|
168
|
+
QueryMethod::Count.new(query_options, criteria).perform
|
138
169
|
end
|
139
170
|
|
140
171
|
private
|
@@ -51,7 +51,7 @@ module Datamappify
|
|
51
51
|
#
|
52
52
|
# @return [Boolean]
|
53
53
|
def new?
|
54
|
-
|
54
|
+
!@entity.persisted?
|
55
55
|
end
|
56
56
|
|
57
57
|
private
|
@@ -85,7 +85,7 @@ module Datamappify
|
|
85
85
|
# @return [void]
|
86
86
|
def construct_setter(name)
|
87
87
|
define_singleton_method "#{name}=" do |value|
|
88
|
-
|
88
|
+
mark_as_dirty(name) unless send(name) == value
|
89
89
|
set_value(name, value)
|
90
90
|
end
|
91
91
|
end
|
data/lib/datamappify/version.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for "extensions - Kaminari" do |data_provider|
|
4
|
+
include_context "user repository for finders", data_provider
|
5
|
+
|
6
|
+
context "#{data_provider}" do
|
7
|
+
describe "pagination" do
|
8
|
+
let(:records) { user_repository.criteria(page: 2, per: 1) }
|
9
|
+
subject { records }
|
10
|
+
|
11
|
+
it { should have(1).user }
|
12
|
+
|
13
|
+
its(:current_page) { should == 2 }
|
14
|
+
its(:limit_value) { should == 1 }
|
15
|
+
its(:offset_value) { should == 1 }
|
16
|
+
its(:total_count) { should == 3 }
|
17
|
+
|
18
|
+
its(:first) { should == existing_user_1 }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Datamappify::Repository do
|
24
|
+
DATA_PROVIDERS.each do |data_provider|
|
25
|
+
it_behaves_like "extensions - Kaminari", data_provider
|
26
|
+
end
|
27
|
+
end
|
@@ -108,18 +108,6 @@ shared_examples_for "has_many records from nested form attributes with invalid d
|
|
108
108
|
)
|
109
109
|
end
|
110
110
|
|
111
|
-
context "before saving" do
|
112
|
-
subject { new_group }
|
113
|
-
|
114
|
-
its(:name) { should == 'New People' }
|
115
|
-
its(:users) { should have(3).items }
|
116
|
-
its(:valid?) { should be_false }
|
117
|
-
|
118
|
-
it "does not save" do
|
119
|
-
-> { group_repository.save!(new_group) }.should raise_error(Datamappify::Data::EntityNotSaved)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
111
|
context "non-saved dirty new group" do
|
124
112
|
before do
|
125
113
|
group_repository.save(new_group)
|
@@ -130,6 +118,10 @@ shared_examples_for "has_many records from nested form attributes with invalid d
|
|
130
118
|
its(:name) { should == 'New People' }
|
131
119
|
its(:users) { should have(3).items }
|
132
120
|
its(:valid?) { should be_false }
|
121
|
+
|
122
|
+
it "does not save" do
|
123
|
+
-> { group_repository.save!(new_group) }.should raise_error(Datamappify::Data::EntityNotSaved)
|
124
|
+
end
|
133
125
|
end
|
134
126
|
|
135
127
|
context "fresh copy of the non-saved new group" do
|
@@ -163,35 +155,6 @@ shared_examples_for "has_many records destroy from nested form attributes" do
|
|
163
155
|
its(:users) { should have(1).item }
|
164
156
|
end
|
165
157
|
|
166
|
-
shared_examples_for "has_many data records" do |data_provider|
|
167
|
-
let(:data_groups) { "Datamappify::Data::Record::#{data_provider}::Group".constantize }
|
168
|
-
let(:data_super_users) { "Datamappify::Data::Record::#{data_provider}::SuperUser".constantize }
|
169
|
-
let(:data_users) { "Datamappify::Data::Record::#{data_provider}::User".constantize }
|
170
|
-
let(:data_user_driver_licenses) { "Datamappify::Data::Record::#{data_provider}::UserDriverLicense".constantize }
|
171
|
-
|
172
|
-
before do
|
173
|
-
saved_group
|
174
|
-
end
|
175
|
-
|
176
|
-
describe "data records" do
|
177
|
-
it "does not create extra primary data records" do
|
178
|
-
expect { saved_group }.to change { data_groups.count }.by(0)
|
179
|
-
end
|
180
|
-
|
181
|
-
it "does not create extra associated primary data records" do
|
182
|
-
expect { saved_group }.to change { data_super_users.count }.by(0)
|
183
|
-
end
|
184
|
-
|
185
|
-
it "does not create extra associated secondary data records" do
|
186
|
-
expect { saved_group }.to change { data_users.count }.by(0)
|
187
|
-
end
|
188
|
-
|
189
|
-
it "does not create extra secondary data records" do
|
190
|
-
expect { saved_group }.to change { data_user_driver_licenses.count }.by(0)
|
191
|
-
end
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
158
|
shared_examples_for "has_many" do |data_provider|
|
196
159
|
let(:user_repository) { "SuperUserRepository#{data_provider}".constantize }
|
197
160
|
let(:group_repository) { "GroupRepository#{data_provider}".constantize }
|
@@ -223,25 +186,29 @@ shared_examples_for "has_many" do |data_provider|
|
|
223
186
|
it_behaves_like "has_many new records created from nested form attributes"
|
224
187
|
it_behaves_like "has_many records created from nested form attributes"
|
225
188
|
it_behaves_like "has_many records from nested form attributes with invalid data"
|
226
|
-
it_behaves_like "
|
189
|
+
it_behaves_like "association data records", data_provider
|
227
190
|
end
|
228
191
|
|
229
192
|
context "reloaded return" do
|
230
193
|
let(:saved_group) { group_repository.save!(group); group_repository.find(group.id) }
|
231
194
|
|
232
195
|
it_behaves_like "has_many records"
|
196
|
+
it_behaves_like "has_many new records created from nested form attributes"
|
233
197
|
it_behaves_like "has_many records created from nested form attributes"
|
234
198
|
it_behaves_like "has_many records from nested form attributes with invalid data"
|
235
199
|
it_behaves_like "has_many records destroy from nested form attributes"
|
200
|
+
it_behaves_like "association data records", data_provider
|
236
201
|
end
|
237
202
|
|
238
203
|
context "collection return" do
|
239
204
|
let(:saved_group) { group_repository.save!(group); group_repository.all.detect { |g| g.id == group.id } }
|
240
205
|
|
241
206
|
it_behaves_like "has_many records"
|
207
|
+
it_behaves_like "has_many new records created from nested form attributes"
|
242
208
|
it_behaves_like "has_many records created from nested form attributes"
|
243
209
|
it_behaves_like "has_many records from nested form attributes with invalid data"
|
244
210
|
it_behaves_like "has_many records destroy from nested form attributes"
|
211
|
+
it_behaves_like "association data records", data_provider
|
245
212
|
end
|
246
213
|
|
247
214
|
context "criteria return" do
|