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,24 +1,43 @@
1
1
  module Tenacity
2
- module HasOne #:nodoc:
2
+ module Associations
3
+ module HasOne #:nodoc:
3
4
 
4
- private
5
+ def _t_cleanup_has_one_association(association)
6
+ associate = has_one_associate(association)
7
+ unless associate.nil?
8
+ if association.dependent == :destroy
9
+ association.associate_class._t_delete([_t_serialize(associate.id)])
10
+ elsif association.dependent == :delete
11
+ association.associate_class._t_delete([_t_serialize(associate.id)], false)
12
+ elsif association.dependent == :nullify
13
+ associate.send "#{association.foreign_key(self.class)}=", nil
14
+ associate.save
15
+ end
16
+ end
17
+ end
5
18
 
6
- def has_one_associate(association)
7
- clazz = association.associate_class
8
- clazz._t_find_first_by_associate(association.foreign_key(self.class), self.id.to_s)
9
- end
19
+ private
10
20
 
11
- def set_has_one_associate(association, associate)
12
- associate.send "#{association.foreign_key(self.class)}=", self.id.to_s
13
- associate.save
14
- end
21
+ def has_one_associate(association)
22
+ clazz = association.associate_class
23
+ associate = clazz._t_find_first_by_associate(association.foreign_key(self.class), _t_serialize(self.id, association))
24
+ associate
25
+ end
15
26
 
16
- module ClassMethods #:nodoc:
17
- def initialize_has_one_association(association)
18
- _t_initialize_has_one_association(association) if respond_to?(:_t_initialize_has_one_association)
27
+ def set_has_one_associate(association, associate)
28
+ associate.send "#{association.foreign_key(self.class)}=", _t_serialize(self.id, association)
29
+ associate.send "#{association.polymorphic_type}=", self.class.to_s if association.polymorphic?
30
+ associate.save unless association.autosave == false
31
+ associate
32
+ end
33
+
34
+ module ClassMethods #:nodoc:
35
+ def initialize_has_one_association(association)
36
+ _t_initialize_has_one_association(association) if respond_to?(:_t_initialize_has_one_association)
37
+ end
19
38
  end
20
- end
21
39
 
40
+ end
22
41
  end
23
42
  end
24
43
 
@@ -86,6 +86,10 @@ module Tenacity
86
86
  # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be
87
87
  # aware of, mostly involving the saving of associated objects.
88
88
  #
89
+ # Unless you set the :autosave option on a <tt>t_has_one</tt>, <tt>t_belongs_to</tt>, or
90
+ # <tt>t_has_many</tt> association. Setting it to +true+ will _always_ save the members,
91
+ # whereas setting it to +false+ will _never_ save the members.
92
+ #
89
93
  # === One-to-one associations
90
94
  #
91
95
  # * Assigning an object to a +t_has_one+ association automatically saves that object and the object being replaced (if there is one), in
@@ -99,6 +103,30 @@ module Tenacity
99
103
  # (the owner of the collection) is not yet stored in the database.
100
104
  # * All unsaved members of the collection are automatically saved when the parent is saved.
101
105
  #
106
+ # === Polymorphic Associations
107
+ #
108
+ # Polymorphic associations on models are not restricted on what types of models they can be associated with. Rather, they
109
+ # specify an interface that a +t_has_many+ association must adhere to.
110
+ #
111
+ # class Asset < ActiveRecord::Base
112
+ # include Tenacity
113
+ # t_belongs_to :attachable, :polymorphic => true
114
+ # end
115
+ #
116
+ # class Post
117
+ # include MongoMapper::Document
118
+ # include Tenacity
119
+ # t_has_many :assets, :as => :attachable # The :as option specifies the polymorphic interface to use.
120
+ # end
121
+ #
122
+ # @asset.attachable = @post
123
+ #
124
+ # This works by using a type field/column in addition to a foreign key to specify the associated record. In the Asset example, you'd need
125
+ # an +attachable_id+ field and an +attachable_type+ string field on your model.
126
+ #
127
+ # IDs for polymorphic associations are always stored as strings in the database, since we cannot determine the true type of the
128
+ # id when the association is defined.
129
+ #
102
130
  # == Caching
103
131
  #
104
132
  # All of the methods are built on a simple caching principle that will keep the result
@@ -139,6 +167,7 @@ module Tenacity
139
167
  # can be used to override these defaults.
140
168
  #
141
169
  module ClassMethods
170
+ attr_reader :_tenacity_associations
142
171
 
143
172
  # Specifies a one-to-one association with another class. This method should only be used
144
173
  # if the other class contains the foreign key. If the current class contains the foreign key,
@@ -170,14 +199,29 @@ module Tenacity
170
199
  # Specify the foreign key used for the association. By default this is guessed to be the name
171
200
  # of this class in lower-case and "_id" suffixed. So a Person class that makes a +t_has_one+ association
172
201
  # will use "person_id" as the default <tt>:foreign_key</tt>.
202
+ # [:dependent]
203
+ # If set to <tt>:destroy</tt>, the associated object is deleted when this object is, and all delete
204
+ # callbacks are called. If set to <tt>:delete</tt>, the associated object is deleted *without*
205
+ # calling any of its delete callbacks. If set to <tt>:nullify</tt>, the associated object's
206
+ # foreign key is set to +NULL+.
207
+ # [:readonly]
208
+ # If true, the associated object is readonly through the association.
209
+ # [:autosave]
210
+ # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
211
+ # [:as]
212
+ # Specifies a polymorphic interface (See <tt>t_belongs_to</tt>).
173
213
  #
174
214
  # Option examples:
215
+ # t_has_one :credit_card, :dependent => :destroy # destroys the associated credit card
216
+ # t_has_one :credit_card, :dependent => :nullify # updates the associated records foreign key value to NULL rather than destroying it
175
217
  # t_has_one :project_manager, :class_name => "Person"
176
218
  # t_has_one :project_manager, :foreign_key => "project_id" # within class named SecretProject
219
+ # t_has_one :boss, :readonly => :true
220
+ # t_has_one :attachment, :as => :attachable
177
221
  #
178
222
  def t_has_one(name, options={})
179
- extend(HasOne::ClassMethods)
180
- association = Association.new(:t_has_one, name, self, options)
223
+ extend(Associations::HasOne::ClassMethods)
224
+ association = _t_create_association(:t_has_one, name, options)
181
225
  initialize_has_one_association(association)
182
226
 
183
227
  define_method(association.name) do |*params|
@@ -225,14 +269,29 @@ module Tenacity
225
269
  # association will use "person_id" as the default <tt>:foreign_key</tt>. Similarly,
226
270
  # <tt>t_belongs_to :favorite_person, :class_name => "Person"</tt> will use a foreign key
227
271
  # of "favorite_person_id".
272
+ # [:dependent]
273
+ # If set to <tt>:destroy</tt>, the associated object is deleted when this object is, calling all delete
274
+ # callbacks. If set to <tt>:delete</tt>, the associated object is deleted *without* calling any of
275
+ # its delete callbacks. This option should not be specified when <tt>t_belongs_to</tt> is used in
276
+ # conjuction with a <tt>t_has_many</tt> relationship on another class because of the potential
277
+ # to leave orphaned records behind.
278
+ # [:readonly]
279
+ # If true, the associated object is readonly through the association.
280
+ # [:autosave]
281
+ # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
282
+ # [:polymorphic]
283
+ # Specify this association is a polymorphic association by passing +true+. (*Note*: IDs for polymorphic associations are always
284
+ # stored as strings in the database.)
228
285
  #
229
286
  # Option examples:
230
287
  # t_belongs_to :project_manager, :class_name => "Person"
231
288
  # t_belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id"
289
+ # t_belongs_to :project, :readonly => true
290
+ # t_belongs_to :attachable, :polymorphic => true
232
291
  #
233
292
  def t_belongs_to(name, options={})
234
- extend(BelongsTo::ClassMethods)
235
- association = Association.new(:t_belongs_to, name, self, options)
293
+ extend(Associations::BelongsTo::ClassMethods)
294
+ association = _t_create_association(:t_belongs_to, name, options)
236
295
  initialize_belongs_to_association(association)
237
296
 
238
297
  define_method(association.name) do |*params|
@@ -265,7 +324,9 @@ module Tenacity
265
324
  # [collection.concat(other_array)]
266
325
  # Adds the objects in the other array to the collection by setting their foreign keys to the collection's primary key.
267
326
  # [collection.delete(object, ...)]
268
- # Removes one or more objects from the collection.
327
+ # Removes one or more objects from the collection by setting their foreign keys to +NULL+.
328
+ # Objects will be in addition deleted and callbacks called if they're associated with <tt>:dependent => :destroy</tt>,
329
+ # and deleted and callbacks skipped if they're associated with <tt>:dependent => :delete_all</tt>.
269
330
  # [collection.destroy_all]
270
331
  # Removes all objects from the collection, and deletes them from their respective
271
332
  # database. If the deleted objects have any delete callbacks defined, they will be called.
@@ -279,7 +340,10 @@ module Tenacity
279
340
  # [collection_singular_ids=ids]
280
341
  # Replace the collection with the objects identified by the primary keys in +ids+.
281
342
  # [collection.clear]
282
- # Removes every object from the collection.
343
+ # Removes every object from the collection. This deletes the associated objects and issues callbacks
344
+ # if they are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the
345
+ # database without calling any callbacks if <tt>:dependent => :delete_all</tt>, otherwise sets their
346
+ # foreign keys to +NULL+.
283
347
  # [collection.empty?]
284
348
  # Returns +true+ if there are no associated objects.
285
349
  # [collection.size]
@@ -319,6 +383,12 @@ module Tenacity
319
383
  # for objects that store associated ids in an array instaed of a join table (CouchRest,
320
384
  # MongoMapper, etc). <b>WARNING:</b> The name of the association with an "_ids" suffix should
321
385
  # not be used as the property name, since tenacity adds a method with this name to the object.
386
+ # [:dependent]
387
+ # If set to <tt>:destroy</tt> all the associated objects are deleted alongside this object
388
+ # in addition to calling their delete callbacks. If set to <tt>:delete_all</tt> all
389
+ # associated objects are deleted *without* calling their delete callbacks. If set to
390
+ # <tt>:nullify</tt> all associated objects' foreign keys are set to +NULL+ *without* calling
391
+ # their save backs.
322
392
  # [:join_table]
323
393
  # Specify the name of the join table if the default based on lexical order isn't what you want.
324
394
  # This option is only valid if one of the models in the association is backed by a relational
@@ -336,6 +406,18 @@ module Tenacity
336
406
  # "_id" suffixed. So if a Person class makes a +t_has_many+ association to Project, the
337
407
  # association will use "person_id" as the default <tt>:association_key</tt>. This option is
338
408
  # only valid if one of the associated objects is backed by a relational database.
409
+ # [:readonly]
410
+ # If true, all the associated objects are readonly through the association.
411
+ # [:limit]
412
+ # An integer determining the limit on the number of rows that should be returned. Results
413
+ # are ordered by a string representation of the id.
414
+ # [:offset]
415
+ # An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
416
+ # Results are ordered by a string representation of the id.
417
+ # [:autosave]
418
+ # If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default.
419
+ # [:as]
420
+ # Specifies a polymorphic interface (See <tt>t_belongs_to</tt>).
339
421
  #
340
422
  # Option examples:
341
423
  # t_has_many :products, :class_name => "SpecialProduct"
@@ -344,10 +426,13 @@ module Tenacity
344
426
  # t_has_many :managers, :join_table => "project_managers_and_projects"
345
427
  # t_has_many :managers, :join_table => "project_managers_and_projects",
346
428
  # :association_foreign_key => "mgr_id", :association_key => "proj_id"
429
+ # t_has_many :tasks, :dependent => :destroy
430
+ # t_has_many :reports, :readonly => true
431
+ # t_has_many :tags, :as => :taggable
347
432
  #
348
433
  def t_has_many(name, options={})
349
- extend(HasMany::ClassMethods)
350
- association = Association.new(:t_has_many, name, self, options)
434
+ extend(Associations::HasMany::ClassMethods)
435
+ association = _t_create_association(:t_has_many, name, options)
351
436
  initialize_has_many_association(association)
352
437
 
353
438
  define_method(association.name) do |*params|
@@ -357,7 +442,9 @@ module Tenacity
357
442
  end
358
443
 
359
444
  define_method("#{association.name}=") do |associates|
360
- set_associate(association, associates)
445
+ set_associate(association, associates) do
446
+ set_has_many_associates(association, associates)
447
+ end
361
448
  end
362
449
 
363
450
  define_method("#{ActiveSupport::Inflector.singularize(association.name)}_ids") do
@@ -375,6 +462,27 @@ module Tenacity
375
462
  end
376
463
  end
377
464
 
465
+ def _t_serialize(object_id, association=nil) #:nodoc:
466
+ if association && association.polymorphic?
467
+ object_id.to_s
468
+ elsif object_id.nil?
469
+ nil
470
+ elsif [Fixnum].include?(object_id.class)
471
+ object_id
472
+ else
473
+ object_id.to_s
474
+ end
475
+ end
476
+
477
+ private
478
+
479
+ def _t_create_association(type, name, options) #:nococ:
480
+ association = Association.new(type, name, self, options)
481
+ @_tenacity_associations ||= []
482
+ @_tenacity_associations << association
483
+ association
484
+ end
485
+
378
486
  end
379
487
  end
380
488
 
@@ -0,0 +1,9 @@
1
+ module Tenacity
2
+ # Generic Tenacity exception class.
3
+ class TenacityError < StandardError
4
+ end
5
+
6
+ # Raised on attempt to update an associate that is instantiated as read only.
7
+ class ReadOnlyError < TenacityError
8
+ end
9
+ end
@@ -5,8 +5,26 @@ module Tenacity
5
5
  "@_t_" + association.name.to_s
6
6
  end
7
7
 
8
+ def _t_save_autosave_associations
9
+ self.class._tenacity_associations.each do |association|
10
+ if association.autosave == true
11
+ if association.type == :t_has_one || association.type == :t_belongs_to
12
+ associate = instance_variable_get(_t_ivar_name(association))
13
+ autosave_save_or_destroy(associate) unless associate.nil?
14
+ elsif association.type == :t_has_many
15
+ associates = instance_variable_get(_t_ivar_name(association))
16
+ associates.each { |associate| autosave_save_or_destroy(associate) } unless associates.nil?
17
+ end
18
+ end
19
+ end
20
+ end
21
+
8
22
  private
9
23
 
24
+ def autosave_save_or_destroy(associate)
25
+ associate.marked_for_destruction? ? associate.destroy : associate.save
26
+ end
27
+
10
28
  def get_associate(association, params)
11
29
  _t_reload unless id.nil?
12
30
  force_reload = params.first unless params.empty?
@@ -19,12 +37,31 @@ module Tenacity
19
37
  end
20
38
 
21
39
  def set_associate(association, associate)
22
- yield if block_given?
23
- instance_variable_set _t_ivar_name(association), associate
40
+ associate = yield if block_given?
41
+ instance_variable_set _t_ivar_name(association), create_proxy(associate, association)
24
42
  end
25
43
 
26
44
  def create_proxy(value, association)
27
- value.respond_to?(:each) ? AssociatesProxy.new(self, value, association) : value
45
+ return value if value.respond_to?(:proxy_respond_to?)
46
+
47
+ if multiple_associates?(association, value)
48
+ value.map! { |v| create_associate_proxy_for(v, association) }
49
+ AssociatesProxy.new(self, value, association)
50
+ else
51
+ create_associate_proxy_for(value, association)
52
+ end
53
+ end
54
+
55
+ def create_associate_proxy_for(value, association)
56
+ value.nil? ? nil : AssociateProxy.new(value, association)
57
+ end
58
+
59
+ def multiple_associates?(association, value)
60
+ association.type == :t_has_many && value.is_a?(Enumerable)
61
+ end
62
+
63
+ def _t_serialize(object, association=nil)
64
+ self.class._t_serialize(object, association)
28
65
  end
29
66
 
30
67
  end
@@ -1,116 +1,135 @@
1
1
  module Tenacity
2
- # Tenacity relationships on ActiveRecord 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 < ActiveRecord::Base
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
- # create_table :cars do |t|
21
- # t.string :driver_id
22
- # end
23
- #
24
- #
25
- # == t_has_one
26
- #
27
- # The +t_has_one+ association requires no special column in the table, since
28
- # the associated object holds the foreign key.
29
- #
30
- #
31
- # == t_has_many
32
- #
33
- # The +t_has_many+ association requires that a join table exist to store the
34
- # associations. The name of the join table follows ActiveRecord conventions.
35
- # The name of the join table in this example would be cars_wheels, since cars
36
- # comes before wheels when shorted alphabetically.
37
- #
38
- # create_table :cars_wheels do |t|
39
- # t.integer :car_id
40
- # t.string :wheel_id
41
- # end
42
- #
43
- module ActiveRecord
44
-
45
- def self.setup(model)
46
- require 'active_record'
47
- if model.ancestors.include?(::ActiveRecord::Base)
48
- model.send :include, ActiveRecord::InstanceMethods
49
- model.extend ActiveRecord::ClassMethods
50
- end
51
- rescue LoadError
52
- # ActiveRecord not available
53
- end
2
+ module OrmExt
3
+ # Tenacity relationships on ActiveRecord 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 < ActiveRecord::Base
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
+ # create_table :cars do |t|
22
+ # t.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
+ # create_table :cars_wheels do |t|
40
+ # t.integer :car_id
41
+ # t.string :wheel_id
42
+ # end
43
+ #
44
+ module ActiveRecord
54
45
 
55
- module ClassMethods #:nodoc:
56
- def _t_find(id)
57
- find_by_id(id)
46
+ def self.setup(model)
47
+ require 'active_record'
48
+ if model.ancestors.include?(::ActiveRecord::Base)
49
+ model.send :include, ActiveRecord::InstanceMethods
50
+ model.extend ActiveRecord::ClassMethods
51
+ end
52
+ rescue LoadError
53
+ # ActiveRecord not available
58
54
  end
59
55
 
60
- def _t_find_bulk(ids)
61
- return [] if ids.nil? || ids.empty?
62
- find(:all, :conditions => ["id in (?)", ids])
63
- end
56
+ module ClassMethods #:nodoc:
57
+ include Tenacity::OrmExt::Helpers
64
58
 
65
- def _t_find_first_by_associate(property, id)
66
- find(:first, :conditions => ["#{property} = ?", id.to_s])
67
- end
59
+ def _t_id_type
60
+ Integer
61
+ end
68
62
 
69
- def _t_find_all_by_associate(property, id)
70
- find(:all, :conditions => ["#{property} = ?", id.to_s])
71
- end
63
+ def _t_find(id)
64
+ find_by_id(_t_serialize(id))
65
+ end
72
66
 
73
- def _t_initialize_has_many_association(association)
74
- after_save { |record| record.class._t_save_associates(record, association) }
75
- end
67
+ def _t_find_bulk(ids)
68
+ return [] if ids.nil? || ids.empty?
69
+ find(:all, :conditions => ["id in (?)", _t_serialize_ids(ids)])
70
+ end
76
71
 
77
- def _t_initialize_belongs_to_association(association)
78
- before_save { |record| record.class._t_stringify_belongs_to_value(record, association) }
79
- end
72
+ def _t_find_first_by_associate(property, id)
73
+ find(:first, :conditions => ["#{property} = ?", _t_serialize(id)])
74
+ end
80
75
 
81
- def _t_delete(ids, run_callbacks=true)
82
- if run_callbacks
83
- destroy_all(["id in (?)", ids])
84
- else
85
- delete_all(["id in (?)", ids])
76
+ def _t_find_all_by_associate(property, id)
77
+ find(:all, :conditions => ["#{property} = ?", _t_serialize(id)])
86
78
  end
87
- end
88
- end
89
79
 
90
- module InstanceMethods #:nodoc:
91
- def _t_reload
92
- reload
93
- end
80
+ def _t_initialize_has_one_association(association)
81
+ after_destroy { |record| record._t_cleanup_has_one_association(association) }
82
+ end
94
83
 
95
- def _t_clear_associates(association)
96
- self.connection.execute("delete from #{association.join_table} where #{association.association_key} = #{self.id}")
97
- end
84
+ def _t_initialize_tenacity
85
+ after_save { |record| record._t_save_autosave_associations }
86
+ end
87
+
88
+ def _t_initialize_has_many_association(association)
89
+ after_save { |record| record.class._t_save_associates(record, association) }
90
+ after_destroy { |record| record._t_cleanup_has_many_association(association) }
91
+ end
92
+
93
+ def _t_initialize_belongs_to_association(association)
94
+ after_destroy { |record| record._t_cleanup_belongs_to_association(association) }
95
+ end
98
96
 
99
- def _t_associate_many(association, associate_ids)
100
- self.transaction do
101
- _t_clear_associates(association)
102
- associate_ids.each do |associate_id|
103
- self.connection.execute("insert into #{association.join_table} (#{association.association_key}, #{association.association_foreign_key}) values (#{self.id}, '#{associate_id}')")
97
+ def _t_delete(ids, run_callbacks=true)
98
+ if run_callbacks
99
+ destroy_all(["id in (?)", _t_serialize_ids(ids)])
100
+ else
101
+ delete_all(["id in (?)", _t_serialize_ids(ids)])
104
102
  end
105
103
  end
106
104
  end
107
105
 
108
- def _t_get_associate_ids(association)
109
- return [] if self.id.nil?
110
- rows = self.connection.execute("select #{association.association_foreign_key} from #{association.join_table} where #{association.association_key} = #{self.id}")
111
- ids = []; rows.each { |r| ids << r[0] }; ids
106
+ module InstanceMethods #:nodoc:
107
+ include Tenacity::OrmExt::Helpers
108
+
109
+ def _t_reload
110
+ reload
111
+ end
112
+
113
+ def _t_clear_associates(association)
114
+ self.connection.execute("delete from #{association.join_table} where #{association.association_key} = #{_t_serialize_id_for_sql(self.id)}")
115
+ end
116
+
117
+ def _t_associate_many(association, associate_ids)
118
+ self.transaction do
119
+ _t_clear_associates(association)
120
+ associate_ids.each do |associate_id|
121
+ self.connection.execute("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)})")
122
+ end
123
+ end
124
+ end
125
+
126
+ def _t_get_associate_ids(association)
127
+ return [] if self.id.nil?
128
+ rows = self.connection.execute("select #{association.association_foreign_key} from #{association.join_table} where #{association.association_key} = #{_t_serialize_id_for_sql(self.id)}")
129
+ ids = []; rows.each { |r| ids << r[0] }; ids
130
+ end
112
131
  end
113
- end
114
132
 
133
+ end
115
134
  end
116
135
  end