tenacity 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/EXTEND.rdoc +18 -21
- data/Gemfile +0 -2
- data/README.rdoc +3 -1
- data/Rakefile +7 -0
- data/history.txt +17 -0
- data/lib/tenacity.rb +4 -0
- data/lib/tenacity/associates_proxy.rb +5 -7
- data/lib/tenacity/association.rb +9 -47
- data/lib/tenacity/associations/belongs_to.rb +1 -2
- data/lib/tenacity/associations/has_many.rb +27 -21
- data/lib/tenacity/associations/has_one.rb +3 -2
- data/lib/tenacity/class_methods.rb +14 -60
- data/lib/tenacity/errors.rb +8 -0
- data/lib/tenacity/instance_methods.rb +42 -12
- data/lib/tenacity/orm_ext/activerecord.rb +11 -32
- data/lib/tenacity/orm_ext/couchrest.rb +14 -22
- data/lib/tenacity/orm_ext/datamapper.rb +14 -31
- data/lib/tenacity/orm_ext/helpers.rb +3 -3
- data/lib/tenacity/orm_ext/mongo_mapper.rb +16 -22
- data/lib/tenacity/orm_ext/mongoid.rb +10 -18
- data/lib/tenacity/orm_ext/ripple.rb +270 -0
- data/lib/tenacity/orm_ext/sequel.rb +21 -33
- data/lib/tenacity/orm_ext/toystore.rb +114 -0
- data/lib/tenacity/version.rb +1 -1
- data/tenacity.gemspec +10 -3
- data/test/association_features/belongs_to_test.rb +12 -0
- data/test/association_features/has_many_test.rb +32 -2
- data/test/association_features/has_one_test.rb +18 -4
- data/test/associations/has_one_test.rb +0 -1
- data/test/core/classmethods_test.rb +7 -0
- data/test/fixtures/active_record_has_many_target.rb +4 -0
- data/test/fixtures/active_record_has_one_target.rb +4 -0
- data/test/fixtures/active_record_object.rb +8 -0
- data/test/fixtures/couch_rest_has_many_target.rb +4 -0
- data/test/fixtures/couch_rest_has_one_target.rb +4 -0
- data/test/fixtures/couch_rest_object.rb +8 -0
- data/test/fixtures/data_mapper_has_many_target.rb +10 -0
- data/test/fixtures/data_mapper_has_one_target.rb +10 -0
- data/test/fixtures/data_mapper_object.rb +8 -0
- data/test/fixtures/mongo_mapper_has_many_target.rb +4 -0
- data/test/fixtures/mongo_mapper_has_one_target.rb +4 -0
- data/test/fixtures/mongo_mapper_object.rb +8 -0
- data/test/fixtures/mongoid_has_many_target.rb +4 -0
- data/test/fixtures/mongoid_has_one_target.rb +4 -0
- data/test/fixtures/mongoid_object.rb +8 -0
- data/test/fixtures/no_associations.rb +4 -0
- data/test/fixtures/ripple_has_many_target.rb +24 -0
- data/test/fixtures/ripple_has_one_target.rb +24 -0
- data/test/fixtures/ripple_object.rb +42 -0
- data/test/fixtures/sequel_has_many_target.rb +4 -0
- data/test/fixtures/sequel_has_one_target.rb +4 -0
- data/test/fixtures/sequel_object.rb +8 -0
- data/test/fixtures/toystore_has_many_target.rb +28 -0
- data/test/fixtures/toystore_has_one_target.rb +28 -0
- data/test/fixtures/toystore_object.rb +46 -0
- data/test/helpers/active_record_test_helper.rb +12 -95
- data/test/helpers/data_mapper_test_helper.rb +0 -64
- data/test/helpers/ripple_test_helper.rb +19 -0
- data/test/helpers/sequel_test_helper.rb +13 -60
- data/test/helpers/toystore_test_helper.rb +17 -0
- data/test/orm_ext/activerecord_test.rb +16 -26
- data/test/orm_ext/couchrest_test.rb +10 -29
- data/test/orm_ext/datamapper_test.rb +16 -29
- data/test/orm_ext/mongo_mapper_test.rb +11 -29
- data/test/orm_ext/mongoid_test.rb +11 -29
- data/test/orm_ext/ripple_test.rb +140 -0
- data/test/orm_ext/sequel_test.rb +15 -26
- data/test/orm_ext/toystore_test.rb +103 -0
- data/test/test_helper.rb +35 -23
- metadata +99 -133
data/.gitignore
CHANGED
data/EXTEND.rdoc
CHANGED
@@ -14,6 +14,11 @@ lib/tenacity/association.rb.
|
|
14
14
|
|
15
15
|
== Class Methods
|
16
16
|
|
17
|
+
_t_id_type
|
18
|
+
|
19
|
+
The type to use when storing instances of this class in a database.
|
20
|
+
Usually String or Integer.
|
21
|
+
|
17
22
|
_t_find(id)
|
18
23
|
|
19
24
|
Find an object by its id, and return it. If the object cannot be found,
|
@@ -34,8 +39,12 @@ and return it. If no object could be found, return nil.
|
|
34
39
|
|
35
40
|
Find all objects by the specified property name, with the specified id, and
|
36
41
|
return them in an array. If no objects could be found, return an empty array.
|
37
|
-
|
38
|
-
|
42
|
+
|
43
|
+
_t_find_all_ids_by_associate(property, id)
|
44
|
+
|
45
|
+
Find all ids of rows/documents/etc by the specified property name, with the
|
46
|
+
specified id, and return them in an array. If no data could be found, return
|
47
|
+
an empty array.
|
39
48
|
|
40
49
|
_t_delete(ids, run_callbacks=true)
|
41
50
|
|
@@ -44,6 +53,12 @@ objects should be deleted in such a way that their delete callback methods
|
|
44
53
|
are run. If false, the objects should be deleted in such a way that their
|
45
54
|
delete callback meethods are not run. Return nothing.
|
46
55
|
|
56
|
+
_t_initialize_tenacity
|
57
|
+
|
58
|
+
Perform any database client specific initialization necessary to support Tenacity
|
59
|
+
associations. This could include defining callbacks on the object required for
|
60
|
+
all association types.
|
61
|
+
|
47
62
|
_t_initialize_has_many_association(association)
|
48
63
|
|
49
64
|
Perform any database client specific initialization necessary to support a has
|
@@ -73,23 +88,5 @@ when the after the object has been deleted.
|
|
73
88
|
_t_reload
|
74
89
|
|
75
90
|
Reload the object from the database, overwriting the objects properties with
|
76
|
-
the data fetched from the database.
|
77
|
-
|
78
|
-
_t_associate_many(association, associate_ids)
|
79
|
-
|
80
|
-
Create has_many associations between this object and the objects with ids
|
81
|
-
specified in the array of associate_ids. This method could involve writing
|
82
|
-
the associate_ids into a join table, or into one of the object's properties.
|
83
|
-
Return nothing.
|
84
|
-
|
85
|
-
_t_get_associate_ids(association)
|
86
|
-
|
87
|
-
Get the ids of the objects associated with this object through the specified
|
88
|
-
association, and return them in an array. Return an empty array if there
|
89
|
-
are no associated objects.
|
90
|
-
|
91
|
-
_t_clear_associates(association)
|
92
|
-
|
93
|
-
Destroy the association between this object and its current associates through
|
94
|
-
the specified association. Return nothing.
|
91
|
+
the data fetched from the database. Returns the instance of the class (self).
|
95
92
|
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -87,7 +87,9 @@ much the same way, supporting many of the same options.
|
|
87
87
|
* DataMapper
|
88
88
|
* MongoMapper
|
89
89
|
* Mongoid
|
90
|
+
* Ripple
|
90
91
|
* Sequel
|
92
|
+
* Toystore
|
91
93
|
|
92
94
|
See EXTEND.rdoc for information on extending Tenacity to work with other database clients.
|
93
95
|
|
@@ -110,7 +112,7 @@ See EXTEND.rdoc for information on extending Tenacity to work with other databas
|
|
110
112
|
|
111
113
|
== Development
|
112
114
|
|
113
|
-
* SQLite, MongoDB, and CouchDB must be installed and
|
115
|
+
* SQLite, MongoDB, Riak, and CouchDB must be installed, configured, and running.
|
114
116
|
* Install the dependencies
|
115
117
|
|
116
118
|
bundle install
|
data/Rakefile
CHANGED
@@ -20,6 +20,7 @@ require 'yard'
|
|
20
20
|
task :default => :test
|
21
21
|
|
22
22
|
Rake::TestTask.new(:test) do |test|
|
23
|
+
ENV['TENACITY_TEST'] = 'true'
|
23
24
|
test.libs << 'lib' << 'test' << 'test/fixtures'
|
24
25
|
test.pattern = ENV['TEST'] || "test/**/*_test.rb"
|
25
26
|
test.verbose = true
|
@@ -31,6 +32,12 @@ task :quick_test do
|
|
31
32
|
Rake::Task["test"].invoke
|
32
33
|
end
|
33
34
|
|
35
|
+
desc 'Run the full test suite, testing all ORM/ODMs against each other'
|
36
|
+
task :long_test do
|
37
|
+
ENV['LONG'] = 'true'
|
38
|
+
Rake::Task["test"].invoke
|
39
|
+
end
|
40
|
+
|
34
41
|
Rcov::RcovTask.new do |test|
|
35
42
|
test.libs << 'test' << 'test/fixtures'
|
36
43
|
test.pattern = 'test/**/*_test.rb'
|
data/history.txt
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
== 0.5.0
|
2
|
+
|
3
|
+
* Major enhancements
|
4
|
+
|
5
|
+
* Added code to verify target object in a t_belongs_to association exists when
|
6
|
+
saving the source object
|
7
|
+
* Don't allow the source object in a t_has_one or t_has_many association to be
|
8
|
+
deleted if the target object in the association is holding its id
|
9
|
+
* Removed the need for join tables for t_has_many associations
|
10
|
+
* Added support for Ripple
|
11
|
+
* Added support for Toystore
|
12
|
+
|
13
|
+
* Bug fixes
|
14
|
+
|
15
|
+
* Fixed bug preventing an object including the Tenacity module from being saved
|
16
|
+
successfully if it had no associations
|
17
|
+
|
1
18
|
== 0.4.1
|
2
19
|
|
3
20
|
* Bug fixes
|
data/lib/tenacity.rb
CHANGED
@@ -15,7 +15,9 @@ require File.join(File.dirname(__FILE__), 'tenacity', 'orm_ext', 'couchrest')
|
|
15
15
|
require File.join(File.dirname(__FILE__), 'tenacity', 'orm_ext', 'datamapper')
|
16
16
|
require File.join(File.dirname(__FILE__), 'tenacity', 'orm_ext', 'mongo_mapper')
|
17
17
|
require File.join(File.dirname(__FILE__), 'tenacity', 'orm_ext', 'mongoid')
|
18
|
+
require File.join(File.dirname(__FILE__), 'tenacity', 'orm_ext', 'ripple')
|
18
19
|
require File.join(File.dirname(__FILE__), 'tenacity', 'orm_ext', 'sequel')
|
20
|
+
require File.join(File.dirname(__FILE__), 'tenacity', 'orm_ext', 'toystore')
|
19
21
|
|
20
22
|
module Tenacity #:nodoc:
|
21
23
|
include InstanceMethods
|
@@ -30,7 +32,9 @@ module Tenacity #:nodoc:
|
|
30
32
|
OrmExt::DataMapper.setup(model)
|
31
33
|
OrmExt::MongoMapper.setup(model)
|
32
34
|
OrmExt::Mongoid.setup(model)
|
35
|
+
OrmExt::Ripple.setup(model)
|
33
36
|
OrmExt::Sequel.setup(model)
|
37
|
+
OrmExt::Toystore.setup(model)
|
34
38
|
|
35
39
|
raise "Tenacity does not support the database client used by #{model}" unless model.respond_to?(:_t_find)
|
36
40
|
model.extend(ClassMethods)
|
@@ -36,13 +36,13 @@ module Tenacity
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def destroy_all
|
39
|
-
|
40
|
-
@association.associate_class._t_delete(
|
39
|
+
remove_associates_from_parent
|
40
|
+
@association.associate_class._t_delete(@parent._t_get_associate_ids(@association))
|
41
41
|
end
|
42
42
|
|
43
43
|
def delete_all
|
44
|
-
|
45
|
-
@association.associate_class._t_delete(
|
44
|
+
remove_associates_from_parent
|
45
|
+
@association.associate_class._t_delete(@parent._t_get_associate_ids(@association), false)
|
46
46
|
end
|
47
47
|
|
48
48
|
def inspect
|
@@ -51,11 +51,9 @@ module Tenacity
|
|
51
51
|
|
52
52
|
private
|
53
53
|
|
54
|
-
def
|
55
|
-
ids = @parent._t_get_associate_ids(@association)
|
54
|
+
def remove_associates_from_parent
|
56
55
|
@parent._t_remove_associates(@association)
|
57
56
|
@parent.save
|
58
|
-
ids
|
59
57
|
end
|
60
58
|
|
61
59
|
def method_missing(method, *args)
|
data/lib/tenacity/association.rb
CHANGED
@@ -35,6 +35,9 @@ module Tenacity
|
|
35
35
|
# Is this association a polymorphic association?
|
36
36
|
attr_reader :polymorphic
|
37
37
|
|
38
|
+
# Should this association disable foreign key like constraints
|
39
|
+
attr_reader :disable_foreign_key_constraints
|
40
|
+
|
38
41
|
def initialize(type, name, source, options={})
|
39
42
|
@type = type
|
40
43
|
@name = name
|
@@ -47,10 +50,6 @@ module Tenacity
|
|
47
50
|
end
|
48
51
|
|
49
52
|
@foreign_key = options[:foreign_key]
|
50
|
-
@foreign_keys_property = options[:foreign_keys_property]
|
51
|
-
@join_table = options[:join_table]
|
52
|
-
@association_key = options[:association_key]
|
53
|
-
@association_foreign_key = options[:association_foreign_key]
|
54
53
|
@dependent = options[:dependent]
|
55
54
|
@readonly = options[:readonly]
|
56
55
|
@limit = options[:limit]
|
@@ -58,12 +57,7 @@ module Tenacity
|
|
58
57
|
@autosave = options[:autosave]
|
59
58
|
@polymorphic = options[:polymorphic]
|
60
59
|
@as = options[:as]
|
61
|
-
|
62
|
-
if @foreign_keys_property
|
63
|
-
if @foreign_keys_property.to_s == ActiveSupport::Inflector.singularize(name) + "_ids"
|
64
|
-
raise "#{ActiveSupport::Inflector.singularize(name) + "_ids"} is an invalid foreign keys property name"
|
65
|
-
end
|
66
|
-
end
|
60
|
+
@disable_foreign_key_constraints = options[:disable_foreign_key_constraints]
|
67
61
|
end
|
68
62
|
|
69
63
|
# The name of the association
|
@@ -93,35 +87,6 @@ module Tenacity
|
|
93
87
|
end
|
94
88
|
end
|
95
89
|
|
96
|
-
# Get the property name used to store the foreign key
|
97
|
-
def foreign_keys_property
|
98
|
-
@foreign_keys_property || "t_" + ActiveSupport::Inflector.singularize(name) + "_ids"
|
99
|
-
end
|
100
|
-
|
101
|
-
# Get the name of the join table used by this association
|
102
|
-
def join_table
|
103
|
-
table_name = fetch_table_name
|
104
|
-
|
105
|
-
if @type == :t_has_many && polymorphic?
|
106
|
-
association_name_for_join_table = name.to_s.pluralize
|
107
|
-
else
|
108
|
-
association_name_for_join_table = name
|
109
|
-
end
|
110
|
-
|
111
|
-
@join_table || (name.to_s < table_name ? "#{association_name_for_join_table}_#{table_name}" : "#{table_name}_#{association_name_for_join_table}")
|
112
|
-
end
|
113
|
-
|
114
|
-
# Get the name of the column in the join table that represents this object
|
115
|
-
def association_key
|
116
|
-
table_name = fetch_table_name
|
117
|
-
@association_key || table_name.singularize + '_id'
|
118
|
-
end
|
119
|
-
|
120
|
-
# Get the name of the column in the join table that represents the associated object
|
121
|
-
def association_foreign_key
|
122
|
-
@association_foreign_key || name.to_s.singularize + '_id'
|
123
|
-
end
|
124
|
-
|
125
90
|
# Are the associated objects read only?
|
126
91
|
def readonly?
|
127
92
|
@readonly == true
|
@@ -137,16 +102,13 @@ module Tenacity
|
|
137
102
|
(name.to_s + "_type").to_sym
|
138
103
|
end
|
139
104
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
if @source.respond_to?(:table_name)
|
144
|
-
@source.table_name.to_s
|
145
|
-
else
|
146
|
-
"#{ActiveSupport::Inflector.underscore(@source)}s"
|
147
|
-
end
|
105
|
+
# Are foreign key constraints enabled for this association?
|
106
|
+
def foreign_key_constraints_enabled?
|
107
|
+
@disable_foreign_key_constraints != true
|
148
108
|
end
|
149
109
|
|
110
|
+
private
|
111
|
+
|
150
112
|
def belongs_to_foreign_key
|
151
113
|
if polymorphic?
|
152
114
|
(name.to_s + "_id").to_sym
|
@@ -18,8 +18,7 @@ module Tenacity
|
|
18
18
|
def belongs_to_associate(association)
|
19
19
|
associate_id = self.send(association.foreign_key)
|
20
20
|
clazz = association.associate_class(self)
|
21
|
-
|
22
|
-
associate
|
21
|
+
clazz._t_find(associate_id)
|
23
22
|
end
|
24
23
|
|
25
24
|
def set_belongs_to_associate(association, associate)
|
@@ -3,8 +3,7 @@ module Tenacity
|
|
3
3
|
module HasMany #:nodoc:
|
4
4
|
|
5
5
|
def _t_remove_associates(association)
|
6
|
-
instance_variable_set
|
7
|
-
_t_clear_associates(association)
|
6
|
+
instance_variable_set(_t_ivar_name(association), [])
|
8
7
|
end
|
9
8
|
|
10
9
|
def _t_cleanup_has_many_association(association)
|
@@ -19,10 +18,23 @@ module Tenacity
|
|
19
18
|
associate.send "#{association.foreign_key(self.class)}=", nil
|
20
19
|
associate.save
|
21
20
|
end
|
21
|
+
elsif association.foreign_key_constraints_enabled?
|
22
|
+
raise ObjectIdInUseError.new("Unable to delete #{self.class} with id of #{self.id} because its id is being referenced by instances of #{associates.first.class}(id: #{associates.map(&:id).join(',')})!")
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
27
|
+
def _t_get_associate_ids(association)
|
28
|
+
if self.id.nil?
|
29
|
+
[]
|
30
|
+
else
|
31
|
+
foreign_key = association.foreign_key(self.class)
|
32
|
+
associate_id = self.class._t_serialize_ids(self.id, association)
|
33
|
+
ids = association.associate_class._t_find_all_ids_by_associate(foreign_key, associate_id)
|
34
|
+
self.class._t_serialize_ids(ids, association)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
26
38
|
private
|
27
39
|
|
28
40
|
def has_many_associates(association)
|
@@ -43,7 +55,7 @@ module Tenacity
|
|
43
55
|
|
44
56
|
def set_has_many_associate_ids(association, associate_ids)
|
45
57
|
clazz = association.associate_class
|
46
|
-
instance_variable_set
|
58
|
+
instance_variable_set(_t_ivar_name(association), clazz._t_find_bulk(associate_ids))
|
47
59
|
end
|
48
60
|
|
49
61
|
def save_without_callback
|
@@ -81,24 +93,12 @@ module Tenacity
|
|
81
93
|
# place of the associated objects. The actual associated objects
|
82
94
|
# will be fetched the first time they are needed. So, force them to
|
83
95
|
# be fetched here, before we clear them out in the database.
|
84
|
-
old_associates.
|
96
|
+
old_associates.first
|
85
97
|
|
86
98
|
_t_clear_old_associations(record, association)
|
87
99
|
|
88
|
-
associates = (record.instance_variable_get
|
89
|
-
|
90
|
-
associate._t_reload
|
91
|
-
associate.send("#{association.foreign_key(record.class)}=", _t_serialize(record.id, association))
|
92
|
-
associate.send "#{association.polymorphic_type}=", self.to_s if association.polymorphic?
|
93
|
-
save_associate(associate)
|
94
|
-
end
|
95
|
-
|
96
|
-
unless associates.blank?
|
97
|
-
associate_ids = associates.map { |associate| _t_serialize(associate.id) }
|
98
|
-
record._t_associate_many(association, associate_ids)
|
99
|
-
save_associate(record)
|
100
|
-
end
|
101
|
-
|
100
|
+
associates = (record.instance_variable_get(record._t_ivar_name(association))) || []
|
101
|
+
establish_relationship_in_target_objects(record, association, associates)
|
102
102
|
destroy_orphaned_associates(association, old_associates, associates)
|
103
103
|
end
|
104
104
|
|
@@ -109,9 +109,6 @@ module Tenacity
|
|
109
109
|
old_associate.send("#{property_name}=", nil)
|
110
110
|
save_associate(old_associate)
|
111
111
|
end
|
112
|
-
|
113
|
-
record._t_clear_associates(association)
|
114
|
-
save_associate(record)
|
115
112
|
end
|
116
113
|
|
117
114
|
def save_associate(associate)
|
@@ -132,6 +129,15 @@ module Tenacity
|
|
132
129
|
end
|
133
130
|
end
|
134
131
|
end
|
132
|
+
|
133
|
+
def establish_relationship_in_target_objects(record, association, associates)
|
134
|
+
associates.each do |a|
|
135
|
+
associate = a._t_reload
|
136
|
+
associate.send("#{association.foreign_key(record.class)}=", _t_serialize(record.id, association))
|
137
|
+
associate.send "#{association.polymorphic_type}=", self.to_s if association.polymorphic?
|
138
|
+
save_associate(associate)
|
139
|
+
end
|
140
|
+
end
|
135
141
|
end
|
136
142
|
|
137
143
|
end
|
@@ -12,6 +12,8 @@ module Tenacity
|
|
12
12
|
elsif association.dependent == :nullify
|
13
13
|
associate.send "#{association.foreign_key(self.class)}=", nil
|
14
14
|
associate.save
|
15
|
+
elsif association.foreign_key_constraints_enabled?
|
16
|
+
raise ObjectIdInUseError.new("Unable to delete #{self.class} with id of #{self.id} because its id is being referenced by an instance of #{associate.class}(id: #{associate.id})!")
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
@@ -20,8 +22,7 @@ module Tenacity
|
|
20
22
|
|
21
23
|
def has_one_associate(association)
|
22
24
|
clazz = association.associate_class
|
23
|
-
|
24
|
-
associate
|
25
|
+
clazz._t_find_first_by_associate(association.foreign_key(self.class), _t_serialize(self.id, association))
|
25
26
|
end
|
26
27
|
|
27
28
|
def set_has_one_associate(association, associate)
|
@@ -140,34 +140,7 @@ module Tenacity
|
|
140
140
|
# project.milestones(true).size # fetches milestones from the database
|
141
141
|
# project.milestones # uses the milestone cache
|
142
142
|
#
|
143
|
-
# == Join Tables
|
144
|
-
#
|
145
|
-
# One-to-many assocations that contain a relational database backed object as one of
|
146
|
-
# the assocaites are implemented using an intermediate join table. This differs from
|
147
|
-
# ActiveRecord::Associations, where only many-to-many relationships are implemented
|
148
|
-
# using an intermediate join table.
|
149
|
-
#
|
150
|
-
# Tenacity will not create the join table. It assume one exists, and is named properly.
|
151
|
-
# Unless the join table is explicitly specified as an option, it is guessed using the
|
152
|
-
# lexical order of the class names. So a join between Developer and Project will give
|
153
|
-
# the default join table name of "developers_projects" because "D" outranks "P". Note
|
154
|
-
# that this precedence is calculated using the < operator for String. This means that
|
155
|
-
# if the strings are of different lengths, and the strings are equal when compared up
|
156
|
-
# to the shortest length, then the longer string is considered of higher lexical
|
157
|
-
# precedence than the shorter one. For example, one would expect the tables "paper_boxes"
|
158
|
-
# and "papers" to generate a join table name of "papers_paper_boxes" because of the
|
159
|
-
# length of the name "paper_boxes", but it in fact generates a join table name of
|
160
|
-
# "paper_boxes_papers". Be aware of this caveat, and use the custom :join_table option
|
161
|
-
# if you need to.
|
162
|
-
#
|
163
|
-
# The column names used in the join table are guessed to be the names of the associated
|
164
|
-
# classes, suffixed with "_id". For example, the "developers_projects" join table
|
165
|
-
# mentioned above is expected to have a column named "developer_id" and a column named
|
166
|
-
# "project_id". The <tt>:associate_key</tt> and <tt>:associate_foreign_key</tt> options
|
167
|
-
# can be used to override these defaults.
|
168
|
-
#
|
169
143
|
module ClassMethods
|
170
|
-
attr_reader :_tenacity_associations
|
171
144
|
|
172
145
|
# Specifies a one-to-one association with another class. This method should only be used
|
173
146
|
# if the other class contains the foreign key. If the current class contains the foreign key,
|
@@ -210,6 +183,9 @@ module Tenacity
|
|
210
183
|
# If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
|
211
184
|
# [:as]
|
212
185
|
# Specifies a polymorphic interface (See <tt>t_belongs_to</tt>).
|
186
|
+
# [:disable_foreign_key_constraints]
|
187
|
+
# If true, bypass foreign key constraints, like verifying no other objects are storing the key of the source object
|
188
|
+
# before deleting it. Defaults to false.
|
213
189
|
#
|
214
190
|
# Option examples:
|
215
191
|
# t_has_one :credit_card, :dependent => :destroy # destroys the associated credit card
|
@@ -282,6 +258,9 @@ module Tenacity
|
|
282
258
|
# [:polymorphic]
|
283
259
|
# Specify this association is a polymorphic association by passing +true+. (*Note*: IDs for polymorphic associations are always
|
284
260
|
# stored as strings in the database.)
|
261
|
+
# [:disable_foreign_key_constraints]
|
262
|
+
# If true, bypass foreign key constraints, like verifying the target object exists when the relationship is created.
|
263
|
+
# Defaults to false.
|
285
264
|
#
|
286
265
|
# Option examples:
|
287
266
|
# t_belongs_to :project_manager, :class_name => "Person"
|
@@ -307,10 +286,7 @@ module Tenacity
|
|
307
286
|
end
|
308
287
|
end
|
309
288
|
|
310
|
-
# Specifies a one-to-many association.
|
311
|
-
# relational database backed object as one of the associates are implemented
|
312
|
-
# using an intermediate join table. See the Join Tables section at the top
|
313
|
-
# for more information.
|
289
|
+
# Specifies a one-to-many association.
|
314
290
|
#
|
315
291
|
# The following methods for retrieval and query of collections of associated objects will be added:
|
316
292
|
#
|
@@ -375,37 +351,12 @@ module Tenacity
|
|
375
351
|
# Specify the foreign key used for the association. By default this is guessed to be the name
|
376
352
|
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +t_has_many+
|
377
353
|
# association will use "person_id" as the default <tt>:foreign_key</tt>.
|
378
|
-
# [:foreign_keys_property]
|
379
|
-
# Specify the name of the property that stores the ids of the associated objects. By default
|
380
|
-
# this is guessed to be the name of the association with a "t_" prefix and an "_ids" suffix.
|
381
|
-
# So a class that defines a <tt>t_has_many :people</tt> association will use t_people_ids as
|
382
|
-
# the property to store the ids of the associated People objects. This option is only valid
|
383
|
-
# for objects that store associated ids in an array instaed of a join table (CouchRest,
|
384
|
-
# MongoMapper, etc). <b>WARNING:</b> The name of the association with an "_ids" suffix should
|
385
|
-
# not be used as the property name, since tenacity adds a method with this name to the object.
|
386
354
|
# [:dependent]
|
387
355
|
# If set to <tt>:destroy</tt> all the associated objects are deleted alongside this object
|
388
356
|
# in addition to calling their delete callbacks. If set to <tt>:delete_all</tt> all
|
389
357
|
# associated objects are deleted *without* calling their delete callbacks. If set to
|
390
358
|
# <tt>:nullify</tt> all associated objects' foreign keys are set to +NULL+ *without* calling
|
391
359
|
# their save backs.
|
392
|
-
# [:join_table]
|
393
|
-
# Specify the name of the join table if the default based on lexical order isn't what you want.
|
394
|
-
# This option is only valid if one of the models in the association is backed by a relational
|
395
|
-
# database.
|
396
|
-
# [:association_foreign_key]
|
397
|
-
# Specify the foreign key in the join table used for the association on the receiving side of
|
398
|
-
# the association. By default this is guessed to be the name of the associated class in
|
399
|
-
# lower-case and "_id" suffixed. So if a Person class makes a +t_has_many+ association to
|
400
|
-
# Project, the association will use "project_id" as the default <tt>:association_foreign_key</tt>.
|
401
|
-
# This option is only valid if one of the associated objects is backed by a relational
|
402
|
-
# database.
|
403
|
-
# [:association_key]
|
404
|
-
# Specify the key in the join table used for the association on the declaring side of
|
405
|
-
# the association. By default this is guessed to be the name of this class in lower-case and
|
406
|
-
# "_id" suffixed. So if a Person class makes a +t_has_many+ association to Project, the
|
407
|
-
# association will use "person_id" as the default <tt>:association_key</tt>. This option is
|
408
|
-
# only valid if one of the associated objects is backed by a relational database.
|
409
360
|
# [:readonly]
|
410
361
|
# If true, all the associated objects are readonly through the association.
|
411
362
|
# [:limit]
|
@@ -418,14 +369,13 @@ module Tenacity
|
|
418
369
|
# If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default.
|
419
370
|
# [:as]
|
420
371
|
# Specifies a polymorphic interface (See <tt>t_belongs_to</tt>).
|
372
|
+
# [:disable_foreign_key_constraints]
|
373
|
+
# If true, bypass foreign key constraints, like verifying no other objects are storing the key of the source object
|
374
|
+
# before deleting it. Defaults to false.
|
421
375
|
#
|
422
376
|
# Option examples:
|
423
377
|
# t_has_many :products, :class_name => "SpecialProduct"
|
424
378
|
# t_has_many :engineers, :foreign_key => "project_id" # within class named SecretProject
|
425
|
-
# t_has_many :engineers, :foreign_keys_property => "worker_ids"
|
426
|
-
# t_has_many :managers, :join_table => "project_managers_and_projects"
|
427
|
-
# t_has_many :managers, :join_table => "project_managers_and_projects",
|
428
|
-
# :association_foreign_key => "mgr_id", :association_key => "proj_id"
|
429
379
|
# t_has_many :tasks, :dependent => :destroy
|
430
380
|
# t_has_many :reports, :readonly => true
|
431
381
|
# t_has_many :tags, :as => :taggable
|
@@ -474,6 +424,10 @@ module Tenacity
|
|
474
424
|
end
|
475
425
|
end
|
476
426
|
|
427
|
+
def _tenacity_associations
|
428
|
+
@_tenacity_associations || []
|
429
|
+
end
|
430
|
+
|
477
431
|
private
|
478
432
|
|
479
433
|
def _t_create_association(type, name, options) #:nococ:
|