datamappify 0.70.0.beta1 → 0.70.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +55 -3
  4. data/datamappify.gemspec +4 -2
  5. data/lib/datamappify/data/criteria/active_record/limit.rb +7 -1
  6. data/lib/datamappify/data/criteria/active_record/save.rb +7 -1
  7. data/lib/datamappify/data/criteria/common.rb +4 -4
  8. data/lib/datamappify/data/criteria/relational/count.rb +4 -2
  9. data/lib/datamappify/data/criteria/sequel/limit.rb +4 -1
  10. data/lib/datamappify/data/criteria/sequel/save.rb +7 -1
  11. data/lib/datamappify/entity/association.rb +11 -0
  12. data/lib/datamappify/entity/association/validation.rb +1 -1
  13. data/lib/datamappify/entity/compatibility/active_record.rb +13 -0
  14. data/lib/datamappify/entity/compatibility/association/active_record.rb +19 -3
  15. data/lib/datamappify/entity/composable/attributes.rb +1 -1
  16. data/lib/datamappify/extensions/kaminari/collection_methods.rb +16 -0
  17. data/lib/datamappify/extensions/kaminari/criteria_processor.rb +36 -0
  18. data/lib/datamappify/lazy/attributes_handler.rb +2 -2
  19. data/lib/datamappify/repository.rb +9 -0
  20. data/lib/datamappify/repository/query_method/callbacks.rb +33 -6
  21. data/lib/datamappify/repository/query_method/count.rb +3 -1
  22. data/lib/datamappify/repository/query_method/method.rb +8 -5
  23. data/lib/datamappify/repository/query_method/method/reference_handler.rb +27 -4
  24. data/lib/datamappify/repository/query_method/method/source_attributes_walker.rb +3 -1
  25. data/lib/datamappify/repository/query_method/save.rb +1 -6
  26. data/lib/datamappify/repository/query_method/update.rb +4 -0
  27. data/lib/datamappify/repository/query_methods.rb +42 -11
  28. data/lib/datamappify/repository/unit_of_work/persistent_states/object.rb +2 -2
  29. data/lib/datamappify/version.rb +1 -1
  30. data/spec/extensions/kaminari_spec.rb +27 -0
  31. data/spec/repository/associations/has_many_spec.rb +9 -42
  32. data/spec/repository/associations/has_one_spec.rb +185 -0
  33. data/spec/repository/callbacks_spec.rb +55 -14
  34. data/spec/repository/dirty_tracking_spec.rb +0 -1
  35. data/spec/repository/finders/criteria_spec.rb +1 -0
  36. data/spec/repository/persistence_spec.rb +21 -0
  37. data/spec/repository_spec.rb +18 -2
  38. data/spec/spec_helper.rb +0 -2
  39. data/spec/support/entities/group.rb +2 -1
  40. data/spec/support/repositories/active_record/group_repository.rb +2 -1
  41. data/spec/support/repositories/hero_user_repository.rb +12 -0
  42. data/spec/support/repositories/sequel/group_repository.rb +2 -1
  43. data/spec/support/shared/contexts.rb +1 -1
  44. data/spec/support/shared/examples/data/association_data_records.rb +28 -0
  45. data/spec/unit/entity/association/reference_spec.rb +4 -4
  46. data/spec/unit/entity/compatibility/active_record_spec.rb +19 -0
  47. metadata +46 -17
  48. data/lib/datamappify/data/criteria/relational/limit.rb +0 -13
  49. 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 :before_create, :after_create,
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 run_callbacks(entity, *types, &block)
28
- run_hooks(types, :before, entity) &&
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, entity) &&
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(:Count)
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, attributes|
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 => via,
110
- :primary_record => _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
- @entity.send("#{@reference_name}=", fetch_referenced_entities)
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 [Array]
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.underscore.to_sym
37
+ self.class.name.demodulize.downcase.to_sym
43
38
  end
44
39
  end
45
40
  end
@@ -4,6 +4,10 @@ module Datamappify
4
4
  module Repository
5
5
  module QueryMethod
6
6
  class Update < Save
7
+ # @see Method#dirty_aware?
8
+ def dirty_aware?
9
+ true
10
+ end
7
11
  end
8
12
  end
9
13
  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
- QueryMethod::Exists.new(query_options, entity).perform
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
- QueryMethod::Find.new(query_options, criteria).perform
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
- @entity.id.nil?
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
- send(:attribute_will_change!, name) unless send(name) == value
88
+ mark_as_dirty(name) unless send(name) == value
89
89
  set_value(name, value)
90
90
  end
91
91
  end
@@ -1,3 +1,3 @@
1
1
  module Datamappify
2
- VERSION = '0.70.0.beta1'
2
+ VERSION = '0.70.0.beta2'
3
3
  end
@@ -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 "has_many data records", data_provider
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