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.
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
@@ -0,0 +1,185 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for "has_one records" do
4
+ subject { saved_group }
5
+
6
+ its(:name) { should == 'People' }
7
+ its(:leader) { should be_kind_of(SuperUser) }
8
+
9
+ context "leader" do
10
+ subject { saved_group.leader }
11
+
12
+ its(:id) { should == existing_user.id }
13
+ its(:first_name) { should == 'Steve' }
14
+ its(:driver_license) { should == 'APPLECOMPUTER' }
15
+ its(:group_id) { should == saved_group.id }
16
+ end
17
+ end
18
+
19
+ shared_examples_for "has_one new records created from nested form attributes" do
20
+ let(:group) do
21
+ Group.new(
22
+ :name => 'People',
23
+ :leader_attributes => {
24
+ 'first_name' => 'Bill', 'driver_license' => 'NEXTCOMPUTER', 'id' => ''
25
+ }
26
+ )
27
+ end
28
+
29
+ subject { saved_group.leader }
30
+
31
+ its(:id) { should_not be_nil }
32
+ its(:id) { should_not == existing_user.id }
33
+ its(:first_name) { should == 'Bill' }
34
+ its(:driver_license) { should == 'NEXTCOMPUTER' }
35
+ its(:group_id) { should == saved_group.id }
36
+ end
37
+
38
+ shared_examples_for "has_one records created from nested form attributes" do
39
+ let(:group) do
40
+ Group.new(
41
+ :name => 'People',
42
+ :leader => existing_user,
43
+ :leader_attributes => {
44
+ 'first_name' => 'Bill', 'driver_license' => 'NEXTCOMPUTER'
45
+ }
46
+ )
47
+ end
48
+
49
+ subject { saved_group.leader }
50
+
51
+ its(:id) { should == existing_user.id }
52
+ its(:first_name) { should == 'Bill' }
53
+ its(:driver_license) { should == 'NEXTCOMPUTER' }
54
+ its(:group_id) { should == saved_group.id }
55
+ end
56
+
57
+ shared_examples_for "has_one records from nested form attributes with invalid data" do
58
+ let(:group) do
59
+ Group.new(
60
+ :name => 'People',
61
+ :leader => existing_user
62
+ )
63
+ end
64
+
65
+ let(:new_group) do
66
+ Group.new(
67
+ :id => saved_group.id,
68
+ :name => 'New People',
69
+ :leader => existing_user,
70
+ :leader_attributes => {
71
+ 'first_name' => 'Bill', 'driver_license' => 'JEFF'
72
+ }
73
+ )
74
+ end
75
+
76
+ context "non-saved dirty new group" do
77
+ subject { new_group }
78
+
79
+ its(:name) { should == 'New People' }
80
+ its(:leader) { should be_kind_of(SuperUser) }
81
+ its(:valid?) { should be_false }
82
+
83
+ it "does not save" do
84
+ -> { group_repository.save!(new_group) }.should raise_error(Datamappify::Data::EntityNotSaved)
85
+ end
86
+ end
87
+
88
+ context "fresh copy of the non-saved new group" do
89
+ before do
90
+ group_repository.save(new_group)
91
+ end
92
+
93
+ subject { group_repository.find(new_group.id) }
94
+
95
+ its(:name) { should == 'People' }
96
+ its(:leader) { should be_kind_of(SuperUser) }
97
+ its(:valid?) { should be_true }
98
+ end
99
+ end
100
+
101
+ shared_examples_for "has_one records destroy from nested form attributes" do
102
+ let(:group) do
103
+ Group.new(
104
+ :name => 'People',
105
+ :leader => existing_user,
106
+ :leader_attributes => {
107
+ 'id' => existing_user.id.to_s, 'first_name' => 'Jeff', 'driver_license' => 'NEXTCOMPUTER', '_destroy' => '1'
108
+ }
109
+ )
110
+ end
111
+
112
+ subject { saved_group }
113
+
114
+ its(:leader) { should be_nil }
115
+ end
116
+
117
+ shared_examples_for "has_one" do |data_provider|
118
+ let(:user_repository) { "SuperUserRepository#{data_provider}".constantize }
119
+ let(:group_repository) { "GroupRepository#{data_provider}".constantize }
120
+ let(:new_user) { SuperUser.new(:first_name => 'Fred', :driver_license => 'MOSDEVOPS') }
121
+ let(:existing_user) { user_repository.save! SuperUser.new(:first_name => 'Steve', :driver_license => 'APPLECOMPUTER') }
122
+ let(:existing_user_2) { user_repository.save! SuperUser.new(:first_name => 'Bill', :driver_license => 'MICROCOMPUTER') }
123
+ let(:group) do
124
+ Group.new(
125
+ :name => 'People',
126
+ :leader => existing_user
127
+ )
128
+ end
129
+
130
+ it "existing_user" do
131
+ existing_user.id.should_not be_nil
132
+ end
133
+
134
+ describe "group entity" do
135
+ subject { group }
136
+
137
+ its(:leader) { should be_kind_of(SuperUser) }
138
+ end
139
+
140
+ context "#{data_provider}" do
141
+ context "immediate return" do
142
+ let(:saved_group) { group_repository.save!(group) }
143
+
144
+ it_behaves_like "has_one records"
145
+ it_behaves_like "has_one new records created from nested form attributes"
146
+ it_behaves_like "has_one records created from nested form attributes"
147
+ it_behaves_like "has_one records from nested form attributes with invalid data"
148
+ it_behaves_like "association data records", data_provider
149
+ end
150
+
151
+ context "reloaded return" do
152
+ let(:saved_group) { group_repository.save!(group); group_repository.find(group.id) }
153
+
154
+ it_behaves_like "has_one records"
155
+ it_behaves_like "has_one new records created from nested form attributes"
156
+ it_behaves_like "has_one records created from nested form attributes"
157
+ it_behaves_like "has_one records from nested form attributes with invalid data"
158
+ it_behaves_like "has_one records destroy from nested form attributes"
159
+ it_behaves_like "association data records", data_provider
160
+ end
161
+
162
+ context "collection return" do
163
+ let(:saved_group) { group_repository.save!(group); group_repository.all.detect { |g| g.id == group.id } }
164
+
165
+ it_behaves_like "has_one records"
166
+ it_behaves_like "has_one new records created from nested form attributes"
167
+ it_behaves_like "has_one records created from nested form attributes"
168
+ it_behaves_like "has_one records from nested form attributes with invalid data"
169
+ it_behaves_like "has_one records destroy from nested form attributes"
170
+ it_behaves_like "association data records", data_provider
171
+ end
172
+
173
+ context "criteria return" do
174
+ let(:saved_group) { group_repository.save!(group); group_repository.criteria(:where => { :id => group.id }).first }
175
+
176
+ it_behaves_like "has_one records"
177
+ end
178
+ end
179
+ end
180
+
181
+ describe Datamappify::Repository do
182
+ DATA_PROVIDERS.each do |data_provider|
183
+ it_behaves_like "has_one", data_provider
184
+ end
185
+ end
@@ -8,6 +8,9 @@ shared_examples_for "callbacks" do |query_method, *performed_callbacks|
8
8
  end
9
9
 
10
10
  all_callbacks = [
11
+ :before_init, :after_init,
12
+ :before_load, :after_load,
13
+ :before_find, :after_find,
11
14
  :before_create, :after_create,
12
15
  :before_update, :after_update,
13
16
  :before_save, :after_save,
@@ -36,21 +39,59 @@ describe Datamappify::Repository do
36
39
  it { valid_user.valid?.should == true }
37
40
  it { invalid_user.valid?.should == false }
38
41
 
42
+ context "#init" do
43
+ let(:criteria) { HeroUser }
44
+ let(:entity) { HeroUser.new }
45
+
46
+ it "returns entity instead of criteria" do
47
+ HeroUserRepository.instance.should_receive(:performed).with(:before_init, criteria)
48
+ HeroUserRepository.instance.should_receive(:performed).with(:after_init, entity)
49
+
50
+ HeroUserRepository.init(criteria)
51
+ end
52
+ end
53
+
54
+ context "#find" do
55
+ context "found" do
56
+ let(:criteria) { valid_user.id }
57
+ let(:entity) { valid_user }
58
+
59
+ before do
60
+ HeroUserRepository.save!(valid_user)
61
+ end
62
+
63
+ it "returns entity instead of criteria" do
64
+ HeroUserRepository.instance.should_receive(:performed).with(:before_load, criteria)
65
+ HeroUserRepository.instance.should_receive(:performed).with(:before_find, criteria)
66
+ HeroUserRepository.instance.should_receive(:performed).with(:after_find, entity)
67
+ HeroUserRepository.instance.should_receive(:performed).with(:after_load, entity)
68
+
69
+ HeroUserRepository.find(criteria)
70
+ end
71
+ end
72
+
73
+ context "not found" do
74
+ let(:entity) { 42 }
75
+
76
+ it_behaves_like "callbacks", :find, :before_load, :before_find
77
+ end
78
+ end
79
+
39
80
  context "non-persisted" do
40
81
  context "on valid entity" do
41
82
  let(:entity) { valid_user }
42
83
 
43
- it_behaves_like "callbacks", :create, :before_save, :before_create, :before_create_2, :before_create_block, :after_create, :after_save
44
- it_behaves_like "callbacks", :update, :before_save, :before_update, :after_update, :after_save
45
- it_behaves_like "callbacks", :save, :before_save, :before_create, :before_create_2, :before_create_block, :after_create, :after_save
84
+ it_behaves_like "callbacks", :create, :before_load, :before_save, :before_create, :before_create_2, :before_create_block, :after_create, :after_save, :after_load
85
+ it_behaves_like "callbacks", :update, :before_load, :before_save, :before_update, :after_update, :after_save, :after_load
86
+ it_behaves_like "callbacks", :save, :before_load, :before_find, :before_load, :before_save, :before_create, :before_create_2, :before_create_block, :after_create, :after_save, :after_load
46
87
  end
47
88
 
48
89
  context "on invalid entity" do
49
90
  let(:entity) { invalid_user }
50
91
 
51
- it_behaves_like "callbacks", :create, :before_save, :before_create, :before_create_2, :before_create_block
52
- it_behaves_like "callbacks", :update, :before_save, :before_update
53
- it_behaves_like "callbacks", :save, :before_save, :before_create, :before_create_2, :before_create_block
92
+ it_behaves_like "callbacks", :create, :before_load, :before_save, :before_create, :before_create_2, :before_create_block
93
+ it_behaves_like "callbacks", :update, :before_load, :before_save, :before_update
94
+ it_behaves_like "callbacks", :save, :before_load, :before_find, :before_load, :before_save, :before_create, :before_create_2, :before_create_block
54
95
  end
55
96
  end
56
97
 
@@ -60,19 +101,19 @@ describe Datamappify::Repository do
60
101
  context "on valid entity" do
61
102
  let(:entity) { persisted_user }
62
103
 
63
- it_behaves_like "callbacks", :create, :before_save, :before_create, :before_create_2, :before_create_block, :after_create, :after_save
64
- it_behaves_like "callbacks", :update, :before_save, :before_update, :after_update, :after_save
65
- it_behaves_like "callbacks", :save, :before_save, :before_update, :after_update, :after_save
66
- it_behaves_like "callbacks", :destroy, :before_destroy, :after_destroy
104
+ it_behaves_like "callbacks", :create, :before_load, :before_save, :before_create, :before_create_2, :before_create_block, :after_create, :after_save, :after_load
105
+ it_behaves_like "callbacks", :update, :before_load, :before_save, :before_update, :after_update, :after_save, :after_load
106
+ it_behaves_like "callbacks", :save, :before_load, :before_find, :after_find, :after_load, :before_load, :before_save, :before_update, :after_update, :after_save, :after_load
107
+ it_behaves_like "callbacks", :destroy, :before_load, :before_destroy, :after_destroy, :after_load
67
108
  end
68
109
 
69
110
  context "on invalid entity" do
70
111
  let(:entity) { persisted_user.tap { |u| u.first_name = 'f' } }
71
112
 
72
- it_behaves_like "callbacks", :create, :before_save, :before_create, :before_create_2, :before_create_block
73
- it_behaves_like "callbacks", :update, :before_save, :before_update
74
- it_behaves_like "callbacks", :save, :before_save, :before_update
75
- it_behaves_like "callbacks", :destroy, :before_destroy, :after_destroy
113
+ it_behaves_like "callbacks", :create, :before_load, :before_save, :before_create, :before_create_2, :before_create_block
114
+ it_behaves_like "callbacks", :update, :before_load, :before_save, :before_update
115
+ it_behaves_like "callbacks", :save, :before_load, :before_find, :after_find, :after_load, :before_load, :before_save, :before_update
116
+ it_behaves_like "callbacks", :destroy, :before_load, :before_destroy, :after_destroy, :after_load
76
117
  end
77
118
  end
78
119
 
@@ -38,7 +38,6 @@ shared_examples_for "dirty tracking" do |data_provider|
38
38
  end
39
39
 
40
40
  describe "change by mutation" do
41
-
42
41
  it "changed" do
43
42
  existing_user.first_name << 'APPEND'
44
43
  state = user_repository.states.find(existing_user)
@@ -3,6 +3,7 @@ require 'spec_helper'
3
3
  shared_examples_for "finders examples" do |data_provider|
4
4
  [
5
5
  { :where => { :last_name => 'Superman' }, :limit => 2, :order => { :last_name => :asc, :first_name => :desc, :id => :desc } },
6
+ { :where => { :last_name => 'Superman' }, :limit => [2, 0], :order => { :last_name => :asc, :first_name => :desc, :id => :desc } },
6
7
  { :order => { :last_name => :asc, :first_name => :desc, :id => :desc }, :limit => 2, :where => { :last_name => 'Superman' } },
7
8
  { :where => {}, :match => { :last_name => 'Super%' }, :limit => 2, :order => { :last_name => :asc, :first_name => :desc, :id => :desc } }
8
9
  ].each_with_index do |criteria, index|
@@ -52,6 +52,27 @@ shared_examples_for "repository persistence" do |data_provider|
52
52
  persisted_user.persisted?.should == true
53
53
  end
54
54
 
55
+ it "updates existing records with nil values" do
56
+ existing_user.first_name = 'Vivian'
57
+ existing_user.last_name = nil
58
+ existing_user.health_care = nil
59
+
60
+ updated_user = nil
61
+
62
+ expect { updated_user = user_repository.send(query_method, existing_user) }.to change { user_repository.count }.by(0)
63
+
64
+ updated_user.first_name.should == 'Vivian'
65
+ updated_user.last_name.should be_nil
66
+ updated_user.health_care.should be_nil
67
+
68
+ persisted_user = user_repository.find(updated_user.id)
69
+
70
+ persisted_user.first_name.should == 'Vivian'
71
+ persisted_user.last_name.should be_nil
72
+ persisted_user.health_care.should be_nil
73
+ persisted_user.persisted?.should == true
74
+ end
75
+
55
76
  it "updates existing and new records" do
56
77
  existing_user.first_name = 'Vivian'
57
78
  existing_user.health_care = 'BATMANCAVE'
@@ -10,8 +10,24 @@ shared_examples_for "a repository" do |data_provider|
10
10
  namespace.const_defined?(:User, false).should == true
11
11
  end
12
12
 
13
- it "delegates methods to the instance singleton" do
14
- expect { "UserRepository#{data_provider}".constantize.find(1) }.to_not raise_error
13
+ describe "delegation" do
14
+ subject { "UserRepository#{data_provider}".constantize }
15
+
16
+ it "delegates methods to the instance singleton" do
17
+ expect { subject.find(1) }.to_not raise_error
18
+ end
19
+
20
+ describe "catch-all delegation" do
21
+ before do
22
+ user_repository.class_eval do
23
+ attr_reader :dummy_method
24
+ end
25
+ end
26
+
27
+ it "delegates other methods to the instance singleton" do
28
+ expect { subject.dummy_method }.to_not raise_error
29
+ end
30
+ end
15
31
  end
16
32
  end
17
33
  end
data/spec/spec_helper.rb CHANGED
@@ -29,8 +29,6 @@ RSpec.configure do |config|
29
29
  DatabaseCleaner[:sequel].strategy = :truncation
30
30
  DatabaseCleaner[:active_record].strategy = :truncation
31
31
 
32
- require File.expand_path('../support/monkey_patches/database_cleaner.rb', __FILE__)
33
-
34
32
  config.before do
35
33
  DatabaseCleaner.clean
36
34
  end
@@ -5,5 +5,6 @@ class Group
5
5
 
6
6
  attribute :name, String
7
7
 
8
- has_many :users, :via => SuperUser
8
+ has_one :leader, :via => SuperUser
9
+ has_many :users, :via => SuperUser
9
10
  end
@@ -6,5 +6,6 @@ class GroupRepositoryActiveRecord
6
6
  for_entity Group
7
7
  default_provider :ActiveRecord
8
8
 
9
- references :users, :via => SuperUserRepositoryActiveRecord
9
+ references :leader, :via => SuperUserRepositoryActiveRecord
10
+ references :users, :via => SuperUserRepositoryActiveRecord
10
11
  end
@@ -11,6 +11,9 @@ class HeroUserRepository
11
11
  map_attribute :gender, :to => 'HeroUserLastName#gender'
12
12
  end
13
13
 
14
+ before_init :action_before_init
15
+ before_load :action_before_load
16
+ before_find :action_before_find
14
17
  before_create :action_before_create
15
18
  before_create :action_before_create_2
16
19
  before_create { |entity| performed(:before_create_block, entity); true }
@@ -18,6 +21,9 @@ class HeroUserRepository
18
21
  before_save :action_before_save
19
22
  before_destroy :action_before_destroy
20
23
 
24
+ after_init :action_after_init
25
+ after_load :action_after_load
26
+ after_find :action_after_find
21
27
  after_create :action_after_create
22
28
  after_update :action_after_update
23
29
  after_save :action_after_save
@@ -25,12 +31,18 @@ class HeroUserRepository
25
31
 
26
32
  private
27
33
 
34
+ def action_before_init (entity); performed(:before_init, entity); true; end
35
+ def action_before_load (entity); performed(:before_load, entity); true; end
36
+ def action_before_find (entity); performed(:before_find, entity); true; end
28
37
  def action_before_create (entity); performed(:before_create, entity); true; end
29
38
  def action_before_create_2(entity); performed(:before_create_2, entity); true; end
30
39
  def action_before_update (entity); performed(:before_update, entity); true; end
31
40
  def action_before_save (entity); performed(:before_save, entity); true; end
32
41
  def action_before_destroy (entity); performed(:before_destroy, entity); true; end
33
42
 
43
+ def action_after_init (entity); performed(:after_init, entity); true; end
44
+ def action_after_load (entity); performed(:after_load, entity); true; end
45
+ def action_after_find (entity); performed(:after_find, entity); true; end
34
46
  def action_after_create (entity); performed(:after_create, entity); true; end
35
47
  def action_after_update (entity); performed(:after_update, entity); true; end
36
48
  def action_after_save (entity); performed(:after_save, entity); true; end
@@ -6,5 +6,6 @@ class GroupRepositorySequel
6
6
  for_entity Group
7
7
  default_provider :Sequel
8
8
 
9
- references :users, :via => SuperUserRepositorySequel
9
+ references :leader, :via => SuperUserRepositorySequel
10
+ references :users, :via => SuperUserRepositorySequel
10
11
  end
@@ -1,6 +1,6 @@
1
1
  shared_context "user repository" do |data_provider|
2
2
  let(:user_repository) { "UserRepository#{data_provider}".constantize }
3
- let!(:existing_user) { user_repository.save(User.new(:first_name => 'Fred', :driver_license => 'FREDWU42')) }
3
+ let!(:existing_user) { user_repository.save(User.new(:first_name => 'Fred', :last_name => 'Wu', :driver_license => 'FREDWU42', :health_care => 'HEALTHCARE')) }
4
4
  let(:new_valid_user) { User.new(:first_name => 'Batman', :driver_license => 'ARKHAMCITY') }
5
5
  let(:new_valid_user2) { User.new(:first_name => 'Ironman', :driver_license => 'NEWYORKCITY') }
6
6
  let(:new_invalid_user) { User.new(:first_name => 'a') }