tenacity 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- data/EXTEND.rdoc +10 -3
- data/Gemfile +1 -2
- data/README.rdoc +3 -2
- data/Rakefile +6 -0
- data/history.txt +11 -0
- data/lib/tenacity/associate_proxy.rb +1 -1
- data/lib/tenacity/associates_proxy.rb +4 -4
- data/lib/tenacity/associations/has_many.rb +7 -8
- data/lib/tenacity/associations/has_one.rb +4 -4
- data/lib/tenacity/class_methods.rb +1 -1
- data/lib/tenacity/errors.rb +4 -8
- data/lib/tenacity/instance_methods.rb +2 -2
- data/lib/tenacity/orm_ext/activerecord.rb +5 -1
- data/lib/tenacity/orm_ext/couchrest.rb +4 -0
- data/lib/tenacity/orm_ext/datamapper.rb +5 -1
- data/lib/tenacity/orm_ext/helpers.rb +3 -3
- data/lib/tenacity/orm_ext/mongo_mapper.rb +4 -0
- data/lib/tenacity/orm_ext/mongoid.rb +4 -0
- data/lib/tenacity/orm_ext/ripple.rb +4 -0
- data/lib/tenacity/orm_ext/sequel.rb +5 -1
- data/lib/tenacity/orm_ext/toystore.rb +4 -0
- data/lib/tenacity/version.rb +1 -1
- data/test/associations/belongs_to_test.rb +3 -3
- data/test/associations/has_many_test.rb +6 -6
- data/test/associations/has_one_test.rb +4 -4
- data/test/core/classmethods_test.rb +14 -0
- data/test/fixtures/active_record_object_with_string_id.rb +5 -0
- data/test/fixtures/couch_rest_object.rb +2 -0
- data/test/fixtures/data_mapper_object.rb +1 -0
- data/test/fixtures/data_mapper_object_with_string_id.rb +6 -0
- data/test/fixtures/mongo_mapper_object.rb +4 -0
- data/test/fixtures/mongoid_object.rb +2 -0
- data/test/fixtures/ripple_object.rb +2 -0
- data/test/fixtures/sequel_object_with_string_id.rb +3 -0
- data/test/fixtures/toystore_object.rb +2 -0
- data/test/helpers/active_record_test_helper.rb +4 -0
- data/test/helpers/data_mapper_test_helper.rb +1 -0
- data/test/helpers/sequel_test_helper.rb +5 -0
- data/test/orm_ext/activerecord_test.rb +16 -0
- data/test/orm_ext/datamapper_test.rb +16 -0
- data/test/orm_ext/mongo_mapper_test.rb +11 -0
- data/test/orm_ext/mongoid_test.rb +11 -0
- data/test/orm_ext/ripple_test.rb +147 -134
- data/test/orm_ext/sequel_test.rb +16 -0
- data/test/orm_ext/toystore_test.rb +11 -0
- data/test/test_helper.rb +8 -8
- metadata +46 -152
data/EXTEND.rdoc
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
For Tenacity to interact with a database client, the client needs to be extended
|
2
2
|
to support the methods listed below. Beyond that, no additional configuration
|
3
|
-
or code is necessary. Tenacity communicates with the client using these
|
4
|
-
|
3
|
+
or code is necessary. Tenacity communicates with the client using only these
|
4
|
+
methods, so as long as they have been implemented and are available on the model
|
5
5
|
object, Tenacity will be able to manage the object's relationships.
|
6
6
|
|
7
7
|
|
@@ -16,7 +16,7 @@ lib/tenacity/association.rb.
|
|
16
16
|
|
17
17
|
_t_id_type
|
18
18
|
|
19
|
-
The type to use when storing instances of this class in a database.
|
19
|
+
The type to use when storing ids from instances of this class in a database.
|
20
20
|
Usually String or Integer.
|
21
21
|
|
22
22
|
_t_find(id)
|
@@ -90,3 +90,10 @@ when the after the object has been deleted.
|
|
90
90
|
Reload the object from the database, overwriting the objects properties with
|
91
91
|
the data fetched from the database. Returns the instance of the class (self).
|
92
92
|
|
93
|
+
_t_save_if_dirty
|
94
|
+
|
95
|
+
Save the object to the database only if the object has changed. This is solely
|
96
|
+
for performance. If the client library does not support dirty tracking, this
|
97
|
+
method can simply save the object. Returns the result of the save operation
|
98
|
+
if the object was saved, or false if the object was not saved.
|
99
|
+
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -32,7 +32,7 @@ much the same way, supporting many of the same options.
|
|
32
32
|
t_belongs_to :car
|
33
33
|
end
|
34
34
|
|
35
|
-
class Dashboard < CouchRest::
|
35
|
+
class Dashboard < CouchRest::Model::Base
|
36
36
|
include Tenacity
|
37
37
|
use_database MY_COUCHDB_DATABASE
|
38
38
|
|
@@ -83,7 +83,7 @@ much the same way, supporting many of the same options.
|
|
83
83
|
== Supported Database Clients
|
84
84
|
|
85
85
|
* ActiveRecord
|
86
|
-
* CouchRest (
|
86
|
+
* CouchRest (CouchRest::Model and ExtendedDocument)
|
87
87
|
* DataMapper
|
88
88
|
* MongoMapper
|
89
89
|
* Mongoid
|
@@ -107,6 +107,7 @@ See EXTEND.rdoc for information on extending Tenacity to work with other databas
|
|
107
107
|
* Start a feature/bugfix branch
|
108
108
|
* Commit and push until you are happy with your contribution
|
109
109
|
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
110
|
+
* Run the tests in ruby 1.8.7 and 1.9.2 on the master and active_support_2_x branches
|
110
111
|
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
111
112
|
|
112
113
|
|
data/Rakefile
CHANGED
@@ -38,6 +38,12 @@ task :long_test do
|
|
38
38
|
Rake::Task["test"].invoke
|
39
39
|
end
|
40
40
|
|
41
|
+
# Usage: rake 'single_test["test/association_features/belongs_to_test.rb", "/memoize the association/"]'
|
42
|
+
desc "Run a single test"
|
43
|
+
task :single_test, [:test_file, :test_name] do |test, args|
|
44
|
+
system "ruby -I'lib:lib:test:test/fixtures' #{args['test_file']} -n #{args['test_name']}"
|
45
|
+
end
|
46
|
+
|
41
47
|
Rcov::RcovTask.new do |test|
|
42
48
|
test.libs << 'test' << 'test/fixtures'
|
43
49
|
test.pattern = 'test/**/*_test.rb'
|
data/history.txt
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
== 0.5.2
|
2
|
+
|
3
|
+
* Bug fixes
|
4
|
+
|
5
|
+
* Changed relational DB clients to evaluate the schema to determine the type
|
6
|
+
of the primary key, instead of just assuming it is an Integer
|
7
|
+
|
8
|
+
* Minor enhancements
|
9
|
+
|
10
|
+
* Performance optimizations
|
11
|
+
|
1
12
|
== 0.5.1
|
2
13
|
|
3
14
|
* Bug fixes
|
@@ -19,18 +19,18 @@ module Tenacity
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def <<(object)
|
22
|
-
object.
|
22
|
+
object._t_save_if_dirty unless @parent.id.nil?
|
23
23
|
@target << AssociateProxy.new(object, @association)
|
24
24
|
end
|
25
25
|
|
26
26
|
def push(*objects)
|
27
|
-
objects.each { |object| object.
|
27
|
+
objects.each { |object| object._t_save_if_dirty } unless @parent.id.nil?
|
28
28
|
proxies = objects.map { |object| AssociateProxy.new(object, @association) }
|
29
29
|
@target.push(*proxies)
|
30
30
|
end
|
31
31
|
|
32
32
|
def concat(objects)
|
33
|
-
objects.each { |object| object.
|
33
|
+
objects.each { |object| object._t_save_if_dirty } unless @parent.id.nil?
|
34
34
|
proxies = objects.map { |object| AssociateProxy.new(object, @association) }
|
35
35
|
@target.concat(proxies)
|
36
36
|
end
|
@@ -53,7 +53,7 @@ module Tenacity
|
|
53
53
|
|
54
54
|
def remove_associates_from_parent
|
55
55
|
@parent._t_remove_associates(@association)
|
56
|
-
@parent.
|
56
|
+
@parent._t_save_if_dirty
|
57
57
|
end
|
58
58
|
|
59
59
|
def method_missing(method, *args)
|
@@ -10,13 +10,13 @@ module Tenacity
|
|
10
10
|
associates = has_many_associates(association)
|
11
11
|
unless associates.nil? || associates.empty?
|
12
12
|
if association.dependent == :destroy
|
13
|
-
associates.each { |associate| association.associate_class._t_delete(
|
13
|
+
associates.each { |associate| association.associate_class._t_delete(_t_serialize(associate.id)) }
|
14
14
|
elsif association.dependent == :delete_all
|
15
|
-
associates.each { |associate| association.associate_class._t_delete(
|
15
|
+
associates.each { |associate| association.associate_class._t_delete(_t_serialize(associate.id), false) }
|
16
16
|
elsif association.dependent == :nullify
|
17
17
|
associates.each do |associate|
|
18
18
|
associate.send "#{association.foreign_key(self.class)}=", nil
|
19
|
-
associate.
|
19
|
+
associate._t_save_if_dirty
|
20
20
|
end
|
21
21
|
elsif association.foreign_key_constraints_enabled?
|
22
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(',')})!")
|
@@ -60,7 +60,7 @@ module Tenacity
|
|
60
60
|
|
61
61
|
def save_without_callback
|
62
62
|
@perform_save_associates_callback = false
|
63
|
-
|
63
|
+
_t_save_if_dirty
|
64
64
|
ensure
|
65
65
|
@perform_save_associates_callback = true
|
66
66
|
end
|
@@ -95,16 +95,15 @@ module Tenacity
|
|
95
95
|
# be fetched here, before we clear them out in the database.
|
96
96
|
old_associates.first
|
97
97
|
|
98
|
-
_t_clear_old_associations(record, association)
|
98
|
+
_t_clear_old_associations(record, association, old_associates)
|
99
99
|
|
100
100
|
associates = (record.instance_variable_get(record._t_ivar_name(association))) || []
|
101
101
|
establish_relationship_in_target_objects(record, association, associates)
|
102
102
|
destroy_orphaned_associates(association, old_associates, associates)
|
103
103
|
end
|
104
104
|
|
105
|
-
def _t_clear_old_associations(record, association)
|
105
|
+
def _t_clear_old_associations(record, association, old_associates)
|
106
106
|
property_name = association.foreign_key(record.class)
|
107
|
-
old_associates = get_current_associates(record, association)
|
108
107
|
old_associates.each do |old_associate|
|
109
108
|
old_associate.send("#{property_name}=", nil)
|
110
109
|
save_associate(old_associate)
|
@@ -112,7 +111,7 @@ module Tenacity
|
|
112
111
|
end
|
113
112
|
|
114
113
|
def save_associate(associate)
|
115
|
-
associate.respond_to?(:_t_save_without_callback) ? associate._t_save_without_callback : associate.
|
114
|
+
associate.respond_to?(:_t_save_without_callback) ? associate._t_save_without_callback : associate._t_save_if_dirty
|
116
115
|
end
|
117
116
|
|
118
117
|
def get_current_associates(record, association)
|
@@ -6,12 +6,12 @@ module Tenacity
|
|
6
6
|
associate = has_one_associate(association)
|
7
7
|
unless associate.nil?
|
8
8
|
if association.dependent == :destroy
|
9
|
-
association.associate_class._t_delete(
|
9
|
+
association.associate_class._t_delete(_t_serialize(associate.id))
|
10
10
|
elsif association.dependent == :delete
|
11
|
-
association.associate_class._t_delete(
|
11
|
+
association.associate_class._t_delete(_t_serialize(associate.id), false)
|
12
12
|
elsif association.dependent == :nullify
|
13
13
|
associate.send "#{association.foreign_key(self.class)}=", nil
|
14
|
-
associate.
|
14
|
+
associate._t_save_if_dirty
|
15
15
|
elsif association.foreign_key_constraints_enabled?
|
16
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})!")
|
17
17
|
end
|
@@ -28,7 +28,7 @@ module Tenacity
|
|
28
28
|
def set_has_one_associate(association, associate)
|
29
29
|
associate.send "#{association.foreign_key(self.class)}=", _t_serialize(self.id, association)
|
30
30
|
associate.send "#{association.polymorphic_type}=", self.class.to_s if association.polymorphic?
|
31
|
-
associate.
|
31
|
+
associate._t_save_if_dirty unless association.autosave == false
|
32
32
|
associate
|
33
33
|
end
|
34
34
|
|
data/lib/tenacity/errors.rb
CHANGED
@@ -1,17 +1,13 @@
|
|
1
1
|
module Tenacity
|
2
2
|
# Generic Tenacity exception class.
|
3
|
-
class TenacityError < StandardError
|
4
|
-
end
|
3
|
+
class TenacityError < StandardError; end
|
5
4
|
|
6
5
|
# Raised on attempt to update an associate that is instantiated as read only.
|
7
|
-
class ReadOnlyError < TenacityError
|
8
|
-
end
|
6
|
+
class ReadOnlyError < TenacityError; end
|
9
7
|
|
10
8
|
# Raised when one of the objects specified in the relationship does not exist in the database
|
11
|
-
class ObjectDoesNotExistError < TenacityError
|
12
|
-
end
|
9
|
+
class ObjectDoesNotExistError < TenacityError; end
|
13
10
|
|
14
11
|
# Rasied when an attempt is made to delete an object whose id is in use by an association
|
15
|
-
class ObjectIdInUseError < TenacityError
|
16
|
-
end
|
12
|
+
class ObjectIdInUseError < TenacityError; end
|
17
13
|
end
|
@@ -35,7 +35,7 @@ module Tenacity
|
|
35
35
|
private
|
36
36
|
|
37
37
|
def autosave_save_or_destroy(associate)
|
38
|
-
associate.marked_for_destruction? ? autosave_destroy(associate) : associate.
|
38
|
+
associate.marked_for_destruction? ? autosave_destroy(associate) : associate._t_save_if_dirty
|
39
39
|
end
|
40
40
|
|
41
41
|
def autosave_destroy(associate)
|
@@ -48,7 +48,7 @@ module Tenacity
|
|
48
48
|
has_one_associate = associate.has_one_associate(association)
|
49
49
|
if has_one_associate
|
50
50
|
has_one_associate.send "#{association.foreign_key(associate.class)}=", nil
|
51
|
-
has_one_associate.
|
51
|
+
has_one_associate._t_save_if_dirty
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -49,7 +49,7 @@ module Tenacity
|
|
49
49
|
include Tenacity::OrmExt::Helpers
|
50
50
|
|
51
51
|
def _t_id_type
|
52
|
-
|
52
|
+
@_t_id_type_clazz ||= Kernel.const_get(columns.find{ |x| x.primary }.type.to_s.capitalize)
|
53
53
|
end
|
54
54
|
|
55
55
|
def _t_find(id)
|
@@ -107,6 +107,10 @@ module Tenacity
|
|
107
107
|
reload
|
108
108
|
self
|
109
109
|
end
|
110
|
+
|
111
|
+
def _t_save_if_dirty
|
112
|
+
changed? ? save : false
|
113
|
+
end
|
110
114
|
end
|
111
115
|
|
112
116
|
end
|
@@ -49,7 +49,7 @@ module Tenacity
|
|
49
49
|
include Tenacity::OrmExt::Helpers
|
50
50
|
|
51
51
|
def _t_id_type
|
52
|
-
|
52
|
+
@_t_id_type_clazz ||= properties.find{ |x| x.key? }.primitive
|
53
53
|
end
|
54
54
|
|
55
55
|
def _t_find(id)
|
@@ -134,6 +134,10 @@ module Tenacity
|
|
134
134
|
self.class._t_find(self.id)
|
135
135
|
end
|
136
136
|
|
137
|
+
def _t_save_if_dirty
|
138
|
+
dirty? ? save : false
|
139
|
+
end
|
140
|
+
|
137
141
|
private
|
138
142
|
|
139
143
|
def taint!
|
@@ -5,10 +5,10 @@ module Tenacity
|
|
5
5
|
def id_class_for(association)
|
6
6
|
if association.polymorphic?
|
7
7
|
String
|
8
|
-
elsif association.type == :
|
9
|
-
association.source._t_id_type
|
10
|
-
else
|
8
|
+
elsif association.type == :t_belongs_to
|
11
9
|
association.associate_class._t_id_type
|
10
|
+
else
|
11
|
+
raise "Unable to determine id class for type: #{association.type}, source: #{association.source}, target: #{association.associate_class}"
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -55,7 +55,7 @@ module Tenacity
|
|
55
55
|
attr_accessor :_t_belongs_to_associations
|
56
56
|
|
57
57
|
def _t_id_type
|
58
|
-
|
58
|
+
@_t_id_type_clazz ||= Kernel.const_get(db_schema.values.find{ |x| x[:primary_key] == true }[:type].to_s.capitalize)
|
59
59
|
end
|
60
60
|
|
61
61
|
def _t_find(id)
|
@@ -142,6 +142,10 @@ module Tenacity
|
|
142
142
|
reload
|
143
143
|
self
|
144
144
|
end
|
145
|
+
|
146
|
+
def _t_save_if_dirty
|
147
|
+
!changed_columns.empty? ? save : false
|
148
|
+
end
|
145
149
|
end
|
146
150
|
|
147
151
|
end
|
data/lib/tenacity/version.rb
CHANGED
@@ -9,11 +9,11 @@ class BelongsToTest < Test::Unit::TestCase
|
|
9
9
|
|
10
10
|
@source_class = class_for_extension(source)
|
11
11
|
@source = @source_class.create({})
|
12
|
-
@target_class = class_for_extension(target, :
|
12
|
+
@target_class = class_for_extension(target, :t_belongs_to)
|
13
13
|
@target = @target_class.create({})
|
14
14
|
|
15
|
-
@foreign_key = foreign_key_for(source, :
|
16
|
-
@foreign_key_id = foreign_key_id_for(source, :
|
15
|
+
@foreign_key = foreign_key_for(source, :t_belongs_to)
|
16
|
+
@foreign_key_id = foreign_key_id_for(source, :t_belongs_to)
|
17
17
|
end
|
18
18
|
|
19
19
|
should "be able to fetch the id of the associated object" do
|
@@ -9,13 +9,13 @@ class HasManyTest < Test::Unit::TestCase
|
|
9
9
|
|
10
10
|
@source_class = class_for_extension(source)
|
11
11
|
@source = @source_class.create({})
|
12
|
-
@target_class = class_for_extension(target, :
|
12
|
+
@target_class = class_for_extension(target, :t_has_many)
|
13
13
|
@target_1 = @target_class.create({})
|
14
14
|
@target_2 = @target_class.create({})
|
15
15
|
@target_3 = @target_class.create({})
|
16
16
|
|
17
|
-
@foreign_key = foreign_key_for(target, :
|
18
|
-
@foreign_key_id = foreign_key_id_for(target, :
|
17
|
+
@foreign_key = foreign_key_for(target, :t_has_many)
|
18
|
+
@foreign_key_id = foreign_key_id_for(target, :t_has_many)
|
19
19
|
end
|
20
20
|
|
21
21
|
should "be able to set the associated objects by their ids" do
|
@@ -109,9 +109,9 @@ class HasManyTest < Test::Unit::TestCase
|
|
109
109
|
assert_not_nil @target_class._t_find(serialize_id(@target_1))
|
110
110
|
assert_not_nil @target_class._t_find(serialize_id(@target_2))
|
111
111
|
assert_not_nil @target_class._t_find(serialize_id(@target_3))
|
112
|
-
assert_nil @target_class._t_find(serialize_id(@target_1)).send(foreign_key_id_for(target, :
|
113
|
-
assert_nil @target_class._t_find(serialize_id(@target_2)).send(foreign_key_id_for(target, :
|
114
|
-
assert_nil @target_class._t_find(serialize_id(@target_3)).send(foreign_key_id_for(target, :
|
112
|
+
assert_nil @target_class._t_find(serialize_id(@target_1)).send(foreign_key_id_for(target, :t_belongs_to))
|
113
|
+
assert_nil @target_class._t_find(serialize_id(@target_2)).send(foreign_key_id_for(target, :t_belongs_to))
|
114
|
+
assert_nil @target_class._t_find(serialize_id(@target_3)).send(foreign_key_id_for(target, :t_belongs_to))
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|