immortal 0.1.4 → 0.1.7
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.
- data/Gemfile.lock +12 -12
- data/README.md +3 -0
- data/immortal.gemspec +2 -2
- data/lib/immortal.rb +48 -11
- data/lib/immortal/has_many_through_mortal_association.rb +23 -0
- data/spec/immortal_spec.rb +43 -4
- data/spec/spec_helper.rb +7 -1
- metadata +8 -7
data/Gemfile.lock
CHANGED
@@ -1,23 +1,23 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
immortal (0.1.
|
5
|
-
activerecord (~> 3.0.
|
4
|
+
immortal (0.1.7)
|
5
|
+
activerecord (~> 3.0.5)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: http://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activemodel (3.0.
|
11
|
-
activesupport (= 3.0.
|
10
|
+
activemodel (3.0.9)
|
11
|
+
activesupport (= 3.0.9)
|
12
12
|
builder (~> 2.1.2)
|
13
|
-
i18n (~> 0.
|
14
|
-
activerecord (3.0.
|
15
|
-
activemodel (= 3.0.
|
16
|
-
activesupport (= 3.0.
|
17
|
-
arel (~> 2.0.
|
13
|
+
i18n (~> 0.5.0)
|
14
|
+
activerecord (3.0.9)
|
15
|
+
activemodel (= 3.0.9)
|
16
|
+
activesupport (= 3.0.9)
|
17
|
+
arel (~> 2.0.10)
|
18
18
|
tzinfo (~> 0.3.23)
|
19
|
-
activesupport (3.0.
|
20
|
-
arel (2.0.
|
19
|
+
activesupport (3.0.9)
|
20
|
+
arel (2.0.10)
|
21
21
|
builder (2.1.2)
|
22
22
|
columnize (0.3.2)
|
23
23
|
diff-lcs (1.1.2)
|
@@ -37,7 +37,7 @@ GEM
|
|
37
37
|
ruby-debug-base (0.10.4)
|
38
38
|
linecache (>= 0.3)
|
39
39
|
sqlite3-ruby (1.3.2)
|
40
|
-
tzinfo (0.3.
|
40
|
+
tzinfo (0.3.29)
|
41
41
|
|
42
42
|
PLATFORMS
|
43
43
|
ruby
|
data/README.md
CHANGED
@@ -44,6 +44,9 @@ If you want to improve immortal
|
|
44
44
|
|
45
45
|
## CHANGELOG
|
46
46
|
|
47
|
+
- 0.1.6 Fixing immortal issue 2: with_deleted breaks associations
|
48
|
+
- 0.1.5 Add "without deleted" scope to join model by overriding HasManyThroughAssociation#construct_conditions
|
49
|
+
rather than simply adding to has_many conditions.
|
47
50
|
- 0.1.4 fix bug where ALL records of any dependent associations were
|
48
51
|
immortally deleted if assocation has :dependant => :delete_all option
|
49
52
|
set
|
data/immortal.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "immortal"
|
6
|
-
s.version = '0.1.
|
6
|
+
s.version = '0.1.7'
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
8
|
s.authors = ["Jordi Romero", "Saimon Moore"]
|
9
9
|
s.email = ["jordi@jrom.net", "saimon@saimonmoore.net"]
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
17
|
s.require_paths = ["lib"]
|
18
18
|
|
19
|
-
s.add_dependency 'activerecord', '~> 3.0.
|
19
|
+
s.add_dependency 'activerecord', '~> 3.0.5'
|
20
20
|
s.add_development_dependency 'rspec', '~> 2.3.0'
|
21
21
|
s.add_development_dependency 'sqlite3-ruby', '~> 1.3.2'
|
22
22
|
s.add_development_dependency 'ruby-debug', '~> 0.10.4'
|
data/lib/immortal.rb
CHANGED
@@ -1,9 +1,28 @@
|
|
1
|
+
require 'immortal/has_many_through_mortal_association'
|
2
|
+
|
1
3
|
module Immortal
|
4
|
+
|
2
5
|
def self.included(base)
|
3
6
|
base.send :extend, ClassMethods
|
4
7
|
base.send :include, InstanceMethods
|
5
8
|
base.class_eval do
|
6
9
|
class << self
|
10
|
+
# In has_many :through => join_model we have to explicitly add
|
11
|
+
# the 'not deleted' scope, otherwise it will take all the rows
|
12
|
+
# from the join model
|
13
|
+
def has_many_mortal(association_id, options = {}, &extension)
|
14
|
+
has_many_immortal(association_id, options, &extension).tap do
|
15
|
+
if options[:through] and reflections[options[:through]] and reflections[options[:through]].class_name.classify.constantize.arel_table[:deleted]
|
16
|
+
reflection = reflect_on_association(association_id)
|
17
|
+
collection_reader_method(reflection, Immortal::HasManyThroughMortalAssociation)
|
18
|
+
collection_accessor_methods(reflection, Immortal::HasManyThroughMortalAssociation, false)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :has_many_immortal, :has_many
|
24
|
+
alias_method :has_many, :has_many_mortal
|
25
|
+
|
7
26
|
alias :mortal_delete_all :delete_all
|
8
27
|
alias :delete_all :immortal_delete_all
|
9
28
|
end
|
@@ -12,12 +31,20 @@ module Immortal
|
|
12
31
|
|
13
32
|
module ClassMethods
|
14
33
|
|
34
|
+
def immortal?
|
35
|
+
self.included_modules.include?(::Immortal::InstanceMethods)
|
36
|
+
end
|
37
|
+
|
15
38
|
def with_deleted
|
16
|
-
|
39
|
+
except(:where).where(filter_undeleted_where_clauses.join(' AND '))
|
17
40
|
end
|
18
41
|
|
19
42
|
def only_deleted
|
20
|
-
|
43
|
+
deleted_clause = arel_table[:deleted].eq(true)
|
44
|
+
where_sql_clauses = filter_undeleted_where_clauses
|
45
|
+
where_sql_clauses.concat unscoped.where(deleted_clause).where_clauses
|
46
|
+
|
47
|
+
except(:where).where(where_sql_clauses.join(" AND "))
|
21
48
|
end
|
22
49
|
|
23
50
|
def count_with_deleted(*args)
|
@@ -44,15 +71,25 @@ module Immortal
|
|
44
71
|
unscoped.mortal_delete_all(*args)
|
45
72
|
end
|
46
73
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
74
|
+
def undeleted_clause_sql
|
75
|
+
unscoped.where(arel_table[:deleted].eq(nil).or(arel_table[:deleted].eq(false))).where_clauses.first
|
76
|
+
end
|
77
|
+
|
78
|
+
def deleted_clause_sql
|
79
|
+
unscoped.where(arel_table[:deleted].eq(true)).where_clauses.first
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def filter_undeleted_where_clauses
|
85
|
+
where_clauses = scoped.arel.send(:where_clauses)
|
86
|
+
|
87
|
+
#Yes it's an ugly hack but I couldn't find a cleaner way of doing this
|
88
|
+
filtered_clauses = where_clauses.dup.map do |clause|
|
89
|
+
clause.gsub(/(\s+AND\s+)?\("?`?(\w+)`?"?."?`?deleted`?"?\sIS\sNULL[^\)]+\)/, '')
|
90
|
+
end.map {|cl| cl.gsub("()", '')}.select {|cl| !cl.empty?}
|
91
|
+
|
92
|
+
filtered_clauses
|
56
93
|
end
|
57
94
|
|
58
95
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module Immortal
|
4
|
+
|
5
|
+
class HasManyThroughMortalAssociation < ActiveRecord::Associations::HasManyThroughAssociation
|
6
|
+
protected
|
7
|
+
|
8
|
+
def construct_conditions
|
9
|
+
klass = @reflection.through_reflection.klass
|
10
|
+
return super unless klass.respond_to?(:immortal?) && klass.immortal?
|
11
|
+
table_name = @reflection.through_reflection.quoted_table_name
|
12
|
+
conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value|
|
13
|
+
"#{table_name}.#{attr} = #{value}"
|
14
|
+
end
|
15
|
+
|
16
|
+
deleted_conditions = ["#{table_name}.deleted IS NULL OR #{table_name}.deleted = ?", false]
|
17
|
+
conditions << klass.send(:sanitize_sql, deleted_conditions)
|
18
|
+
conditions << sql_conditions if sql_conditions
|
19
|
+
"(" + conditions.join(') AND (') + ")"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/spec/immortal_spec.rb
CHANGED
@@ -181,13 +181,13 @@ describe Immortal do
|
|
181
181
|
@n = ImmortalNode.create! :title => 'testing association'
|
182
182
|
@join = ImmortalJoin.create! :immortal_model => @m, :immortal_node => @n
|
183
183
|
|
184
|
-
@m.
|
185
|
-
@n.
|
184
|
+
@m.nodes.count.should == 1
|
185
|
+
@n.models.count.should == 1
|
186
186
|
|
187
187
|
@join.destroy
|
188
188
|
|
189
|
-
@m.
|
190
|
-
@n.
|
189
|
+
@m.nodes.count.should == 0
|
190
|
+
@n.models.count.should == 0
|
191
191
|
end
|
192
192
|
|
193
193
|
it "should only immortally delete scoped associations, NOT ALL RECORDS" do
|
@@ -205,4 +205,43 @@ describe Immortal do
|
|
205
205
|
[n1,n2,j1,j2].all? {|r| r.reload.deleted?}.should be_true
|
206
206
|
[n3,j3].all? {|r| !r.reload.deleted?}.should be_true
|
207
207
|
end
|
208
|
+
|
209
|
+
it "should properly generate joins" do
|
210
|
+
join_sql = 'INNER JOIN "immortal_joins" ON "immortal_nodes"."id" = "immortal_joins"."immortal_node_id" INNER JOIN "immortal_models" ON "immortal_models"."id" = "immortal_joins"."immortal_model_id"'
|
211
|
+
ImmortalNode.joins(:immortal_models).to_sql.should include(join_sql)
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should not unscope associations when using with_deleted scope" do
|
215
|
+
m1 = ImmortalModel.create! :title => 'previously created model'
|
216
|
+
n1 = ImmortalNode.create! :title => 'previously created association'
|
217
|
+
j1 = ImmortalJoin.create! :immortal_model => m1, :immortal_node => n1
|
218
|
+
|
219
|
+
@n = ImmortalNode.create! :title => 'testing association'
|
220
|
+
@join = ImmortalJoin.create! :immortal_model => @m, :immortal_node => @n
|
221
|
+
|
222
|
+
@join.destroy
|
223
|
+
|
224
|
+
@m.nodes.count.should == 0
|
225
|
+
@n.joins.count.should == 0
|
226
|
+
|
227
|
+
@m.nodes.with_deleted.count.should == 1
|
228
|
+
@n.joins.with_deleted.count.should == 1
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should not unscope associations when using only_deleted scope" do
|
232
|
+
m1 = ImmortalModel.create! :title => 'previously created model'
|
233
|
+
n1 = ImmortalNode.create! :title => 'previously created association'
|
234
|
+
j1 = ImmortalJoin.create! :immortal_model => m1, :immortal_node => n1
|
235
|
+
|
236
|
+
@n = ImmortalNode.create! :title => 'testing association'
|
237
|
+
@join = ImmortalJoin.create! :immortal_model => @m, :immortal_node => @n
|
238
|
+
|
239
|
+
@join.destroy
|
240
|
+
|
241
|
+
@m.nodes.count.should == 0
|
242
|
+
@n.joins.count.should == 0
|
243
|
+
|
244
|
+
@m.nodes.only_deleted.count.should == 1
|
245
|
+
@n.joins.only_deleted.count.should == 1
|
246
|
+
end
|
208
247
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -50,7 +50,7 @@ class ImmortalJoin < ActiveRecord::Base
|
|
50
50
|
include Immortal
|
51
51
|
|
52
52
|
belongs_to :immortal_model
|
53
|
-
belongs_to :immortal_node
|
53
|
+
belongs_to :immortal_node, :dependent => :destroy
|
54
54
|
end
|
55
55
|
|
56
56
|
class ImmortalNode < ActiveRecord::Base
|
@@ -58,6 +58,9 @@ class ImmortalNode < ActiveRecord::Base
|
|
58
58
|
|
59
59
|
has_many :immortal_joins
|
60
60
|
has_many :immortal_models, :through => :immortal_joins
|
61
|
+
|
62
|
+
has_many :joins, :class_name => 'ImmortalJoin'
|
63
|
+
has_many :models, :through => :joins, :source => :immortal_model
|
61
64
|
end
|
62
65
|
|
63
66
|
class ImmortalModel < ActiveRecord::Base
|
@@ -66,6 +69,9 @@ class ImmortalModel < ActiveRecord::Base
|
|
66
69
|
has_many :immortal_nodes, :through => :immortal_joins, :dependent => :destroy
|
67
70
|
has_many :immortal_joins, :dependent => :delete_all
|
68
71
|
|
72
|
+
has_many :joins, :class_name => 'ImmortalJoin', :dependent => :delete_all
|
73
|
+
has_many :nodes, :through => :joins, :source => :immortal_node, :dependent => :destroy
|
74
|
+
|
69
75
|
attr_accessor :before_d, :after_d, :before_u, :after_u
|
70
76
|
|
71
77
|
before_destroy :set_before
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: immortal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 7
|
10
|
+
version: 0.1.7
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jordi Romero
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2011-
|
19
|
+
date: 2011-08-30 00:00:00 +02:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -27,12 +27,12 @@ dependencies:
|
|
27
27
|
requirements:
|
28
28
|
- - ~>
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
hash:
|
30
|
+
hash: 13
|
31
31
|
segments:
|
32
32
|
- 3
|
33
33
|
- 0
|
34
|
-
-
|
35
|
-
version: 3.0.
|
34
|
+
- 5
|
35
|
+
version: 3.0.5
|
36
36
|
type: :runtime
|
37
37
|
version_requirements: *id001
|
38
38
|
- !ruby/object:Gem::Dependency
|
@@ -101,6 +101,7 @@ files:
|
|
101
101
|
- Rakefile
|
102
102
|
- immortal.gemspec
|
103
103
|
- lib/immortal.rb
|
104
|
+
- lib/immortal/has_many_through_mortal_association.rb
|
104
105
|
- spec/immortal_spec.rb
|
105
106
|
- spec/spec_helper.rb
|
106
107
|
has_rdoc: true
|