Skip to content

Commit aa1e4e4

Browse files
author
Jeremy Anderson
committed
patch to load_changeset to also load has_many -> through changes
1 parent d6668e2 commit aa1e4e4

3 files changed

Lines changed: 93 additions & 0 deletions

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,16 @@ t.amount # 100
120120
t.location.latitude # 12.345, instead of 54.321
121121
```
122122

123+
When calling `changeset` on a `version` you can include changes to associations by specifying the following options
124+
125+
- To restore Has-Many-Through associations, use option `has_many_through: true`
126+
127+
For example:
128+
129+
```ruby
130+
item.versions.last.changeset(has_many_through: true)
131+
```
132+
123133
# Limitations
124134

125135
1. Only reifies the first level of associations. If you want to include nested associations simply add `:through` relationships to your model.

lib/paper_trail-association_tracking.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class << self
5050

5151
module VersionConcern
5252
include ::PaperTrailAssociationTracking::VersionConcern
53+
prepend ::PaperTrailAssociationTracking::VersionConcern::ClassMethods
5354
end
5455
end
5556

lib/paper_trail_association_tracking/version_concern.rb

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,87 @@ module VersionConcern
3333
#def reify(options = {})
3434
# super
3535
#end
36+
37+
module ClassMethods
38+
def changeset(options = {})
39+
super()
40+
return @changeset unless ::PaperTrail.config.track_associations?
41+
42+
@changeset = load_changeset_has_many_through(@changeset) if options[:has_many_through]
43+
@changeset
44+
end
45+
46+
private
47+
48+
def load_changeset_has_many_through(changes)
49+
has_many_through_assocs
50+
.reduce(changes) do |acc, assoc|
51+
assoc_changes = has_many_through_changes(assoc)
52+
acc.merge!(assoc_changes) if assoc_changes.any?
53+
54+
acc
55+
end
56+
57+
changes
58+
end
59+
60+
def has_many_assocs
61+
item_type.to_s.classify.constantize
62+
.reflect_on_all_associations(:has_many)
63+
end
64+
65+
def has_many_through_assocs
66+
has_many_assocs
67+
.select { |assoc| assoc.through_reflection? }
68+
end
69+
70+
def paper_trail_enabled?(assoc)
71+
::PaperTrail.request.enabled_for_model?(assoc.klass) ||
72+
(assoc.through_reflection? && ::PaperTrail.request.enabled_for_model?(assoc.through_reflection.klass))
73+
end
74+
75+
def has_many_through_changes(assoc)
76+
return {} unless object_changes.present?
77+
78+
versions = find_associated_versions(assoc.through_reflection)
79+
80+
return { "#{assoc.name}": updated_changes(assoc, versions) } if updated_changes(assoc, versions).any?
81+
82+
{ "#{assoc.name}": [removed_changes(assoc, versions), added_changes(assoc, versions)] }
83+
end
84+
85+
def find_associated_versions(assoc)
86+
::PaperTrail::Version.where(item_type: assoc.class_name, transaction_id: transaction_id)
87+
.joins(:version_associations)
88+
.where(version_associations: { foreign_key_name: assoc.foreign_key, foreign_key_id: item_id })
89+
.order('versions.created_at desc')
90+
end
91+
92+
def updated_changes(assoc, versions)
93+
versions.select { |version| version.event == "update" }
94+
.map(&:changeset)
95+
.pluck(assoc.foreign_key)
96+
.flat_map do |change|
97+
[
98+
assoc.klass.find_by(id: change.first),
99+
assoc.klass.find_by(id: change.second)
100+
]
101+
end
102+
end
103+
104+
def added_changes(assoc, versions)
105+
versions.select { |version| version.event == "create" }
106+
.map(&:changeset)
107+
.pluck(assoc.foreign_key)
108+
.map { |change| assoc.klass.find_by(id: change.second) }
109+
end
110+
111+
def removed_changes(assoc, versions)
112+
versions.select { |version| version.event == "destroy" }
113+
.map(&:changeset)
114+
.pluck(assoc.foreign_key)
115+
.map { |change| assoc.klass.find_by(id: change.first) }
116+
end
117+
end
36118
end
37119
end

0 commit comments

Comments
 (0)