Skip to content

Commit

Permalink
Working on a better fix than mongoid#191
Browse files Browse the repository at this point in the history
  • Loading branch information
Startouf committed Oct 18, 2018
1 parent 8ed7ba1 commit a36ec04
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 13 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ gem 'mongoid-compatibility'

group :development, :test do
gem 'bundler'
gem 'byebug'
gem 'pry'
gem 'rake', '< 11.0'
end
Expand Down
78 changes: 65 additions & 13 deletions lib/mongoid/history/attributes/update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,85 @@ module Mongoid
module History
module Attributes
class Update < ::Mongoid::History::Attributes::Base
# @example
#
# {
# 'foo' => ['foo_before_changes', 'foo_after_changes']
# 'nested_bar' => {
# 'baz' => ['nested_bar_baz_before_changes', 'nested_bar_baz_after_changes']
# }
# }
#
# @return [Hash<String, ?>] Hash of changes
# ? can be either a pair or a hash for embedded documents
def attributes
@attributes = {}
require 'byebug'
byebug
changes_from_parent.deep_merge(changes_from_children)
end

private

def changes_from_parent
parent_changes = {}
changes.each do |k, v|
if trackable_class.tracked_embeds_one?(k)
insert_embeds_one_changes(k, v)
elsif trackable_class.tracked_embeds_many?(k)
insert_embeds_many_changes(k, v)
elsif trackable_class.tracked?(k, :update)
@attributes[k] = format_field(k, v) unless v.all?(&:blank?)
change_value = begin
if trackable_class.tracked_embeds_one?(k)
embeds_one_changes_from_parent(k, v)
elsif trackable_class.tracked_embeds_many?(k)
embeds_many_changes_from_parent(k, v)
elsif trackable_class.tracked?(k, :update)
{ k => format_field(k, v) } unless v.all?(&:blank?)
end
end
parent_changes.merge!(change_value) if change_value.present?
end
@attributes
parent_changes
end

private
def changes_from_children
embeds_one_changes_from_embedded_documents
end

# @return [Hash<String, Array<?,?>] changes of embeds_ones from embedded documents
def embeds_one_changes_from_embedded_documents
embedded_doc_changes = {}
trackable_class.tracked_embeds_one.each do |rel|
rel_class = trackable_class.relation_class_of(rel)
paranoia_field = Mongoid::History.trackable_class_settings(rel_class)[:paranoia_field]
paranoia_field = rel_class.aliased_fields.key(paranoia_field) || paranoia_field
rel = aliased_fields.key(rel) || rel
obj = trackable.send(rel)
next if !obj || (obj.respond_to?(paranoia_field) && obj.public_send(paranoia_field).present?)
embedded_doc_field_changes = obj.changes.map do |k,v|
[{ k => v.first }, { k => v.last }]
end
embedded_doc_changes[rel] = embedded_doc_field_changes if embedded_doc_field_changes.any?
end
embedded_doc_changes
end

def insert_embeds_one_changes(relation, value)
# @param [String] relation <description>
# @param [String] value <description>
#
# @return [Hash<String, Array<(?,?)>>]
def embeds_one_changes_from_parent(relation, value)
relation = trackable_class.database_field_name(relation)
relation_class = trackable_class.relation_class_of(relation)
paranoia_field = Mongoid::History.trackable_class_settings(relation_class)[:paranoia_field]
original_value = value[0][paranoia_field].present? ? {} : format_embeds_one_relation(relation, value[0])
modified_value = value[1][paranoia_field].present? ? {} : format_embeds_one_relation(relation, value[1])
return if original_value == modified_value
@attributes[relation] = [original_value, modified_value]
[original_value, modified_value]
byebug
{ relation => [original_value, modified_value] }
end

def insert_embeds_many_changes(relation, value)
# @param [String] relation <description>
# @param [String] value <description>
#
# @return [Hash<Array<(?,?)>>]
def embeds_many_changes_from_parent(relation, value)
relation = trackable_class.database_field_name(relation)
relation_class = trackable_class.relation_class_of(relation)
paranoia_field = Mongoid::History.trackable_class_settings(relation_class)[:paranoia_field]
Expand All @@ -37,7 +89,7 @@ def insert_embeds_many_changes(relation, value)
modified_value = value[1].reject { |rel| rel[paranoia_field].present? }
.map { |v_attrs| format_embeds_many_relation(relation, v_attrs) }
return if original_value == modified_value
@attributes[relation] = [original_value, modified_value]
{ relation => [original_value, modified_value] }
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
require 'spec_helper'

describe Mongoid::History::Tracker do
describe 'Tracking of changes from embedded documents' do
before :each do
# Child model (will be embedded in Parent)
class Child
include Mongoid::Document
include Mongoid::History::Trackable

store_in collection: :child

field :name
embedded_in :parent, inverse_of: :child
end

# Parent model (embeds one Child)
class Parent
include Mongoid::Document
include Mongoid::History::Trackable

field :name, type: String
embeds_one :child

store_in collection: :parent

track_history(
on: %i[fields embedded_relations],
version_field: :version,
track_create: true,
track_update: true,
track_destroy: false,
modifier_field: nil
)
end
end

after :each do
Object.send(:remove_const, :Parent)
Object.send(:remove_const, :Child)
end

it 'tracks history for nested embedded documents in parent' do
p = Parent.new(name: 'bowser')
p.child = Child.new(name: 'todd')
p.save!
expect(p.history_tracks.length).to eq(1)
change = p.history_tracks.last
aggregate_failures do
expect(change.modified['name']).to eq('bowser')
expect(change.modified['child']['name']).to eq('todd')
end

p.update_attributes(name: 'brow')
expect(p.history_tracks.length).to eq(2)

p.child.name = 'mario'
p.save!
expect(p.history_tracks.length).to eq(3)
require 'byebug'
aggregate_failures do
expect(p.history_tracks.last.original['child']['name']).to eq('todd')
expect(p.history_tracks.last.modified['child']['name']).to eq('mario')
end
end
end
end

0 comments on commit a36ec04

Please sign in to comment.