tenacity 0.3.0 → 0.4.0

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 (70) hide show
  1. data/EXTEND.rdoc +9 -13
  2. data/Rakefile +6 -0
  3. data/history.txt +17 -0
  4. data/lib/tenacity.rb +13 -9
  5. data/lib/tenacity/associate_proxy.rb +56 -0
  6. data/lib/tenacity/associates_proxy.rb +5 -3
  7. data/lib/tenacity/association.rb +81 -9
  8. data/lib/tenacity/associations/belongs_to.rb +28 -16
  9. data/lib/tenacity/associations/has_many.rb +113 -53
  10. data/lib/tenacity/associations/has_one.rb +33 -14
  11. data/lib/tenacity/class_methods.rb +117 -9
  12. data/lib/tenacity/errors.rb +9 -0
  13. data/lib/tenacity/instance_methods.rb +40 -3
  14. data/lib/tenacity/orm_ext/activerecord.rb +114 -95
  15. data/lib/tenacity/orm_ext/couchrest.rb +132 -113
  16. data/lib/tenacity/orm_ext/datamapper.rb +138 -112
  17. data/lib/tenacity/orm_ext/helpers.rb +36 -0
  18. data/lib/tenacity/orm_ext/mongo_mapper.rb +102 -84
  19. data/lib/tenacity/orm_ext/mongoid.rb +106 -87
  20. data/lib/tenacity/orm_ext/sequel.rb +137 -110
  21. data/lib/tenacity/version.rb +1 -1
  22. data/tenacity.gemspec +2 -1
  23. data/test/association_features/belongs_to_test.rb +46 -1
  24. data/test/association_features/has_many_test.rb +187 -2
  25. data/test/association_features/has_one_test.rb +57 -2
  26. data/test/associations/belongs_to_test.rb +41 -7
  27. data/test/associations/has_many_test.rb +59 -10
  28. data/test/associations/has_one_test.rb +57 -2
  29. data/test/fixtures/active_record_car.rb +5 -4
  30. data/test/fixtures/active_record_climate_control_unit.rb +1 -0
  31. data/test/fixtures/active_record_engine.rb +1 -0
  32. data/test/fixtures/active_record_has_many_target.rb +7 -0
  33. data/test/fixtures/active_record_has_one_target.rb +7 -0
  34. data/test/fixtures/active_record_object.rb +19 -0
  35. data/test/fixtures/couch_rest_has_many_target.rb +7 -0
  36. data/test/fixtures/couch_rest_has_one_target.rb +7 -0
  37. data/test/fixtures/couch_rest_object.rb +14 -0
  38. data/test/fixtures/data_mapper_has_many_target.rb +23 -3
  39. data/test/fixtures/data_mapper_has_one_target.rb +23 -3
  40. data/test/fixtures/data_mapper_object.rb +14 -0
  41. data/test/fixtures/mongo_mapper_air_filter.rb +6 -0
  42. data/test/fixtures/mongo_mapper_alternator.rb +7 -0
  43. data/test/fixtures/mongo_mapper_autosave_false_has_many_target.rb +8 -0
  44. data/test/fixtures/mongo_mapper_autosave_false_has_one_target.rb +8 -0
  45. data/test/fixtures/mongo_mapper_autosave_true_has_many_target.rb +8 -0
  46. data/test/fixtures/mongo_mapper_autosave_true_has_one_target.rb +8 -0
  47. data/test/fixtures/mongo_mapper_circuit_board.rb +6 -0
  48. data/test/fixtures/mongo_mapper_dashboard.rb +5 -2
  49. data/test/fixtures/mongo_mapper_has_many_target.rb +7 -0
  50. data/test/fixtures/mongo_mapper_has_one_target.rb +7 -0
  51. data/test/fixtures/mongo_mapper_object.rb +14 -0
  52. data/test/fixtures/mongo_mapper_wheel.rb +2 -0
  53. data/test/fixtures/mongo_mapper_windows.rb +6 -0
  54. data/test/fixtures/mongoid_has_many_target.rb +7 -0
  55. data/test/fixtures/mongoid_has_one_target.rb +7 -0
  56. data/test/fixtures/mongoid_object.rb +14 -0
  57. data/test/fixtures/sequel_has_many_target.rb +7 -0
  58. data/test/fixtures/sequel_has_one_target.rb +7 -0
  59. data/test/fixtures/sequel_object.rb +14 -0
  60. data/test/helpers/active_record_test_helper.rb +87 -8
  61. data/test/helpers/couch_rest_test_helper.rb +7 -0
  62. data/test/helpers/data_mapper_test_helper.rb +41 -3
  63. data/test/helpers/sequel_test_helper.rb +65 -9
  64. data/test/orm_ext/activerecord_test.rb +1 -1
  65. data/test/orm_ext/datamapper_test.rb +33 -24
  66. data/test/orm_ext/mongo_mapper_test.rb +6 -6
  67. data/test/orm_ext/mongoid_test.rb +6 -6
  68. data/test/orm_ext/sequel_test.rb +1 -1
  69. data/test/test_helper.rb +18 -9
  70. metadata +119 -55
@@ -1,134 +1,161 @@
1
1
  module Tenacity
2
- # Tenacity relationships on Sequel objects require that certain columns
3
- # exist on the associated table, and that join tables exist for one-to-many
4
- # relationships. Take the following class for example:
5
- #
6
- # class Car < Sequel::Model
7
- # include Tenacity
8
- #
9
- # t_has_many :wheels
10
- # t_has_one :dashboard
11
- # t_belongs_to :driver
12
- # end
13
- #
14
- #
15
- # == t_belongs_to
16
- #
17
- # The +t_belongs_to+ association requires that a property exist in the table
18
- # to hold the id of the assoicated object.
19
- #
20
- # DB.create_table :cars do
21
- # primary_key :id
22
- # String :driver_id
23
- # end
24
- #
25
- #
26
- # == t_has_one
27
- #
28
- # The +t_has_one+ association requires no special column in the table, since
29
- # the associated object holds the foreign key.
30
- #
31
- #
32
- # == t_has_many
33
- #
34
- # The +t_has_many+ association requires that a join table exist to store the
35
- # associations. The name of the join table follows ActiveRecord conventions.
36
- # The name of the join table in this example would be cars_wheels, since cars
37
- # comes before wheels when shorted alphabetically.
38
- #
39
- # DB.create_table :cars_wheels do
40
- # Integer :car_id
41
- # String :wheel_id
42
- # end
43
- #
44
- module Sequel
45
-
46
- def self.setup(model)
47
- require 'sequel'
48
- if model.ancestors.include?(::Sequel::Model)
49
- model.send :include, Sequel::InstanceMethods
50
- model.extend Sequel::ClassMethods
2
+ module OrmExt
3
+ # Tenacity relationships on Sequel objects require that certain columns
4
+ # exist on the associated table, and that join tables exist for one-to-many
5
+ # relationships. Take the following class for example:
6
+ #
7
+ # class Car < Sequel::Model
8
+ # include Tenacity
9
+ #
10
+ # t_has_many :wheels
11
+ # t_has_one :dashboard
12
+ # t_belongs_to :driver
13
+ # end
14
+ #
15
+ #
16
+ # == t_belongs_to
17
+ #
18
+ # The +t_belongs_to+ association requires that a property exist in the table
19
+ # to hold the id of the assoicated object.
20
+ #
21
+ # DB.create_table :cars do
22
+ # primary_key :id
23
+ # String :driver_id
24
+ # end
25
+ #
26
+ #
27
+ # == t_has_one
28
+ #
29
+ # The +t_has_one+ association requires no special column in the table, since
30
+ # the associated object holds the foreign key.
31
+ #
32
+ #
33
+ # == t_has_many
34
+ #
35
+ # The +t_has_many+ association requires that a join table exist to store the
36
+ # associations. The name of the join table follows ActiveRecord conventions.
37
+ # The name of the join table in this example would be cars_wheels, since cars
38
+ # comes before wheels when shorted alphabetically.
39
+ #
40
+ # DB.create_table :cars_wheels do
41
+ # Integer :car_id
42
+ # String :wheel_id
43
+ # end
44
+ #
45
+ module Sequel
46
+
47
+ def self.setup(model)
48
+ require 'sequel'
49
+ if model.ancestors.include?(::Sequel::Model)
50
+ model.send :include, Sequel::InstanceMethods
51
+ model.extend Sequel::ClassMethods
52
+ end
53
+ rescue LoadError
54
+ # Sequel not available
51
55
  end
52
- rescue LoadError
53
- # Sequel not available
54
- end
55
56
 
56
- module ClassMethods #:nodoc:
57
- attr_accessor :_t_has_many_associations
58
- attr_accessor :_t_belongs_to_associations
57
+ module ClassMethods #:nodoc:
58
+ include Tenacity::OrmExt::Helpers
59
59
 
60
- def _t_find(id)
61
- self[id]
62
- end
60
+ attr_accessor :_t_has_one_associations
61
+ attr_accessor :_t_has_many_associations
62
+ attr_accessor :_t_belongs_to_associations
63
63
 
64
- def _t_find_bulk(ids)
65
- return [] if ids.nil? || ids.empty?
66
- filter(:id => ids)
67
- end
64
+ def _t_id_type
65
+ Integer
66
+ end
68
67
 
69
- def _t_find_first_by_associate(property, id)
70
- first(property.to_sym => id)
71
- end
68
+ def _t_find(id)
69
+ self[_t_serialize(id)]
70
+ end
72
71
 
73
- def _t_find_all_by_associate(property, id)
74
- filter(property => id)
75
- end
72
+ def _t_find_bulk(ids)
73
+ return [] if ids.nil? || ids.empty?
74
+ filter(:id => _t_serialize_ids(ids)).to_a
75
+ end
76
76
 
77
- def _t_initialize_has_many_association(association)
78
- @_t_has_many_associations ||= []
79
- @_t_has_many_associations << association
80
- end
77
+ def _t_find_first_by_associate(property, id)
78
+ first(property.to_sym => _t_serialize(id))
79
+ end
81
80
 
82
- def _t_initialize_belongs_to_association(association)
83
- @_t_belongs_to_associations ||= []
84
- @_t_belongs_to_associations << association
85
- end
81
+ def _t_find_all_by_associate(property, id)
82
+ filter(property => _t_serialize(id)).to_a
83
+ end
86
84
 
87
- def _t_delete(ids, run_callbacks=true)
88
- if run_callbacks
89
- filter(:id => ids).destroy
90
- else
91
- filter(:id => ids).delete
85
+ def _t_initialize_tenacity
92
86
  end
93
- end
94
- end
95
87
 
96
- module InstanceMethods #:nodoc:
97
- def before_save
98
- associations = self.class._t_belongs_to_associations || []
99
- associations.each { |association| self.class._t_stringify_belongs_to_value(self, association) }
100
- super
101
- end
88
+ def _t_initialize_has_one_association(association)
89
+ @_t_has_one_associations ||= []
90
+ @_t_has_one_associations << association
91
+ end
102
92
 
103
- def after_save
104
- associations = self.class._t_has_many_associations || []
105
- associations.each { |association| self.class._t_save_associates(self, association) }
106
- super
107
- end
93
+ def _t_initialize_has_many_association(association)
94
+ @_t_has_many_associations ||= []
95
+ @_t_has_many_associations << association
96
+ end
108
97
 
109
- def _t_reload
110
- reload
111
- end
98
+ def _t_initialize_belongs_to_association(association)
99
+ @_t_belongs_to_associations ||= []
100
+ @_t_belongs_to_associations << association
101
+ end
112
102
 
113
- def _t_clear_associates(association)
114
- db["delete from #{association.join_table} where #{association.association_key} = #{self.id}"].delete
103
+ def _t_delete(ids, run_callbacks=true)
104
+ if run_callbacks
105
+ filter(:id => _t_serialize_ids(ids)).destroy
106
+ else
107
+ filter(:id => _t_serialize_ids(ids)).delete
108
+ end
109
+ end
115
110
  end
116
111
 
117
- def _t_associate_many(association, associate_ids)
118
- db.transaction do
119
- _t_clear_associates(association)
120
- associate_ids.each do |associate_id|
121
- db["insert into #{association.join_table} (#{association.association_key}, #{association.association_foreign_key}) values (#{self.id}, '#{associate_id}')"].insert
112
+ module InstanceMethods #:nodoc:
113
+ include Tenacity::OrmExt::Helpers
114
+
115
+ def after_save
116
+ _t_save_autosave_associations
117
+
118
+ associations = self.class._t_has_many_associations || []
119
+ associations.each { |association| self.class._t_save_associates(self, association) }
120
+ super
121
+ end
122
+
123
+ def after_destroy
124
+ associations = self.class._t_belongs_to_associations || []
125
+ associations.each { |association| self._t_cleanup_belongs_to_association(association) }
126
+
127
+ associations = self.class._t_has_one_associations || []
128
+ associations.each { |association| self._t_cleanup_has_one_association(association) }
129
+
130
+ associations = self.class._t_has_many_associations || []
131
+ associations.each { |association| self._t_cleanup_has_many_association(association) }
132
+ super
133
+ end
134
+
135
+ def _t_reload
136
+ reload
137
+ end
138
+
139
+ def _t_clear_associates(association)
140
+ db["delete from #{association.join_table} where #{association.association_key} = #{_t_serialize_id_for_sql(self.id)}"].delete
141
+ end
142
+
143
+ def _t_associate_many(association, associate_ids)
144
+ db.transaction do
145
+ _t_clear_associates(association)
146
+ associate_ids.each do |associate_id|
147
+ db["insert into #{association.join_table} (#{association.association_key}, #{association.association_foreign_key}) values (#{_t_serialize_id_for_sql(self.id)}, #{_t_serialize_id_for_sql(associate_id)})"].insert
148
+ end
122
149
  end
123
150
  end
124
- end
125
151
 
126
- def _t_get_associate_ids(association)
127
- return [] if self.id.nil?
128
- rows = db["select #{association.association_foreign_key} from #{association.join_table} where #{association.association_key} = #{self.id}"].all
129
- rows.map { |row| row[association.association_foreign_key.to_sym] }
152
+ def _t_get_associate_ids(association)
153
+ return [] if self.id.nil?
154
+ rows = db["select #{association.association_foreign_key} from #{association.join_table} where #{association.association_key} = #{_t_serialize_id_for_sql(self.id)}"].all
155
+ rows.map { |row| row[association.association_foreign_key.to_sym] }
156
+ end
130
157
  end
131
- end
132
158
 
159
+ end
133
160
  end
134
161
  end
@@ -1,3 +1,3 @@
1
1
  module Tenacity
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/tenacity.gemspec CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
22
22
  s.add_development_dependency "rake", "~> 0.8.7"
23
23
  s.add_development_dependency "rcov", "~> 0.9.9"
24
24
  s.add_development_dependency "shoulda", "~> 2.11.3"
25
+ s.add_development_dependency "mocha", "~> 0.9.10"
25
26
  s.add_development_dependency "yard", "~> 0.6.4"
26
27
 
27
28
  # Relational DBs
@@ -33,7 +34,7 @@ Gem::Specification.new do |s|
33
34
 
34
35
  # MongoDB
35
36
  s.add_development_dependency "mongo_mapper", "~> 0.8.6"
36
- s.add_development_dependency "bson_ext", "~> 1.1.3"
37
+ s.add_development_dependency "bson_ext", "~> 1.2.4"
37
38
  s.add_development_dependency "mongoid", "~> 2.0.0.beta"
38
39
 
39
40
  # CouchDB
@@ -30,13 +30,58 @@ class BelongsToTest < Test::Unit::TestCase
30
30
  should "be able to specify the foreign key to use for the associated class" do
31
31
  car = ActiveRecordCar.create
32
32
  windshield = CouchRestWindshield.create(:active_record_car => car)
33
- assert_equal car.id.to_s, windshield.car_id
33
+ assert_equal car.id, windshield.car_id
34
34
  assert !windshield.respond_to?(:active_record_car_id)
35
35
 
36
36
  engine = ActiveRecordEngine.create(:active_record_car => car)
37
37
  assert_equal car.id, engine.car_id
38
38
  assert !engine.respond_to?(:active_record_car_id)
39
39
  end
40
+
41
+ should "not be able to modify the associated object if the readonly option is set" do
42
+ engine = ActiveRecordEngine.create
43
+ air_filter = MongoMapperAirFilter.create(:active_record_engine => engine)
44
+ engine = air_filter.active_record_engine
45
+ engine.prop = "value"
46
+ assert_raises(Tenacity::ReadOnlyError) { engine.save }
47
+ end
48
+
49
+ should "save the associated object if autosave is true" do
50
+ source = MongoMapperAutosaveTrueHasOneTarget.create
51
+ target = ActiveRecordObject.create(:prop => 'abc')
52
+ source.active_record_object = target
53
+ source.save
54
+ assert_equal 'abc', source.active_record_object.prop
55
+
56
+ source.active_record_object.prop = 'xyz'
57
+ source.save
58
+ source.reload && source.active_record_object(true)
59
+ assert_equal 'xyz', source.active_record_object.prop
60
+ end
61
+
62
+ should "destroy the associated object if autosave is true and object is marked for destruction" do
63
+ source = MongoMapperAutosaveTrueHasOneTarget.create
64
+ target = ActiveRecordObject.create
65
+ source.active_record_object = target
66
+ source.save
67
+ assert_not_nil source.active_record_object(true)
68
+
69
+ source.active_record_object.mark_for_destruction
70
+ assert source.active_record_object.marked_for_destruction?
71
+ source.save
72
+ source.reload
73
+ assert_nil source.active_record_object(true)
74
+ end
75
+
76
+ should "be able to create a polymorphic association" do
77
+ circuit_board = MongoMapperCircuitBoard.create
78
+ alternator = MongoMapperAlternator.create
79
+ circuit_board.diagnosable = alternator
80
+ circuit_board.save
81
+
82
+ assert_equal alternator, MongoMapperCircuitBoard.find(circuit_board.id).diagnosable
83
+ assert_equal 'MongoMapperAlternator', circuit_board.diagnosable_type
84
+ end
40
85
  end
41
86
 
42
87
  end
@@ -4,7 +4,7 @@ class HasManyTest < Test::Unit::TestCase
4
4
 
5
5
  context "A class with a belongs_to association to another class" do
6
6
  setup do
7
- setup_fixtures
7
+ setup_all_fixtures
8
8
  @car = ActiveRecordCar.create
9
9
  @wheels = [MongoMapperWheel.create, MongoMapperWheel.create, MongoMapperWheel.create]
10
10
 
@@ -43,7 +43,7 @@ class HasManyTest < Test::Unit::TestCase
43
43
  car.save
44
44
 
45
45
  assert_set_equal [door_1, door_2, door_3], ActiveRecordCar.find(car.id).couch_rest_doors
46
- assert_set_equal [door_1.id.to_s, door_2.id.to_s, door_3.id.to_s], ActiveRecordCar.find(car.id).couch_rest_door_ids
46
+ assert_set_equal [door_1.id, door_2.id, door_3.id], ActiveRecordCar.find(car.id).couch_rest_door_ids
47
47
  end
48
48
 
49
49
  should "save the associate object when it is added as an associate if the parent object is saved" do
@@ -79,6 +79,113 @@ class HasManyTest < Test::Unit::TestCase
79
79
  assert_set_equal [door_1], ActiveRecordCar.find(car.id).couch_rest_doors
80
80
  end
81
81
 
82
+ should "not be able to modify an associated object if the readonly option is set" do
83
+ car = ActiveRecordCar.create
84
+ wheel_1 = MongoMapperWheel.create
85
+ wheel_2 = MongoMapperWheel.create
86
+ wheel_3 = MongoMapperWheel.create
87
+ car.mongo_mapper_wheels = [wheel_1, wheel_2, wheel_3]
88
+ car.save
89
+
90
+ wheel = car.mongo_mapper_wheels.first
91
+ wheel.prop = "value"
92
+ assert_raises(Tenacity::ReadOnlyError) { wheel.save }
93
+ end
94
+
95
+ should "only return the number of results specified by the :limit option" do
96
+ car = ActiveRecordCar.create
97
+ wheel_1 = MongoMapperWheel.create
98
+ wheel_2 = MongoMapperWheel.create
99
+ wheel_3 = MongoMapperWheel.create
100
+ wheel_4 = MongoMapperWheel.create
101
+ wheel_5 = MongoMapperWheel.create
102
+ wheel_6 = MongoMapperWheel.create
103
+ wheel_7 = MongoMapperWheel.create
104
+ wheel_8 = MongoMapperWheel.create
105
+ car.mongo_mapper_wheels = [wheel_1, wheel_2, wheel_3, wheel_4, wheel_5, wheel_6, wheel_7, wheel_8]
106
+ car.save
107
+
108
+ sorted_wheels = car.mongo_mapper_wheels.sort { |a,b| a.id.to_s <=> b.id.to_s }
109
+ sorted_wheel_ids = sorted_wheels.map { |wheel| wheel.id.to_s }
110
+
111
+ assert_set_equal [sorted_wheels[0], sorted_wheels[1], sorted_wheels[2], sorted_wheels[3], sorted_wheels[4]],
112
+ ActiveRecordCar.find(car.id).mongo_mapper_wheels
113
+ assert_set_equal [sorted_wheel_ids[0], sorted_wheel_ids[1], sorted_wheel_ids[2], sorted_wheel_ids[3], sorted_wheel_ids[4]],
114
+ ActiveRecordCar.find(car.id).mongo_mapper_wheel_ids
115
+ end
116
+
117
+ should "skip over the first group of results, as specified by the :offset option" do
118
+ car = ActiveRecordCar.create
119
+ window_1 = MongoMapperWindow.create
120
+ window_2 = MongoMapperWindow.create
121
+ window_3 = MongoMapperWindow.create
122
+ window_4 = MongoMapperWindow.create
123
+ window_5 = MongoMapperWindow.create
124
+ window_6 = MongoMapperWindow.create
125
+ window_7 = MongoMapperWindow.create
126
+ window_8 = MongoMapperWindow.create
127
+ window_9 = MongoMapperWindow.create
128
+ car.mongo_mapper_windows = [window_1, window_2, window_3, window_4, window_5, window_6, window_7, window_8, window_9]
129
+ car.save
130
+
131
+ sorted_windows = car.mongo_mapper_windows.sort { |a,b| a.id.to_s <=> b.id.to_s }
132
+ sorted_window_ids = sorted_windows.map { |window| window.id.to_s }
133
+
134
+ assert_set_equal [sorted_windows[3], sorted_windows[4], sorted_windows[5], sorted_windows[6], sorted_windows[7]],
135
+ ActiveRecordCar.find(car.id).mongo_mapper_windows
136
+ assert_set_equal [sorted_window_ids[3], sorted_window_ids[4], sorted_window_ids[5], sorted_window_ids[6], sorted_window_ids[7]],
137
+ ActiveRecordCar.find(car.id).mongo_mapper_window_ids
138
+ end
139
+
140
+ should "be able to store objects via their polymorphic interface" do
141
+ circuit_board_1 = MongoMapperCircuitBoard.create
142
+ circuit_board_2 = MongoMapperCircuitBoard.create
143
+ circuit_board_3 = MongoMapperCircuitBoard.create
144
+ engine = ActiveRecordEngine.create
145
+ engine.diagnosable = [circuit_board_1, circuit_board_2, circuit_board_3]
146
+ engine.save
147
+
148
+ components = ActiveRecordEngine.find(engine.id).diagnosable
149
+ assert_set_equal [circuit_board_1, circuit_board_2, circuit_board_3], components
150
+ assert_set_equal ['ActiveRecordEngine', 'ActiveRecordEngine', 'ActiveRecordEngine'], components.map {|c| c.diagnosable_type}
151
+ end
152
+
153
+ context "with an autosave association" do
154
+ setup do
155
+ @source = ActiveRecordObject.create
156
+ @targets = [MongoMapperAutosaveTrueHasManyTarget.create(:prop => 'abc'), MongoMapperAutosaveTrueHasManyTarget.create(:prop => 'def')]
157
+ @source.mongo_mapper_autosave_true_has_many_targets = @targets
158
+ @source.save
159
+ assert_equal 'abc', @source.mongo_mapper_autosave_true_has_many_targets(true).first.prop
160
+ end
161
+
162
+ should "save the associated object if autosave is true" do
163
+ @source.mongo_mapper_autosave_true_has_many_targets.first.prop = 'xyz'
164
+ @source.save
165
+ @source.reload && @source.mongo_mapper_autosave_true_has_many_targets(true)
166
+ assert_equal 'xyz', @source.mongo_mapper_autosave_true_has_many_targets.first.prop
167
+ end
168
+
169
+ should "destroy the associated object stored in a local variable if autosave is true and object is marked for destruction" do
170
+ object = @source.mongo_mapper_autosave_true_has_many_targets.first
171
+ object.mark_for_destruction
172
+ assert object.marked_for_destruction?
173
+ @source.save
174
+ @source.reload && @source.mongo_mapper_autosave_true_has_many_targets(true)
175
+ assert_equal 1, @source.mongo_mapper_autosave_true_has_many_targets.size
176
+ assert_equal 'def', @source.mongo_mapper_autosave_true_has_many_targets.first.prop
177
+ end
178
+
179
+ should "destroy the associated object if autosave is true and object is marked for destruction" do
180
+ @source.mongo_mapper_autosave_true_has_many_targets.first.mark_for_destruction
181
+ assert @source.mongo_mapper_autosave_true_has_many_targets.first.marked_for_destruction?
182
+ @source.save
183
+ @source.reload && @source.mongo_mapper_autosave_true_has_many_targets(true)
184
+ assert_equal 1, @source.mongo_mapper_autosave_true_has_many_targets.size
185
+ assert_equal 'def', @source.mongo_mapper_autosave_true_has_many_targets.first.prop
186
+ end
187
+ end
188
+
82
189
  context "with a set of associates that need to be deleted" do
83
190
  setup do
84
191
  @new_car = ActiveRecordCar.create
@@ -104,6 +211,84 @@ class HasManyTest < Test::Unit::TestCase
104
211
  assert_equal old_count - 3, CouchRestDoor.count
105
212
  assert_set_equal [], ActiveRecordCar.find(@new_car.id).couch_rest_doors
106
213
  end
214
+
215
+ context "the delete method" do
216
+ should "not delete the object from the database if no dependent option is specified" do
217
+ dashboard = MongoMapperDashboard.create
218
+ vent_1 = MongoMapperVent.create
219
+ vent_2 = MongoMapperVent.create
220
+ vent_3 = MongoMapperVent.create
221
+ dashboard.vents = [vent_1, vent_2, vent_3]
222
+ dashboard.save
223
+ assert_set_equal [vent_1, vent_2, vent_3], MongoMapperDashboard.find(dashboard.id).vents
224
+
225
+ dashboard.vents.delete(vent_1)
226
+ dashboard.save
227
+ assert_set_equal [vent_2, vent_3], MongoMapperDashboard.find(dashboard.id).vents
228
+ assert_not_nil MongoMapperVent.find(vent_1.id)
229
+ end
230
+
231
+ should "delete the associated object and issue callbacks if association is configured to :destroy dependents" do
232
+ wheel_1 = MongoMapperWheel.create
233
+ wheel_2 = MongoMapperWheel.create
234
+ wheel_3 = MongoMapperWheel.create
235
+ @new_car.mongo_mapper_wheels = [wheel_1, wheel_2, wheel_3]
236
+ @new_car.save
237
+ assert_set_equal [wheel_1, wheel_2, wheel_3], ActiveRecordCar.find(@new_car.id).mongo_mapper_wheels
238
+
239
+ @new_car.mongo_mapper_wheels.delete(wheel_1)
240
+ @new_car.save
241
+ assert_set_equal [wheel_2, wheel_3], ActiveRecordCar.find(@new_car.id).mongo_mapper_wheels
242
+ assert_nil MongoMapperWheel._t_find(wheel_1.id)
243
+ end
244
+
245
+ should "delete the associated object without issuing callbacks if association is configured to :delete_all dependents" do
246
+ @new_car.couch_rest_doors.delete(@door_1)
247
+ @new_car.save
248
+ assert_set_equal [@door_2, @door_3], ActiveRecordCar.find(@new_car.id).couch_rest_doors
249
+ assert_nil CouchRestDoor._t_find(@door_1.id)
250
+ end
251
+ end
252
+
253
+ context "the clear method" do
254
+ should "not delete the object from the database if no dependent option is specified" do
255
+ dashboard = MongoMapperDashboard.create
256
+ vent_1 = MongoMapperVent.create
257
+ vent_2 = MongoMapperVent.create
258
+ dashboard.vents = [vent_1, vent_2]
259
+ dashboard.save
260
+ assert_set_equal [vent_1, vent_2], MongoMapperDashboard.find(dashboard.id).vents
261
+
262
+ dashboard.vents.clear
263
+ dashboard.save
264
+ assert_set_equal [], MongoMapperDashboard.find(dashboard.id).vents
265
+ assert_not_nil MongoMapperVent.find(vent_1.id)
266
+ assert_not_nil MongoMapperVent.find(vent_2.id)
267
+ end
268
+
269
+ should "delete the associated object and issue callbacks if association is configured to :destroy dependents" do
270
+ wheel_1 = MongoMapperWheel.create
271
+ wheel_2 = MongoMapperWheel.create
272
+ @new_car.mongo_mapper_wheels = [wheel_1, wheel_2]
273
+ @new_car.save
274
+ assert_set_equal [wheel_1, wheel_2], ActiveRecordCar.find(@new_car.id).mongo_mapper_wheels
275
+
276
+ @new_car.mongo_mapper_wheels.clear
277
+ @new_car.save
278
+ assert_set_equal [], ActiveRecordCar.find(@new_car.id).mongo_mapper_wheels
279
+ assert_nil MongoMapperWheel._t_find(wheel_1.id)
280
+ assert_nil MongoMapperWheel._t_find(wheel_2.id)
281
+ end
282
+
283
+ should "delete the associated object without issuing callbacks if association is configured to :delete_all dependents" do
284
+ @new_car.couch_rest_doors.clear
285
+ @new_car.save
286
+ assert_set_equal [], ActiveRecordCar.find(@new_car.id).couch_rest_doors
287
+ assert_nil CouchRestDoor._t_find(@door_1.id)
288
+ assert_nil CouchRestDoor._t_find(@door_2.id)
289
+ assert_nil CouchRestDoor._t_find(@door_3.id)
290
+ end
291
+ end
107
292
  end
108
293
  end
109
294