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
@@ -0,0 +1,36 @@
1
+ module Tenacity
2
+ module OrmExt
3
+ module Helpers #:nodoc:
4
+
5
+ def id_class_for(association)
6
+ if association.polymorphic?
7
+ String
8
+ elsif association.type == :belongs_to
9
+ association.source._t_id_type
10
+ else
11
+ association.associate_class._t_id_type
12
+ end
13
+ end
14
+
15
+ def _t_serialize_ids(ids)
16
+ if ids.respond_to?(:map)
17
+ ids.map { |id| _t_serialize(id) }
18
+ else
19
+ _t_serialize(ids)
20
+ end
21
+ end
22
+
23
+ def _t_serialize_id_for_sql(id)
24
+ return id if id.nil?
25
+
26
+ serialized_id = _t_serialize(id)
27
+ if serialized_id.class == String
28
+ "'#{serialized_id}'"
29
+ else
30
+ serialized_id
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -1,105 +1,123 @@
1
1
  module Tenacity
2
- # Tenacity relationships on MongoMapper objects require no special keys
3
- # defined on the object. Tenacity will define the keys that it needs
4
- # to support the relationships. Take the following class for example:
5
- #
6
- # class Car < ActiveRecord::Base
7
- # include MongoMapper::Document
8
- # include Tenacity
9
- #
10
- # t_has_many :wheels
11
- # t_has_one :dashboard
12
- # t_belongs_to :driver
13
- # end
14
- #
15
- # == t_belongs_to
16
- #
17
- # The +t_belongs_to+ association will define a key named after the association.
18
- # The example above will create a key named <tt>:driver_id</tt>
19
- #
20
- #
21
- # == t_has_one
22
- #
23
- # The +t_has_one+ association will not define any new keys on the object, since
24
- # the associated object holds the foreign key.
25
- #
26
- #
27
- # == t_has_many
28
- #
29
- # The +t_has_many+ association will define a key named after the association.
30
- # The example above will create a key named <tt>:wheels_ids</tt>
31
- #
32
- module MongoMapper
33
-
34
- def self.setup(model) #:nodoc:
35
- require 'mongo_mapper'
36
- if model.included_modules.include?(::MongoMapper::Document)
37
- model.send :include, MongoMapper::InstanceMethods
38
- model.extend MongoMapper::ClassMethods
39
- end
40
- rescue LoadError
41
- # MongoMapper not available
42
- end
2
+ module OrmExt
3
+ # Tenacity relationships on MongoMapper objects require no special keys
4
+ # defined on the object. Tenacity will define the keys that it needs
5
+ # to support the relationships. Take the following class for example:
6
+ #
7
+ # class Car < ActiveRecord::Base
8
+ # include MongoMapper::Document
9
+ # include Tenacity
10
+ #
11
+ # t_has_many :wheels
12
+ # t_has_one :dashboard
13
+ # t_belongs_to :driver
14
+ # end
15
+ #
16
+ # == t_belongs_to
17
+ #
18
+ # The +t_belongs_to+ association will define a key named after the association.
19
+ # The example above will create a key named <tt>:driver_id</tt>
20
+ #
21
+ #
22
+ # == t_has_one
23
+ #
24
+ # The +t_has_one+ association will not define any new keys on the object, since
25
+ # the associated object holds the foreign key.
26
+ #
27
+ #
28
+ # == t_has_many
29
+ #
30
+ # The +t_has_many+ association will define a key named after the association.
31
+ # The example above will create a key named <tt>:wheels_ids</tt>
32
+ #
33
+ module MongoMapper
43
34
 
44
- module ClassMethods #:nodoc:
45
- def _t_find(id)
46
- find(id)
35
+ def self.setup(model) #:nodoc:
36
+ require 'mongo_mapper'
37
+ if model.included_modules.include?(::MongoMapper::Document)
38
+ model.send :include, MongoMapper::InstanceMethods
39
+ model.extend MongoMapper::ClassMethods
40
+ end
41
+ rescue LoadError
42
+ # MongoMapper not available
47
43
  end
48
44
 
49
- def _t_find_bulk(ids=[])
50
- find(ids)
51
- end
45
+ module ClassMethods #:nodoc:
46
+ include Tenacity::OrmExt::Helpers
52
47
 
53
- def _t_find_first_by_associate(property, id)
54
- first(property => id.to_s)
55
- end
48
+ def _t_id_type
49
+ String
50
+ end
56
51
 
57
- def _t_find_all_by_associate(property, id)
58
- all(property => id.to_s)
59
- end
52
+ def _t_find(id)
53
+ find(_t_serialize(id))
54
+ end
60
55
 
61
- def _t_initialize_has_many_association(association)
62
- unless self.respond_to?(association.foreign_keys_property)
63
- key association.foreign_keys_property, Array
64
- after_save { |record| _t_save_associates(record, association) }
56
+ def _t_find_bulk(ids=[])
57
+ find(_t_serialize_ids(ids))
65
58
  end
66
- end
67
59
 
68
- def _t_initialize_belongs_to_association(association)
69
- unless self.respond_to?(association.foreign_key)
70
- key association.foreign_key, String
71
- before_save { |record| _t_stringify_belongs_to_value(record, association) }
60
+ def _t_find_first_by_associate(property, id)
61
+ first(property => _t_serialize(id))
72
62
  end
73
- end
74
63
 
75
- def _t_delete(ids, run_callbacks=true)
76
- if run_callbacks
77
- destroy(ids)
78
- else
79
- delete(ids)
64
+ def _t_find_all_by_associate(property, id)
65
+ all(property => _t_serialize(id))
80
66
  end
81
- end
82
- end
83
67
 
84
- module InstanceMethods #:nodoc:
85
- def _t_reload
86
- reload
87
- rescue ::MongoMapper::DocumentNotFound
88
- nil
89
- end
68
+ def _t_initialize_tenacity
69
+ after_save { |record| record._t_save_autosave_associations }
70
+ end
90
71
 
91
- def _t_associate_many(association, associate_ids)
92
- self.send(association.foreign_keys_property + '=', associate_ids.map { |associate_id| associate_id.to_s })
93
- end
72
+ def _t_initialize_has_one_association(association)
73
+ after_destroy { |record| record._t_cleanup_has_one_association(association) }
74
+ end
75
+
76
+ def _t_initialize_has_many_association(association)
77
+ unless self.respond_to?(association.foreign_keys_property)
78
+ key association.foreign_keys_property, Array
79
+ after_save { |record| _t_save_associates(record, association) }
80
+ after_destroy { |record| record._t_cleanup_has_many_association(association) }
81
+ end
82
+ end
83
+
84
+ def _t_initialize_belongs_to_association(association)
85
+ unless self.respond_to?(association.foreign_key)
86
+ key association.foreign_key, id_class_for(association)
87
+ key association.polymorphic_type, String if association.polymorphic?
88
+ after_destroy { |record| record._t_cleanup_belongs_to_association(association) }
89
+ end
90
+ end
94
91
 
95
- def _t_get_associate_ids(association)
96
- self.send(association.foreign_keys_property)
92
+ def _t_delete(ids, run_callbacks=true)
93
+ if run_callbacks
94
+ destroy(_t_serialize_ids(ids))
95
+ else
96
+ delete(_t_serialize_ids(ids))
97
+ end
98
+ end
97
99
  end
98
100
 
99
- def _t_clear_associates(association)
100
- self.send(association.foreign_keys_property + '=', [])
101
+ module InstanceMethods #:nodoc:
102
+ def _t_reload
103
+ reload
104
+ rescue ::MongoMapper::DocumentNotFound
105
+ nil
106
+ end
107
+
108
+ def _t_associate_many(association, associate_ids)
109
+ self.send(association.foreign_keys_property + '=', associate_ids)
110
+ end
111
+
112
+ def _t_get_associate_ids(association)
113
+ self.send(association.foreign_keys_property)
114
+ end
115
+
116
+ def _t_clear_associates(association)
117
+ self.send(association.foreign_keys_property + '=', [])
118
+ end
101
119
  end
102
- end
103
120
 
121
+ end
104
122
  end
105
123
  end
@@ -1,108 +1,127 @@
1
1
  module Tenacity
2
- # Tenacity relationships on Mongoid objects require no special keys
3
- # defined on the object. Tenacity will define the keys that it needs
4
- # to support the relationships. Take the following class for example:
5
- #
6
- # class Car
7
- # include Mongoid::Document
8
- # include Tenacity
9
- #
10
- # t_has_many :wheels
11
- # t_has_one :dashboard
12
- # t_belongs_to :driver
13
- # end
14
- #
15
- # == t_belongs_to
16
- #
17
- # The +t_belongs_to+ association will define a key named after the association.
18
- # The example above will create a key named <tt>:driver_id</tt>
19
- #
20
- #
21
- # == t_has_one
22
- #
23
- # The +t_has_one+ association will not define any new keys on the object, since
24
- # the associated object holds the foreign key.
25
- #
26
- #
27
- # == t_has_many
28
- #
29
- # The +t_has_many+ association will define a key named after the association.
30
- # The example above will create a key named <tt>:wheels_ids</tt>
31
- #
32
- module Mongoid
33
-
34
- def self.setup(model) #:nodoc:
35
- require 'mongoid'
36
- if model.included_modules.include?(::Mongoid::Document)
37
- model.send :include, Mongoid::InstanceMethods
38
- model.extend Mongoid::ClassMethods
39
- end
40
- rescue LoadError
41
- # Mongoid not available
42
- end
2
+ module OrmExt
3
+ # Tenacity relationships on Mongoid objects require no special keys
4
+ # defined on the object. Tenacity will define the keys that it needs
5
+ # to support the relationships. Take the following class for example:
6
+ #
7
+ # class Car
8
+ # include Mongoid::Document
9
+ # include Tenacity
10
+ #
11
+ # t_has_many :wheels
12
+ # t_has_one :dashboard
13
+ # t_belongs_to :driver
14
+ # end
15
+ #
16
+ # == t_belongs_to
17
+ #
18
+ # The +t_belongs_to+ association will define a key named after the association.
19
+ # The example above will create a key named <tt>:driver_id</tt>
20
+ #
21
+ #
22
+ # == t_has_one
23
+ #
24
+ # The +t_has_one+ association will not define any new keys on the object, since
25
+ # the associated object holds the foreign key.
26
+ #
27
+ #
28
+ # == t_has_many
29
+ #
30
+ # The +t_has_many+ association will define a key named after the association.
31
+ # The example above will create a key named <tt>:wheels_ids</tt>
32
+ #
33
+ module Mongoid
43
34
 
44
- module ClassMethods #:nodoc:
45
- def _t_find(id)
46
- (id.nil? || id.to_s.strip == "") ? nil : find(id)
47
- rescue ::Mongoid::Errors::DocumentNotFound
48
- nil
35
+ def self.setup(model) #:nodoc:
36
+ require 'mongoid'
37
+ if model.included_modules.include?(::Mongoid::Document)
38
+ model.send :include, Mongoid::InstanceMethods
39
+ model.extend Mongoid::ClassMethods
40
+ end
41
+ rescue LoadError
42
+ # Mongoid not available
49
43
  end
50
44
 
51
- def _t_find_bulk(ids)
52
- find(ids)
53
- rescue ::Mongoid::Errors::DocumentNotFound
54
- []
55
- end
45
+ module ClassMethods #:nodoc:
46
+ include Tenacity::OrmExt::Helpers
56
47
 
57
- def _t_find_first_by_associate(property, id)
58
- find(:first, :conditions => { property => id })
59
- end
48
+ def _t_id_type
49
+ String
50
+ end
60
51
 
61
- def _t_find_all_by_associate(property, id)
62
- find(:all, :conditions => { property => id })
63
- end
52
+ def _t_find(id)
53
+ (id.nil? || id.to_s.strip == "") ? nil : find(_t_serialize(id))
54
+ rescue ::Mongoid::Errors::DocumentNotFound
55
+ nil
56
+ end
64
57
 
65
- def _t_initialize_has_many_association(association)
66
- unless self.respond_to?(association.foreign_keys_property)
67
- field association.foreign_keys_property, :type => Array
68
- after_save { |record| self.class._t_save_associates(record, association) }
58
+ def _t_find_bulk(ids)
59
+ docs = find(_t_serialize_ids(ids))
60
+ docs.respond_to?(:each) ? docs : [docs]
61
+ rescue ::Mongoid::Errors::DocumentNotFound
62
+ []
69
63
  end
70
- end
71
64
 
72
- def _t_initialize_belongs_to_association(association)
73
- unless self.respond_to?(association.foreign_key)
74
- field association.foreign_key, :type => String
75
- before_save { |record| self.class._t_stringify_belongs_to_value(record, association) }
65
+ def _t_find_first_by_associate(property, id)
66
+ find(:first, :conditions => { property => _t_serialize(id) })
76
67
  end
77
- end
78
68
 
79
- def _t_delete(ids, run_callbacks=true)
80
- docs = _t_find_bulk(ids)
81
- if run_callbacks
82
- docs.each { |doc| doc.destroy }
83
- else
84
- docs.each { |doc| doc.delete }
69
+ def _t_find_all_by_associate(property, id)
70
+ find(:all, :conditions => { property => _t_serialize(id) })
85
71
  end
86
- end
87
- end
88
72
 
89
- module InstanceMethods #:nodoc:
90
- def _t_reload
91
- reload
92
- end
73
+ def _t_initialize_tenacity
74
+ after_save { |record| record._t_save_autosave_associations }
75
+ end
93
76
 
94
- def _t_associate_many(association, associate_ids)
95
- self.send(association.foreign_keys_property + '=', associate_ids.map { |associate_id| associate_id.to_s })
96
- end
77
+ def _t_initialize_has_one_association(association)
78
+ after_destroy { |record| record._t_cleanup_has_one_association(association) }
79
+ end
80
+
81
+ def _t_initialize_has_many_association(association)
82
+ unless self.respond_to?(association.foreign_keys_property)
83
+ field association.foreign_keys_property, :type => Array
84
+ after_save { |record| self.class._t_save_associates(record, association) }
85
+ after_destroy { |record| record._t_cleanup_has_many_association(association) }
86
+ end
87
+ end
88
+
89
+ def _t_initialize_belongs_to_association(association)
90
+ unless self.respond_to?(association.foreign_key)
91
+ field association.foreign_key, :type => id_class_for(association)
92
+ field association.polymorphic_type, :type => String if association.polymorphic?
93
+ after_destroy { |record| record._t_cleanup_belongs_to_association(association) }
94
+ end
95
+ end
97
96
 
98
- def _t_get_associate_ids(association)
99
- self.send(association.foreign_keys_property)
97
+ def _t_delete(ids, run_callbacks=true)
98
+ docs = _t_find_bulk(ids)
99
+ if run_callbacks
100
+ docs.each { |doc| doc.destroy }
101
+ else
102
+ docs.each { |doc| doc.delete }
103
+ end
104
+ end
100
105
  end
101
106
 
102
- def _t_clear_associates(association)
103
- self.send(association.foreign_keys_property + '=', [])
107
+ module InstanceMethods #:nodoc:
108
+ def _t_reload
109
+ reload
110
+ end
111
+
112
+ def _t_associate_many(association, associate_ids)
113
+ self.send(association.foreign_keys_property + '=', associate_ids)
114
+ end
115
+
116
+ def _t_get_associate_ids(association)
117
+ self.send(association.foreign_keys_property)
118
+ end
119
+
120
+ def _t_clear_associates(association)
121
+ self.send(association.foreign_keys_property + '=', [])
122
+ end
104
123
  end
105
- end
106
124
 
125
+ end
107
126
  end
108
127
  end