immortal 1.0.1 → 1.0.2

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/README.md CHANGED
@@ -41,6 +41,7 @@ If you want to improve immortal
41
41
 
42
42
  ## CHANGELOG
43
43
 
44
+ - 1.0.2 Added with/only_deleted singular association readers (see specs)
44
45
  - 1.0.1 Made compatible with Rails 3.1.X
45
46
  - 1.0.0 Changed the API, made it compatible with Rails 3.1, removed
46
47
  functionality
data/immortal.gemspec CHANGED
@@ -3,8 +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 = '1.0.1'
7
- s.platform = Gem::Platform::RUBY
6
+ s.version = '1.0.2'
8
7
  s.authors = ["Jordi Romero", "Saimon Moore"]
9
8
  s.email = ["jordi@jrom.net", "saimon@saimonmoore.net"]
10
9
  s.homepage = "http://github.com/teambox/immortal"
data/lib/immortal.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'immortal/belongs_to_builder'
2
+
1
3
  module Immortal
2
4
 
3
5
  def self.included(base)
@@ -5,6 +7,12 @@ module Immortal
5
7
  base.send :include, InstanceMethods
6
8
  base.class_eval do
7
9
  class << self
10
+
11
+ # Add with/how_deleted singular association readers
12
+ def belongs_to_mortal(name, options = {})
13
+ ::Immortal::BelongsToBuilder.build(self, name, options)
14
+ end
15
+
8
16
  # In has_many :through => join_model we have to explicitly add
9
17
  # the 'not deleted' scope, otherwise it will take all the rows
10
18
  # from the join model
@@ -17,6 +25,10 @@ module Immortal
17
25
  alias_method :has_many_immortal, :has_many
18
26
  alias_method :has_many, :has_many_mortal
19
27
 
28
+ alias_method :belongs_to_immortal, :belongs_to
29
+ alias_method :belongs_to, :belongs_to_mortal
30
+
31
+
20
32
  alias :mortal_delete_all :delete_all
21
33
  alias :delete_all :immortal_delete_all
22
34
  end
@@ -0,0 +1,46 @@
1
+ require 'active_record'
2
+ require 'immortal/singular_association'
3
+
4
+ module Immortal
5
+ class BelongsToBuilder < ::ActiveRecord::Associations::Builder::BelongsTo
6
+
7
+ def define_accessors
8
+ super
9
+ define_deletables
10
+ end
11
+
12
+ private
13
+
14
+ def define_deletables
15
+ define_with_deleted_reader
16
+ define_only_deleted_reader
17
+ end
18
+
19
+ def define_with_deleted_reader
20
+ name = self.name
21
+
22
+ model.redefine_method("#{name}_with_deleted") do |*params|
23
+ assoc = association(name)
24
+ assoc.send(:extend, SingularAssociation)
25
+ assoc.with_deleted_reader(*params)
26
+ end
27
+ end
28
+
29
+ def define_only_deleted_reader
30
+ name = self.name
31
+
32
+ model.redefine_method("#{name}_only_deleted") do |*params|
33
+
34
+ assoc = association(name)
35
+ assoc.send(:extend, SingularAssociation)
36
+ assoc.only_deleted_reader(*params)
37
+ end
38
+ end
39
+
40
+ module InstanceMethods
41
+
42
+ end #InstanceMethods
43
+
44
+ end
45
+ end
46
+
@@ -0,0 +1,57 @@
1
+ module Immortal
2
+ module SingularAssociation
3
+ attr_reader :deleted_target
4
+
5
+ def with_deleted_reader(force_reload = false)
6
+ deleted_reader('with', force_reload)
7
+ end
8
+
9
+ def only_deleted_reader(force_reload = false)
10
+ deleted_reader('only', force_reload)
11
+ end
12
+
13
+ private
14
+
15
+ def deleted_reader(how, force_reload = false)
16
+ klass.uncached do
17
+ send(:"reload_#{how}_deleted")
18
+ end
19
+
20
+ deleted_target
21
+ end
22
+
23
+ def reload_with_deleted
24
+ reset
25
+ reset_scope
26
+ load_deleted_target('with')
27
+ self unless deleted_target.nil?
28
+ end
29
+
30
+ def reload_only_deleted
31
+ reset
32
+ reset_scope
33
+ load_deleted_target('only')
34
+ self unless deleted_target.nil?
35
+ end
36
+
37
+ def find_with_deleted_target
38
+ klass.unscoped do
39
+ scoped.first.tap { |record| set_inverse_instance(record) }
40
+ end
41
+ end
42
+
43
+ def find_only_deleted_target
44
+ klass.unscoped do
45
+ scoped.where(:deleted => true).first.tap { |record| set_inverse_instance(record) }
46
+ end
47
+ end
48
+
49
+ def load_deleted_target(how)
50
+ @deleted_target ||= send(:"find_#{how}_deleted_target")
51
+ deleted_target
52
+ rescue ActiveRecord::RecordNotFound
53
+ reset
54
+ end
55
+
56
+ end
57
+ end
@@ -198,4 +198,53 @@ describe Immortal do
198
198
  generated_sql.should include(join_sql2)
199
199
  end
200
200
 
201
+ it "should reload immortal polymorphic associations using default reader" do
202
+ node = ImmortalNode.create! :title => 'testing association 1'
203
+ target_1 = ImmortalSomeTarget.create! :title => 'target 1'
204
+ target_2 = ImmortalSomeOtherTarget.create! :title => 'target 2'
205
+
206
+ node.target.should be_nil
207
+ node.target = target_1
208
+ node.target.should == target_1
209
+
210
+ node.target_id = target_2.id
211
+ node.target_type = target_2.class.name
212
+
213
+ target_2.destroy
214
+ node.target.should be_nil
215
+ end
216
+
217
+ it "should reload immortal polymorphic associations using deleted reader" do
218
+ #setup
219
+ node = ImmortalNode.create! :title => 'testing association 1'
220
+ target_1 = ImmortalSomeTarget.create! :title => 'target 1'
221
+ target_2 = ImmortalSomeOtherTarget.create! :title => 'target 2'
222
+
223
+ #confirm initial state
224
+ node.target.should be_nil
225
+
226
+ #load target & confirm
227
+ node.target = target_1
228
+ node.target.should == target_1
229
+
230
+ #switch target indirectly
231
+ node.target_id = target_2.id
232
+ node.target_type = target_2.class.name
233
+
234
+ #don't assign directly and destroy new target
235
+ target_2.destroy
236
+
237
+ #Respect what's expected
238
+ node.target.should be_nil
239
+
240
+ #Ask for deleted target (or not deleted). Will NOT cache
241
+ node.target_with_deleted.should == target_2
242
+
243
+ #Ask only for deleted target. Will NOT cache
244
+ node.target_only_deleted.should == target_2
245
+
246
+ #Confirm we haven't invaded the target namespace
247
+ node.target.should be_nil
248
+ end
249
+
201
250
  end
data/spec/spec_helper.rb CHANGED
@@ -36,11 +36,25 @@ begin
36
36
  t.timestamps
37
37
  end
38
38
  create_table :immortal_nodes do |t|
39
+ t.integer :target_id
40
+ t.string :target_type
39
41
  t.string :title
40
42
  t.integer :value
41
43
  t.boolean :deleted, :default => false
42
44
  t.timestamps
43
45
  end
46
+
47
+ create_table :immortal_some_targets do |t|
48
+ t.string :title
49
+ t.boolean :deleted, :default => false
50
+ t.timestamps
51
+ end
52
+
53
+ create_table :immortal_some_other_targets do |t|
54
+ t.string :title
55
+ t.boolean :deleted, :default => false
56
+ t.timestamps
57
+ end
44
58
  end
45
59
  ensure
46
60
  $stdout = old_stdout
@@ -61,8 +75,23 @@ class ImmortalNode < ActiveRecord::Base
61
75
 
62
76
  has_many :joins, :class_name => 'ImmortalJoin'
63
77
  has_many :models, :through => :joins, :source => :immortal_model
78
+
79
+ belongs_to :target, :polymorphic => true
80
+ end
81
+
82
+ class ImmortalSomeTarget < ActiveRecord::Base
83
+ include Immortal
84
+
85
+ has_many :immortal_nodes, :as => :target
64
86
  end
65
87
 
88
+ class ImmortalSomeOtherTarget < ActiveRecord::Base
89
+ include Immortal
90
+
91
+ has_many :immortal_nodes, :as => :target
92
+ end
93
+
94
+
66
95
  class ImmortalModel < ActiveRecord::Base
67
96
  include Immortal
68
97
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: immortal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -14,7 +14,7 @@ date: 2011-10-17 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
17
- requirement: &70094483448900 !ruby/object:Gem::Requirement
17
+ requirement: &70147088431320 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 3.1.1
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70094483448900
25
+ version_requirements: *70147088431320
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rspec
28
- requirement: &70094483448420 !ruby/object:Gem::Requirement
28
+ requirement: &70147088430840 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ~>
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 2.6.0
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *70094483448420
36
+ version_requirements: *70147088430840
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: sqlite3
39
- requirement: &70094483448040 !ruby/object:Gem::Requirement
39
+ requirement: &70147088430460 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,7 +44,7 @@ dependencies:
44
44
  version: '0'
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *70094483448040
47
+ version_requirements: *70147088430460
48
48
  description: Typical paranoid gem built for Rails 3 and with the minimum code needed
49
49
  to satisfy acts_as_paranoid's API
50
50
  email:
@@ -61,6 +61,8 @@ files:
61
61
  - Rakefile
62
62
  - immortal.gemspec
63
63
  - lib/immortal.rb
64
+ - lib/immortal/belongs_to_builder.rb
65
+ - lib/immortal/singular_association.rb
64
66
  - spec/immortal_spec.rb
65
67
  - spec/spec_helper.rb
66
68
  homepage: http://github.com/teambox/immortal