tenacity 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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