From 774dd0d297db20d8e89da4d6969171621ab6789b Mon Sep 17 00:00:00 2001 From: Jagdeep Singh Date: Mon, 6 Jun 2016 17:19:03 +0530 Subject: [PATCH 01/12] Save embedded relations in parent audit history - Change rbx version --- .rubocop_todo.yml | 4 +- .travis.yml | 2 +- README.md | 59 ++++++++ lib/mongoid/history.rb | 1 + .../history/services/options_cleaner.rb | 71 ++++++++++ lib/mongoid/history/trackable.rb | 101 ++++++++------ lib/mongoid/history/tracker.rb | 2 +- spec/unit/trackable_spec.rb | 129 ++++++++++++++++++ 8 files changed, 326 insertions(+), 43 deletions(-) create mode 100644 lib/mongoid/history/services/options_cleaner.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 1221481e..ca1bd790 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -27,12 +27,12 @@ Metrics/LineLength: # Offense count: 7 # Configuration parameters: CountComments. Metrics/MethodLength: - Max: 32 + Max: 33 # Offense count: 2 # Configuration parameters: CountComments. Metrics/ModuleLength: - Max: 168 + Max: 174 # Offense count: 4 Metrics/PerceivedComplexity: diff --git a/.travis.yml b/.travis.yml index ba824579..bd6420de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ rvm: - 2.1.1 - 2.0.0 - 1.9.3 - - rbx-2.2.10 + - rbx-2 - jruby-19mode env: diff --git a/README.md b/README.md index 51d95b87..b32929b6 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,53 @@ Mongoid::History.disable do end ``` +**Include embedded objects attributes in parent audit** + +Modify above `Post` and `Comment` classes as below: + +```ruby +class Post + include Mongoid::Document + include Mongoid::Timestamps + include Mongoid::History::Trackable + + field :title + field :body + field :rating + embeds_many :comments + + track_history :on => [:title, :body], + :embeds_many => [:comments], # track comments as embedded attributes, default is [] + :modifier_field => :modifier, + :modifier_field_inverse_of => :nil, + :version_field => :version, + :track_create => true, # track create on Post + :track_update => true, + :track_destroy => false +end + +class Comment + include Mongoid::Document + include Mongoid::Timestamps + + field :title + field :body + embedded_in :post, :inverse_of => :comments +end + +user = User.create(:name => "Aaron") +post = Post.create(:title => "Test", :body => "Post", :modifier => user) +comment = post.comments.build(:title => "test", :body => "comment", :modifier => user) +post.save +post.history_tracks.count # should be 1 + +comment.respond_to?(:history_tracks) # should be false + +track = post.history_tracks.first +track.original # {} +track.modified # { "title" => "Test", "body" => "Post", "comments" => [{ "_id" => "575fa9e667d827e5ed00000d", "title" => "test", "body" => "comment" }], ... } +``` + **Retrieving the list of tracked fields** ```ruby @@ -172,6 +219,18 @@ Book.tracked_field?(:title) #=> true Book.tracked_field?(:author) #=> false ``` +**Retrieving the list of tracked embedded relations** + +```ruby +class Book + ... + track_history :embeds_many => [:pages] +end + +Book.tracked_embeds_many #=> ["pages"] +Book.tracked_embeds_many?(:pages) #=> true +``` + **Displaying history trackers as an audit trail** In your Controller: diff --git a/lib/mongoid/history.rb b/lib/mongoid/history.rb index d85135a2..e7eabd3f 100644 --- a/lib/mongoid/history.rb +++ b/lib/mongoid/history.rb @@ -1,5 +1,6 @@ require 'easy_diff' require 'mongoid/compatibility' +require 'mongoid/history/services/options_cleaner' require 'mongoid/history/version' require 'mongoid/history/tracker' require 'mongoid/history/trackable' diff --git a/lib/mongoid/history/services/options_cleaner.rb b/lib/mongoid/history/services/options_cleaner.rb new file mode 100644 index 00000000..48679f4e --- /dev/null +++ b/lib/mongoid/history/services/options_cleaner.rb @@ -0,0 +1,71 @@ +module Mongoid + module History + class OptionsCleaner + attr_reader :trackable, :options + + def initialize(trackable, options = {}) + @trackable = trackable + @options = default_options.merge(options) + end + + def scope + trackable.collection_name.to_s.singularize.to_sym + end + + # Options + # :on + # - :all OR [:all] OR :fields OR [:fields] - track all fields for now + # - :foo OR [:foo, ...] - track specified fields + # - [:, ...] - track only specified associations + # - [:all, :, ...] OR [:foo, ..., :, ...] - combination of above + def default_options + { on: :all, + except: [:created_at, :updated_at], + modifier_field: :modifier, + version_field: :version, + changes_method: :changes, + scope: scope, + track_create: false, + track_update: true, + track_destroy: false } + end + + def clean + prepare_skipped_fields + prepare_tracked_fields_and_relations + options + end + + def prepare_skipped_fields + # normalize :except fields to an array of database field strings + @options[:except] = Array(options[:except]) + @options[:except] = options[:except].map { |field| trackable.database_field_name(field) }.compact.uniq + end + + def prepare_tracked_fields_and_relations + @options[:on] = Array(options[:on]) + + # :all is just an alias to :fields for now, to support existing users of `mongoid-history` + # In future, :all will track all the fields and associations of trackable class + @options[:on] = options[:on].map { |opt| (opt == :all) ? :fields : opt } + @options[:on] = options[:on].map { |opt| trackable.database_field_name(opt) }.compact.uniq + + if @options[:on].include?('fields') + @options[:tracked_fields] = trackable.fields.keys + @options[:tracked_relations] = options[:on].reject { |opt| opt == 'fields' } + else + tracked_fields_and_relations = options[:on] - options[:except] + @options[:tracked_fields] = trackable.fields.keys.select { |field| tracked_fields_and_relations.include?(field) } + @options[:tracked_relations] = tracked_fields_and_relations - options[:tracked_fields] + end + + @options[:tracked_fields] = options[:tracked_fields] - options[:except] + @options[:tracked_relations] = options[:tracked_relations] - options[:except] + end + + def self.clean(trackable, options = {}) + new(trackable, options).clean + end + end + end +end diff --git a/lib/mongoid/history/trackable.rb b/lib/mongoid/history/trackable.rb index 0911c696..b16df3ca 100644 --- a/lib/mongoid/history/trackable.rb +++ b/lib/mongoid/history/trackable.rb @@ -5,30 +5,7 @@ module Trackable module ClassMethods def track_history(options = {}) - scope_name = collection_name.to_s.singularize.to_sym - default_options = { - on: :all, - except: [:created_at, :updated_at], - modifier_field: :modifier, - version_field: :version, - changes_method: :changes, - scope: scope_name, - track_create: false, - track_update: true, - track_destroy: false - } - - options = default_options.merge(options) - - # normalize :except fields to an array of database field strings - options[:except] = [options[:except]] unless options[:except].is_a? Array - options[:except] = options[:except].map { |field| database_field_name(field) }.compact.uniq - - # normalize :on fields to either :all or an array of database field strings - if options[:on] != :all - options[:on] = [options[:on]] unless options[:on].is_a? Array - options[:on] = options[:on].map { |field| database_field_name(field) }.compact.uniq - end + options = Mongoid::History::OptionsCleaner.clean(self, options) field options[:version_field].to_sym, type: Integer @@ -47,7 +24,7 @@ def track_history(options = {}) before_destroy :track_destroy if options[:track_destroy] Mongoid::History.trackable_class_options ||= {} - Mongoid::History.trackable_class_options[scope_name] = options + Mongoid::History.trackable_class_options[options[:scope]] = options end def track_history? @@ -212,21 +189,27 @@ def modified_attributes_for_action(action) end def modified_attributes_for_update - @modified_attributes_for_update ||= send(history_trackable_options[:changes_method]).select { |k, _| self.class.tracked_field?(k, :update) } + @modified_attributes_for_update ||= send(history_trackable_options[:changes_method]).select { |k, _| self.class.tracked?(k, :update) } end def modified_attributes_for_create - @modified_attributes_for_create ||= attributes.inject({}) do |h, (k, v)| - h[k] = [nil, v] - h - end.select { |k, _| self.class.tracked_field?(k, :create) } + return @modified_attributes_for_create if @modified_attributes_for_create + aliased_fields = self.class.aliased_fields + attrs = {} + self.class.tracked_fields_for_action(:create).each { |field| attrs[field] = [nil, send(field)] } + self.class.tracked_embedded_one.map { |rel| aliased_fields.key(rel) || rel }.each { |rel| attrs[rel] = [nil, send(rel).attributes] } + self.class.tracked_embedded_many.map { |rel| aliased_fields.key(rel) || rel }.each { |rel| attrs[rel] = [nil, send(rel).map(&:attributes)] } + @modified_attributes_for_create = attrs end def modified_attributes_for_destroy - @modified_attributes_for_destroy ||= attributes.inject({}) do |h, (k, v)| - h[k] = [v, nil] - h - end.select { |k, _| self.class.tracked_field?(k, :destroy) } + return @modified_attributes_for_destroy if @modified_attributes_for_destroy + aliased_fields = self.class.aliased_fields + attrs = {} + self.class.tracked_fields_for_action(:destroy).each { |field| attrs[field] = [send(field), nil] } + self.class.tracked_embedded_one.map { |rel| aliased_fields.key(rel) || rel }.each { |rel| attrs[rel] = [send(rel).attributes, nil] } + self.class.tracked_embedded_many.map { |rel| aliased_fields.key(rel) || rel }.each { |rel| attrs[rel] = [send(rel).map(&:attributes), nil] } + @modified_attributes_for_destroy = attrs end def history_tracker_attributes(action) @@ -293,6 +276,16 @@ def track_history_for_action(action) end module SingletonMethods + # Whether or not the field or embedded relation should be tracked. + # + # @param [ String | Symbol ] field_or_relation The name or alias of the field OR the name of embedded relation + # @param [ String | Symbol ] action The optional action name (:create, :update, or :destroy) + # + # @return [ Boolean ] whether or not the field or embedded relation is tracked for the given action + def tracked?(field_or_relation, action = :update) + tracked_field?(field_or_relation, action) || tracked_relation?(field_or_relation) + end + # Whether or not the field should be tracked. # # @param [ String | Symbol ] field The name or alias of the field @@ -324,14 +317,11 @@ def tracked_fields_for_action(action) end end - # Retrieves the memoized base list of tracked fields, excluding reserved fields. + # Retrieves the memoized base list of tracked fields, and associations excluding reserved fields. # # @return [ Array < String > ] the base list of tracked database field names def tracked_fields - @tracked_fields ||= begin - h = history_trackable_options - (h[:on] == :all ? fields.keys : h[:on]) - h[:except] - reserved_tracked_fields - end + @tracked_fields ||= history_trackable_options[:tracked_fields] - reserved_tracked_fields end # Retrieves the memoized list of reserved tracked fields, which are only included for certain actions. @@ -341,6 +331,39 @@ def reserved_tracked_fields @reserved_tracked_fields ||= ['_id', history_trackable_options[:version_field].to_s, "#{history_trackable_options[:modifier_field]}_id"] end + # Whether or not the embedded relation should be tracked. + # + # @param [ String | Symbol ] relation The name of the embedded relation + # + # @return [ Boolean ] whether or not the embedded relation is tracked + def tracked_relation?(relation) + tracked_embedded_one?(relation) || tracked_embedded_many?(relation) + end + + def tracked_embedded_one?(relation) + tracked_embedded_one.include?(database_field_name(relation)) + end + + def tracked_embedded_one + @tracked_embedded_one ||= begin + reflect_on_all_associations(:embeds_one) + .map(&:key) + .select { |rel| history_trackable_options[:tracked_relations].include? rel } + end + end + + def tracked_embedded_many?(relation) + tracked_embedded_many.include?(database_field_name(relation)) + end + + def tracked_embedded_many + @tracked_embedded_many ||= begin + reflect_on_all_associations(:embeds_many) + .map(&:key) + .select { |rel| history_trackable_options[:tracked_relations].include? rel } + end + end + def history_trackable_options @history_trackable_options ||= Mongoid::History.trackable_class_options[collection_name.to_s.singularize.to_sym] end diff --git a/lib/mongoid/history/tracker.rb b/lib/mongoid/history/tracker.rb index 2b48095e..09959f0d 100644 --- a/lib/mongoid/history/tracker.rb +++ b/lib/mongoid/history/tracker.rb @@ -91,7 +91,7 @@ def tracked_changes @tracked_changes ||= (modified.keys | original.keys).inject(HashWithIndifferentAccess.new) do |h, k| h[k] = { from: original[k], to: modified[k] }.delete_if { |_, vv| vv.nil? } h - end.delete_if { |k, v| v.blank? || !trackable_parent_class.tracked_field?(k) } + end.delete_if { |k, v| v.blank? || !trackable_parent_class.tracked?(k) } end # Outputs summary of edit actions performed: :add, :modify, :remove, or :array. diff --git a/spec/unit/trackable_spec.rb b/spec/unit/trackable_spec.rb index 8a7fbaec..14704fb2 100644 --- a/spec/unit/trackable_spec.rb +++ b/spec/unit/trackable_spec.rb @@ -4,6 +4,24 @@ class MyModel include Mongoid::Document include Mongoid::History::Trackable field :foo + + embeds_many :my_embedded_models + embeds_many :my_untracked_embedded_models +end + +class MyEmbeddedModel + include Mongoid::Document + field :foo + field :bar + + embedded_in :my_model +end + +class MyUntrackedEmbeddedModel + include Mongoid::Document + field :baz + + embedded_in :my_model end class MyDynamicModel @@ -35,6 +53,7 @@ class HistoryTracker before(:each) { Mongoid::History.trackable_class_options = @persisted_history_options } let(:expected_option) do { on: :all, + embeds_many: [], modifier_field: :modifier, version_field: :version, changes_method: :changes, @@ -283,4 +302,114 @@ def my_changes end end end + + describe 'MyInstanceMethods' do + before :all do + MyModel.instance_variable_set(:@history_trackable_options, nil) + MyModel.track_history(embeds_many: [:my_embedded_models]) + @persisted_history_options = Mongoid::History.trackable_class_options + end + before(:each) { Mongoid::History.trackable_class_options = @persisted_history_options } + let(:my_embedded_models) { [MyEmbeddedModel.new(foo: 'embedded-foo-value', bar: 'embedded-bar-value')] } + let(:my_untracked_embedded_models) { [MyUntrackedEmbeddedModel.new(baz: 'untracked-embedded-baz-value')] } + let(:my_model) { MyModel.new(foo: 'foo-value', my_embedded_models: my_embedded_models, my_untracked_embedded_models: my_untracked_embedded_models) } + + it 'should have embeds_many option' do + expect(Mongoid::History.trackable_class_options[:my_model][:embeds_many]).to eq([:my_embedded_models]) + end + + describe '#modified_attributes_for_create' do + subject { my_model.send(:modified_attributes_for_create) } + + it 'should include tracked embedded objects attributes' do + keys = subject.keys + expect(keys.size).to eq 2 + expect(keys).to include 'foo' + expect(keys).to include 'my_embedded_models' + + expect(subject['foo']).to eq([nil, 'foo-value']) + + my_embedded_model_attrs = subject['my_embedded_models'] + expect(my_embedded_model_attrs.size).to eq 2 + expect(my_embedded_model_attrs.first).to be_nil + expect(my_embedded_model_attrs.last.size).to eq 1 + + json = my_embedded_model_attrs.last.first + em_keys = json.keys + expect(em_keys.size).to eq 3 + expect(em_keys).to include '_id' + expect(em_keys).to include 'foo' + expect(em_keys).to include 'bar' + + expect(json['_id']).to eq my_embedded_models[0].id + expect(json['foo']).to eq 'embedded-foo-value' + expect(json['bar']).to eq 'embedded-bar-value' + end + end + + describe '#modified_attributes_for_destroy' do + subject { my_model.send(:modified_attributes_for_destroy) } + + it 'should include tracked embedded objects attributes' do + keys = subject.keys + expect(keys.size).to eq 3 + expect(keys).to include '_id' + expect(keys).to include 'foo' + expect(keys).to include 'my_embedded_models' + + expect(subject['_id']).to eq([my_model.id, nil]) + expect(subject['foo']).to eq(['foo-value', nil]) + + my_embedded_model_attrs = subject['my_embedded_models'] + expect(my_embedded_model_attrs.size).to eq 2 + expect(my_embedded_model_attrs.first.size).to eq 1 + + json = my_embedded_model_attrs.first.first + em_keys = json.keys + expect(em_keys.size).to eq 3 + expect(em_keys).to include '_id' + expect(em_keys).to include 'foo' + expect(em_keys).to include 'bar' + + expect(json['_id']).to eq my_embedded_models[0].id + expect(json['foo']).to eq 'embedded-foo-value' + expect(json['bar']).to eq 'embedded-bar-value' + + expect(my_embedded_model_attrs.last).to be_nil + end + end + end + + describe 'SingletonMethods' do + before(:all) { MyModel.track_history(embeds_many: [:my_embedded_models]) } + + describe '#tracked?' do + describe 'embeds_many relation' do + context 'when included in options' do + it { expect(MyModel.tracked?(:my_embedded_models)).to be true } + end + + context 'when not included in options' do + context 'and dynamic_field disabled' do + before { allow(MyModel).to receive(:dynamic_enabled?) { false } } + it { expect(MyModel.tracked?(:my_untracked_embedded_models)).to be false } + end + end + end + end + + describe '#tracked_embeds_many?' do + context 'when included in options' do + it { expect(MyModel.tracked_embeds_many?(:my_embedded_models)).to be true } + end + + context 'when not included in options' do + it { expect(MyModel.tracked_embeds_many?(:my_untracked_embedded_models)).to be false } + end + end + + describe '#tracked_embeds_many' do + it { expect(MyModel.tracked_embeds_many).to eq(['my_embedded_models']) } + end + end end From 511542d8bf398c9ef6e4244e3b4e7e59f260221d Mon Sep 17 00:00:00 2001 From: Jagdeep Singh Date: Wed, 22 Jun 2016 14:53:23 +0530 Subject: [PATCH 02/12] Fix documentation and rubocop warnings - Fix polymorphic tracking and dynamic fields tracking, Fix rspecs --- .rubocop_todo.yml | 5 +-- README.md | 12 +++---- .../history/services/options_cleaner.rb | 34 ++++++++++--------- lib/mongoid/history/trackable.rb | 31 ++++++++++++----- spec/unit/trackable_spec.rb | 28 +++++++-------- 5 files changed, 62 insertions(+), 48 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index ca1bd790..3e687085 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -13,7 +13,7 @@ Lint/HandleExceptions: # Offense count: 12 Metrics/AbcSize: - Max: 51 + Max: 60 # Offense count: 4 Metrics/CyclomaticComplexity: @@ -27,7 +27,7 @@ Metrics/LineLength: # Offense count: 7 # Configuration parameters: CountComments. Metrics/MethodLength: - Max: 33 + Max: 32 # Offense count: 2 # Configuration parameters: CountComments. @@ -41,6 +41,7 @@ Metrics/PerceivedComplexity: # Offense count: 44 Style/Documentation: Exclude: + - 'lib/mongoid/history/services/options_cleaner.rb' - 'lib/mongoid/history.rb' - 'lib/mongoid/history/trackable.rb' - 'lib/mongoid/history/tracker.rb' diff --git a/README.md b/README.md index b32929b6..dab17d0e 100644 --- a/README.md +++ b/README.md @@ -171,8 +171,7 @@ class Post field :rating embeds_many :comments - track_history :on => [:title, :body], - :embeds_many => [:comments], # track comments as embedded attributes, default is [] + track_history :on => [:title, :body, :comments], :modifier_field => :modifier, :modifier_field_inverse_of => :nil, :version_field => :version, @@ -219,16 +218,17 @@ Book.tracked_field?(:title) #=> true Book.tracked_field?(:author) #=> false ``` -**Retrieving the list of tracked embedded relations** +**Retrieving the list of tracked relations** ```ruby class Book ... - track_history :embeds_many => [:pages] + track_history :on => [:pages] end -Book.tracked_embeds_many #=> ["pages"] -Book.tracked_embeds_many?(:pages) #=> true +Book.tracked_relation?(:pages) #=> true +Book.tracked_embedded_many #=> ["pages"] +Book.tracked_embedded_many?(:pages) #=> true ``` **Displaying history trackers as an audit trail** diff --git a/lib/mongoid/history/services/options_cleaner.rb b/lib/mongoid/history/services/options_cleaner.rb index 48679f4e..32b5111b 100644 --- a/lib/mongoid/history/services/options_cleaner.rb +++ b/lib/mongoid/history/services/options_cleaner.rb @@ -3,21 +3,14 @@ module History class OptionsCleaner attr_reader :trackable, :options - def initialize(trackable, options = {}) + def initialize(trackable) @trackable = trackable - @options = default_options.merge(options) end def scope trackable.collection_name.to_s.singularize.to_sym end - # Options - # :on - # - :all OR [:all] OR :fields OR [:fields] - track all fields for now - # - :foo OR [:foo, ...] - track specified fields - # - [:, ...] - track only specified associations - # - [:all, :, ...] OR [:foo, ..., :, ...] - combination of above def default_options { on: :all, except: [:created_at, :updated_at], @@ -30,12 +23,16 @@ def default_options track_destroy: false } end - def clean + def clean(options = {}) + @options = default_options.merge(options) prepare_skipped_fields prepare_tracked_fields_and_relations - options + remove_reserved_fields + @options end + private + def prepare_skipped_fields # normalize :except fields to an array of database field strings @options[:except] = Array(options[:except]) @@ -50,21 +47,26 @@ def prepare_tracked_fields_and_relations @options[:on] = options[:on].map { |opt| (opt == :all) ? :fields : opt } @options[:on] = options[:on].map { |opt| trackable.database_field_name(opt) }.compact.uniq - if @options[:on].include?('fields') + if options[:on].include?('fields') @options[:tracked_fields] = trackable.fields.keys @options[:tracked_relations] = options[:on].reject { |opt| opt == 'fields' } else - tracked_fields_and_relations = options[:on] - options[:except] - @options[:tracked_fields] = trackable.fields.keys.select { |field| tracked_fields_and_relations.include?(field) } - @options[:tracked_relations] = tracked_fields_and_relations - options[:tracked_fields] + @options[:tracked_fields] = trackable.fields.keys.select { |field| options[:on].include?(field) } + @options[:tracked_relations] = options[:on] - options[:tracked_fields] end @options[:tracked_fields] = options[:tracked_fields] - options[:except] @options[:tracked_relations] = options[:tracked_relations] - options[:except] + @options[:tracked_dynamic] = options[:tracked_relations].dup + end + + def remove_reserved_fields + @options[:tracked_fields] = options[:tracked_fields] - reserved_fields + @options[:tracked_dynamic] = options[:tracked_dynamic] - reserved_fields end - def self.clean(trackable, options = {}) - new(trackable, options).clean + def reserved_fields + ['_id', '_type', options[:version_field].to_s, "#{options[:modifier_field]}_id"] end end end diff --git a/lib/mongoid/history/trackable.rb b/lib/mongoid/history/trackable.rb index b16df3ca..316a4b12 100644 --- a/lib/mongoid/history/trackable.rb +++ b/lib/mongoid/history/trackable.rb @@ -5,7 +5,8 @@ module Trackable module ClassMethods def track_history(options = {}) - options = Mongoid::History::OptionsCleaner.clean(self, options) + options_cleaner = Mongoid::History::OptionsCleaner.new(self) + options = options_cleaner.clean(options) field options[:version_field].to_sym, type: Integer @@ -24,7 +25,7 @@ def track_history(options = {}) before_destroy :track_destroy if options[:track_destroy] Mongoid::History.trackable_class_options ||= {} - Mongoid::History.trackable_class_options[options[:scope]] = options + Mongoid::History.trackable_class_options[options_cleaner.scope] = options end def track_history? @@ -196,7 +197,7 @@ def modified_attributes_for_create return @modified_attributes_for_create if @modified_attributes_for_create aliased_fields = self.class.aliased_fields attrs = {} - self.class.tracked_fields_for_action(:create).each { |field| attrs[field] = [nil, send(field)] } + attributes.each { |k, v| attrs[k] = [nil, v] if self.class.tracked_field?(k, :create) } self.class.tracked_embedded_one.map { |rel| aliased_fields.key(rel) || rel }.each { |rel| attrs[rel] = [nil, send(rel).attributes] } self.class.tracked_embedded_many.map { |rel| aliased_fields.key(rel) || rel }.each { |rel| attrs[rel] = [nil, send(rel).map(&:attributes)] } @modified_attributes_for_create = attrs @@ -206,7 +207,7 @@ def modified_attributes_for_destroy return @modified_attributes_for_destroy if @modified_attributes_for_destroy aliased_fields = self.class.aliased_fields attrs = {} - self.class.tracked_fields_for_action(:destroy).each { |field| attrs[field] = [send(field), nil] } + attributes.each { |k, v| attrs[k] = [v, nil] if self.class.tracked_field?(k, :destroy) } self.class.tracked_embedded_one.map { |rel| aliased_fields.key(rel) || rel }.each { |rel| attrs[rel] = [send(rel).attributes, nil] } self.class.tracked_embedded_many.map { |rel| aliased_fields.key(rel) || rel }.each { |rel| attrs[rel] = [send(rel).map(&:attributes), nil] } @modified_attributes_for_destroy = attrs @@ -321,7 +322,11 @@ def tracked_fields_for_action(action) # # @return [ Array < String > ] the base list of tracked database field names def tracked_fields - @tracked_fields ||= history_trackable_options[:tracked_fields] - reserved_tracked_fields + @tracked_fields ||= begin + fields = history_trackable_options[:tracked_fields] + history_trackable_options[:tracked_dynamic] + fields = fields - tracked_embedded_one - tracked_embedded_many + fields + end end # Retrieves the memoized list of reserved tracked fields, which are only included for certain actions. @@ -347,8 +352,8 @@ def tracked_embedded_one?(relation) def tracked_embedded_one @tracked_embedded_one ||= begin reflect_on_all_associations(:embeds_one) - .map(&:key) - .select { |rel| history_trackable_options[:tracked_relations].include? rel } + .map(&:key) + .select { |rel| history_trackable_options[:tracked_relations].include? rel } end end @@ -359,8 +364,8 @@ def tracked_embedded_many?(relation) def tracked_embedded_many @tracked_embedded_many ||= begin reflect_on_all_associations(:embeds_many) - .map(&:key) - .select { |rel| history_trackable_options[:tracked_relations].include? rel } + .map(&:key) + .select { |rel| history_trackable_options[:tracked_relations].include? rel } end end @@ -395,6 +400,14 @@ def embedded_alias(embed) embedded_aliases[embed] end + def clear_trackable_memoization + @reserved_tracked_fields = nil + @history_trackable_options = nil + @tracked_fields = nil + @tracked_embedded_one = nil + @tracked_embedded_many = nil + end + protected # Retrieves the memoized hash of embedded aliases and their associated database representations. diff --git a/spec/unit/trackable_spec.rb b/spec/unit/trackable_spec.rb index 14704fb2..540de943 100644 --- a/spec/unit/trackable_spec.rb +++ b/spec/unit/trackable_spec.rb @@ -52,16 +52,18 @@ class HistoryTracker end before(:each) { Mongoid::History.trackable_class_options = @persisted_history_options } let(:expected_option) do - { on: :all, - embeds_many: [], + { on: ['fields'], + except: %w(created_at updated_at), modifier_field: :modifier, version_field: :version, changes_method: :changes, scope: :my_model, - except: %w(created_at updated_at), track_create: false, track_update: true, - track_destroy: false } + track_destroy: false, + tracked_fields: %w(foo), + tracked_relations: [], + tracked_dynamic: [] } end let(:regular_fields) { ['foo'] } let(:reserved_fields) { %w(_id version modifier_id) } @@ -305,8 +307,8 @@ def my_changes describe 'MyInstanceMethods' do before :all do - MyModel.instance_variable_set(:@history_trackable_options, nil) - MyModel.track_history(embeds_many: [:my_embedded_models]) + MyModel.clear_trackable_memoization + MyModel.track_history(on: [:fields, :my_embedded_models]) @persisted_history_options = Mongoid::History.trackable_class_options end before(:each) { Mongoid::History.trackable_class_options = @persisted_history_options } @@ -314,10 +316,6 @@ def my_changes let(:my_untracked_embedded_models) { [MyUntrackedEmbeddedModel.new(baz: 'untracked-embedded-baz-value')] } let(:my_model) { MyModel.new(foo: 'foo-value', my_embedded_models: my_embedded_models, my_untracked_embedded_models: my_untracked_embedded_models) } - it 'should have embeds_many option' do - expect(Mongoid::History.trackable_class_options[:my_model][:embeds_many]).to eq([:my_embedded_models]) - end - describe '#modified_attributes_for_create' do subject { my_model.send(:modified_attributes_for_create) } @@ -398,18 +396,18 @@ def my_changes end end - describe '#tracked_embeds_many?' do + describe '#tracked_embedded_many?' do context 'when included in options' do - it { expect(MyModel.tracked_embeds_many?(:my_embedded_models)).to be true } + it { expect(MyModel.tracked_embedded_many?(:my_embedded_models)).to be true } end context 'when not included in options' do - it { expect(MyModel.tracked_embeds_many?(:my_untracked_embedded_models)).to be false } + it { expect(MyModel.tracked_embedded_many?(:my_untracked_embedded_models)).to be false } end end - describe '#tracked_embeds_many' do - it { expect(MyModel.tracked_embeds_many).to eq(['my_embedded_models']) } + describe '#tracked_embedded_many' do + it { expect(MyModel.tracked_embedded_many).to eq(['my_embedded_models']) } end end end From d7bd27105ed680184c406732508baab89e23d335 Mon Sep 17 00:00:00 2001 From: Jagdeep Singh Date: Thu, 23 Jun 2016 12:15:58 +0530 Subject: [PATCH 03/12] Add allow_failure for rbx-2 --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bd6420de..c6258370 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,9 +11,11 @@ rvm: - 2.1.1 - 2.0.0 - 1.9.3 - - rbx-2 - jruby-19mode +allow_failures: + - rvm: rbx-2 + env: - MONGOID_VERSION=3 - MONGOID_VERSION=4 From 409dab7f2d07da229e566e7dfacea0a7b5628441 Mon Sep 17 00:00:00 2001 From: Jagdeep Singh Date: Thu, 23 Jun 2016 16:42:56 +0530 Subject: [PATCH 04/12] Add rspecs for options_cleaner --- .rubocop_todo.yml | 1 + .../history/services/options_cleaner.rb | 4 +- spec/unit/services/options_cleaner_spec.rb | 355 ++++++++++++++++++ 3 files changed, 358 insertions(+), 2 deletions(-) create mode 100644 spec/unit/services/options_cleaner_spec.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 3e687085..a353e2b7 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -53,6 +53,7 @@ Style/Documentation: - 'spec/integration/nested_embedded_polymorphic_documents_spec.rb' - 'spec/integration/subclasses_spec.rb' - 'spec/support/mongoid_history.rb' + - 'spec/unit/services/options_cleaner_spec.rb' - 'spec/unit/trackable_spec.rb' - 'spec/unit/tracker_spec.rb' diff --git a/lib/mongoid/history/services/options_cleaner.rb b/lib/mongoid/history/services/options_cleaner.rb index 32b5111b..8128bed2 100644 --- a/lib/mongoid/history/services/options_cleaner.rb +++ b/lib/mongoid/history/services/options_cleaner.rb @@ -57,12 +57,12 @@ def prepare_tracked_fields_and_relations @options[:tracked_fields] = options[:tracked_fields] - options[:except] @options[:tracked_relations] = options[:tracked_relations] - options[:except] - @options[:tracked_dynamic] = options[:tracked_relations].dup end def remove_reserved_fields @options[:tracked_fields] = options[:tracked_fields] - reserved_fields - @options[:tracked_dynamic] = options[:tracked_dynamic] - reserved_fields + @options[:tracked_relations] = options[:tracked_relations] - reserved_fields + @options[:tracked_dynamic] = options[:tracked_relations].dup end def reserved_fields diff --git a/spec/unit/services/options_cleaner_spec.rb b/spec/unit/services/options_cleaner_spec.rb new file mode 100644 index 00000000..f81f98e6 --- /dev/null +++ b/spec/unit/services/options_cleaner_spec.rb @@ -0,0 +1,355 @@ +require 'spec_helper' + +describe Mongoid::History::OptionsCleaner do + class MyOptionsCleanerModel + include Mongoid::Document + include Mongoid::History::Trackable + field :foo + field :bar + + embeds_many :my_options_cleaner_embedded_models + embeds_many :my_options_cleaner_two_embedded_models, store_as: :emone + end + + class MyOptionsCleanerEmbeddedModel + include Mongoid::Document + field :baz + + embedded_in :my_options_cleaner_model + end + + class MyOptionsCleanerTwoEmbeddedModel + include Mongoid::Document + field :baz_two + + embedded_in :my_options_cleaner_model + end + + let(:service) { described_class.new(MyOptionsCleanerModel) } + + subject { service } + + it { is_expected.to respond_to :trackable } + it { is_expected.to respond_to :options } + + describe '#initialize' do + it { expect(service.trackable).to eq MyOptionsCleanerModel } + end + + describe '#scope' do + it { expect(service.scope).to eq :my_options_cleaner_model } + end + + describe '#default_options' do + let(:expected_options) do + { on: :all, + except: [:created_at, :updated_at], + modifier_field: :modifier, + version_field: :version, + changes_method: :changes, + scope: :my_options_cleaner_model, + track_create: false, + track_update: true, + track_destroy: false } + end + it { expect(service.default_options).to eq expected_options } + end + + describe '#clean' do + context 'when options not passed' do + let(:expected_options) do + { on: %w(fields), + except: %w(created_at updated_at), + modifier_field: :modifier, + version_field: :version, + changes_method: :changes, + scope: :my_options_cleaner_model, + track_create: false, + track_update: true, + track_destroy: false, + tracked_fields: %w(foo bar), + tracked_relations: [], + tracked_dynamic: [] } + end + it { expect(service.clean).to eq expected_options } + end + + context 'when options passed' do + subject { service.clean(options) } + + describe ':on' do + let(:options) { { on: value } } + + context 'with :fields' do + let(:value) { :fields } + it { expect(subject[:on]).to eq %w(fields) } + it { expect(subject[:tracked_fields]).to eq %w(foo bar) } + it { expect(subject[:tracked_dynamic]).to eq [] } + end + + context 'with :foo' do + let(:value) { :foo } + it { expect(subject[:on]).to eq %w(foo) } + it { expect(subject[:tracked_fields]).to eq %w(foo) } + it { expect(subject[:tracked_dynamic]).to eq [] } + end + + context 'with [:foo]' do + let(:value) { [:foo] } + it { expect(subject[:on]).to eq %w(foo) } + it { expect(subject[:tracked_fields]).to eq %w(foo) } + it { expect(subject[:tracked_dynamic]).to eq [] } + end + + context 'with :my_options_cleaner_embedded_models' do + let(:value) { :my_options_cleaner_embedded_models } + it { expect(subject[:on]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_fields]).to eq [] } + it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models) } + end + + context 'with [:my_options_cleaner_embedded_models]' do + let(:value) { [:my_options_cleaner_embedded_models] } + it { expect(subject[:on]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_fields]).to eq [] } + it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models) } + end + + context 'with [:my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models]' do + let(:value) { [:my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models] } + it { expect(subject[:on]).to eq %w(my_options_cleaner_embedded_models emone) } + it { expect(subject[:tracked_fields]).to eq [] } + it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models emone) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models emone) } + end + + context 'with [:all, :my_options_cleaner_embedded_models]' do + let(:value) { [:all, :my_options_cleaner_embedded_models] } + it { expect(subject[:on]).to eq %w(fields my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_fields]).to eq %w(foo bar) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models) } + end + + context 'with [:fields, :my_options_cleaner_embedded_models]' do + let(:value) { [:all, :my_options_cleaner_embedded_models] } + it { expect(subject[:on]).to eq %w(fields my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_fields]).to eq %w(foo bar) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models) } + end + + context 'with [:foo, :my_options_cleaner_embedded_models]' do + let(:value) { [:foo, :my_options_cleaner_embedded_models] } + it { expect(subject[:on]).to eq %w(foo my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_fields]).to eq %w(foo) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models) } + end + + context 'with [:foo, :bar, :my_options_cleaner_embedded_models]' do + let(:value) { [:foo, :bar, :my_options_cleaner_embedded_models] } + it { expect(subject[:on]).to eq %w(foo bar my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_fields]).to eq %w(foo bar) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models) } + end + + context 'with [:foo, :bar, :my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models]' do + let(:value) { [:foo, :bar, :my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models] } + it { expect(subject[:on]).to eq %w(foo bar my_options_cleaner_embedded_models emone) } + it { expect(subject[:tracked_fields]).to eq %w(foo bar) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models emone) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models emone) } + end + + context 'with :my_dynamic_field' do + let(:value) { :my_dynamic_field } + it { expect(subject[:on]).to eq %w(my_dynamic_field) } + it { expect(subject[:tracked_fields]).to eq [] } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field) } + end + + context 'with [:my_dynamic_field]' do + let(:value) { [:my_dynamic_field] } + it { expect(subject[:on]).to eq %w(my_dynamic_field) } + it { expect(subject[:tracked_fields]).to eq [] } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field) } + end + + context 'with [:all, :my_dynamic_field]' do + let(:value) { [:all, :my_dynamic_field] } + it { expect(subject[:on]).to eq %w(fields my_dynamic_field) } + it { expect(subject[:tracked_fields]).to eq %w(foo bar) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field) } + end + + context 'with [:fields, :my_dynamic_field]' do + let(:value) { [:fields, :my_dynamic_field] } + it { expect(subject[:on]).to eq %w(fields my_dynamic_field) } + it { expect(subject[:tracked_fields]).to eq %w(foo bar) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field) } + end + + context 'with [:foo, :bar, :my_dynamic_field]' do + let(:value) { [:foo, :bar, :my_dynamic_field] } + it { expect(subject[:on]).to eq %w(foo bar my_dynamic_field) } + it { expect(subject[:tracked_fields]).to eq %w(foo bar) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field) } + end + + context 'with [:my_dynamic_field, :my_options_cleaner_embedded_models]' do + let(:value) { [:my_dynamic_field, :my_options_cleaner_embedded_models] } + it { expect(subject[:on]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_fields]).to eq [] } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } + end + + context 'with [:all, :my_dynamic_field, :my_options_cleaner_embedded_models]' do + let(:value) { [:all, :my_dynamic_field, :my_options_cleaner_embedded_models] } + it { expect(subject[:on]).to eq %w(fields my_dynamic_field my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_fields]).to eq %w(foo bar) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } + end + + context 'with [:foo, :some_dynamic_field, :my_options_cleaner_embedded_models]' do + let(:value) { [:foo, :my_dynamic_field, :my_options_cleaner_embedded_models] } + it { expect(subject[:on]).to eq %w(foo my_dynamic_field my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_fields]).to eq %w(foo) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } + end + end + + describe ':except' do + let(:options) { { except: value } } + + context 'with :foo' do + let(:value) { :foo } + it { expect(subject[:on]).to eq %w(fields) } + it { expect(subject[:tracked_fields]).to eq %w(bar) } + it { expect(subject[:tracked_dynamic]).to eq [] } + end + + context 'with [:foo]' do + let(:value) { [:foo] } + it { expect(subject[:on]).to eq %w(fields) } + it { expect(subject[:tracked_fields]).to eq %w(bar) } + it { expect(subject[:tracked_dynamic]).to eq [] } + end + + context 'with [:foo, :bar]' do + let(:value) { [:foo, :bar] } + it { expect(subject[:on]).to eq %w(fields) } + it { expect(subject[:tracked_fields]).to eq [] } + it { expect(subject[:tracked_dynamic]).to eq [] } + end + + context 'with :my_options_cleaner_embedded_models' do + let(:value) { :my_options_cleaner_embedded_models } + let(:options) { { on: [:foo, :my_options_cleaner_embedded_models], except: value } } + it { expect(subject[:on]).to eq %w(foo my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_fields]).to eq %w(foo) } + it { expect(subject[:tracked_relations]).to eq [] } + it { expect(subject[:tracked_dynamic]).to eq [] } + end + + context 'with [:my_options_cleaner_embedded_models]' do + let(:value) { [:my_options_cleaner_embedded_models] } + let(:options) { { on: [:foo, :my_options_cleaner_embedded_models], except: value } } + it { expect(subject[:on]).to eq %w(foo my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_fields]).to eq %w(foo) } + it { expect(subject[:tracked_relations]).to eq [] } + it { expect(subject[:tracked_dynamic]).to eq [] } + end + + context 'with [:foo, :my_options_cleaner_embedded_models]' do + let(:value) { [:foo, :my_options_cleaner_embedded_models] } + let(:options) { { on: [:all, :my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models], except: value } } + it { expect(subject[:on]).to eq %w(fields my_options_cleaner_embedded_models emone) } + it { expect(subject[:tracked_fields]).to eq %w(bar) } + it { expect(subject[:tracked_relations]).to eq %w(emone) } + it { expect(subject[:tracked_dynamic]).to eq %w(emone) } + end + + context 'with [:foo, :my_dynamic_field]' do + let(:value) { [:foo, :my_dynamic_field] } + let(:options) { { on: [:all, :my_dynamic_field, :my_dynamic_field_two], except: value } } + it { expect(subject[:on]).to eq %w(fields my_dynamic_field my_dynamic_field_two) } + it { expect(subject[:tracked_fields]).to eq %w(bar) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field_two) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field_two) } + end + + context 'with [:my_dynamic_field, :my_options_cleaner_embedded_models]' do + let(:value) { [:my_dynamic_field, :my_options_cleaner_embedded_models] } + let(:options) { { on: [:all, :my_dynamic_field, :my_dynamic_field_two, :my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models], except: value } } + it { expect(subject[:on]).to eq %w(fields my_dynamic_field my_dynamic_field_two my_options_cleaner_embedded_models emone) } + it { expect(subject[:tracked_fields]).to eq %w(foo bar) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field_two emone) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field_two emone) } + end + + context 'with [:foo, :my_dynamic_field, :my_options_cleaner_embedded_models]' do + let(:value) { [:foo, :my_dynamic_field, :my_options_cleaner_embedded_models] } + let(:options) { { on: [:all, :my_dynamic_field, :my_dynamic_field_two, :my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models], except: value } } + it { expect(subject[:on]).to eq %w(fields my_dynamic_field my_dynamic_field_two my_options_cleaner_embedded_models emone) } + it { expect(subject[:tracked_fields]).to eq %w(bar) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field_two emone) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field_two emone) } + end + end + + describe ':modifier_field' do + let(:options) { { modifier_field: :my_modifier_field } } + it { expect(subject[:modifier_field]).to eq :my_modifier_field } + end + + describe ':version_field' do + let(:options) { { version_field: :my_version_field } } + it { expect(subject[:version_field]).to eq :my_version_field } + end + + describe ':changes_method' do + let(:options) { { changes_method: :my_changes_method } } + it { expect(subject[:changes_method]).to eq :my_changes_method } + end + + describe ':scope' do + let(:options) { { scope: :my_scope } } + it { expect(subject[:scope]).to eq :my_scope } + end + + describe ':track_create' do + let(:options) { { track_create: true } } + it { expect(subject[:track_create]).to be true } + end + + describe ':track_update' do + let(:options) { { track_update: false } } + it { expect(subject[:track_update]).to be false } + end + + describe ':track_destroy' do + let(:options) { { track_destroy: true } } + it { expect(subject[:track_destroy]).to be true } + end + + describe '#remove_reserved_fields' do + let(:options) { { on: [:_id, :_type, :foo, :version, :modifier_id] } } + it { expect(subject[:tracked_fields]).to eq %w(foo) } + it { expect(subject[:tracked_relations]).to eq [] } + it { expect(subject[:tracked_dynamic]).to eq [] } + end + end + end +end From 113d825aa53a0d358ba74b72ceb9a37a94c8bb48 Mon Sep 17 00:00:00 2001 From: Jagdeep Singh Date: Thu, 23 Jun 2016 16:50:13 +0530 Subject: [PATCH 05/12] Syntax refactoring --- lib/mongoid/history/services/options_cleaner.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mongoid/history/services/options_cleaner.rb b/lib/mongoid/history/services/options_cleaner.rb index 8128bed2..46d7b4ff 100644 --- a/lib/mongoid/history/services/options_cleaner.rb +++ b/lib/mongoid/history/services/options_cleaner.rb @@ -51,7 +51,7 @@ def prepare_tracked_fields_and_relations @options[:tracked_fields] = trackable.fields.keys @options[:tracked_relations] = options[:on].reject { |opt| opt == 'fields' } else - @options[:tracked_fields] = trackable.fields.keys.select { |field| options[:on].include?(field) } + @options[:tracked_fields] = trackable.fields.keys & options[:on] @options[:tracked_relations] = options[:on] - options[:tracked_fields] end From ab6dc25b1aa136ed5b27545e5a3f3aeb9e51a7cb Mon Sep 17 00:00:00 2001 From: Jagdeep Singh Date: Thu, 23 Jun 2016 18:15:55 +0530 Subject: [PATCH 06/12] Add rspecs --- .rubocop_todo.yml | 2 +- lib/mongoid/history/trackable.rb | 28 +++- spec/unit/trackable_spec.rb | 236 ++++++++++++++++++++----------- 3 files changed, 178 insertions(+), 88 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index a353e2b7..53dacc4d 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -32,7 +32,7 @@ Metrics/MethodLength: # Offense count: 2 # Configuration parameters: CountComments. Metrics/ModuleLength: - Max: 174 + Max: 188 # Offense count: 4 Metrics/PerceivedComplexity: diff --git a/lib/mongoid/history/trackable.rb b/lib/mongoid/history/trackable.rb index 316a4b12..2cef59f0 100644 --- a/lib/mongoid/history/trackable.rb +++ b/lib/mongoid/history/trackable.rb @@ -198,8 +198,18 @@ def modified_attributes_for_create aliased_fields = self.class.aliased_fields attrs = {} attributes.each { |k, v| attrs[k] = [nil, v] if self.class.tracked_field?(k, :create) } - self.class.tracked_embedded_one.map { |rel| aliased_fields.key(rel) || rel }.each { |rel| attrs[rel] = [nil, send(rel).attributes] } - self.class.tracked_embedded_many.map { |rel| aliased_fields.key(rel) || rel }.each { |rel| attrs[rel] = [nil, send(rel).map(&:attributes)] } + + self.class.tracked_embedded_one + .map { |rel| aliased_fields.key(rel) || rel } + .each do |rel| + obj = send(rel) + attrs[rel] = [nil, obj.attributes] if obj + end + + self.class.tracked_embedded_many + .map { |rel| aliased_fields.key(rel) || rel } + .each { |rel| attrs[rel] = [nil, send(rel).map(&:attributes)] } + @modified_attributes_for_create = attrs end @@ -208,8 +218,18 @@ def modified_attributes_for_destroy aliased_fields = self.class.aliased_fields attrs = {} attributes.each { |k, v| attrs[k] = [v, nil] if self.class.tracked_field?(k, :destroy) } - self.class.tracked_embedded_one.map { |rel| aliased_fields.key(rel) || rel }.each { |rel| attrs[rel] = [send(rel).attributes, nil] } - self.class.tracked_embedded_many.map { |rel| aliased_fields.key(rel) || rel }.each { |rel| attrs[rel] = [send(rel).map(&:attributes), nil] } + + self.class.tracked_embedded_one + .map { |rel| aliased_fields.key(rel) || rel } + .each do |rel| + obj = send(rel) + attrs[rel] = [obj.attributes, nil] if obj + end + + self.class.tracked_embedded_many + .map { |rel| aliased_fields.key(rel) || rel } + .each { |rel| attrs[rel] = [send(rel).map(&:attributes), nil] } + @modified_attributes_for_destroy = attrs end diff --git a/spec/unit/trackable_spec.rb b/spec/unit/trackable_spec.rb index 540de943..89e4f08c 100644 --- a/spec/unit/trackable_spec.rb +++ b/spec/unit/trackable_spec.rb @@ -4,24 +4,6 @@ class MyModel include Mongoid::Document include Mongoid::History::Trackable field :foo - - embeds_many :my_embedded_models - embeds_many :my_untracked_embedded_models -end - -class MyEmbeddedModel - include Mongoid::Document - field :foo - field :bar - - embedded_in :my_model -end - -class MyUntrackedEmbeddedModel - include Mongoid::Document - field :baz - - embedded_in :my_model end class MyDynamicModel @@ -307,107 +289,195 @@ def my_changes describe 'MyInstanceMethods' do before :all do - MyModel.clear_trackable_memoization - MyModel.track_history(on: [:fields, :my_embedded_models]) + MyTrackableModel = Class.new do + include Mongoid::Document + include Mongoid::History::Trackable + field :foo + field :b, as: :bar + embeds_one :my_embed_one_model, inverse_class_name: 'MyEmbedOneModel' + embeds_many :my_embed_many_models, inverse_class_name: 'MyEmbedManyModel' + end + + MyEmbedOneModel = Class.new do + include Mongoid::Document + field :baz + embedded_in :my_trackable_model + end + + MyEmbedManyModel = Class.new do + include Mongoid::Document + field :bla + embedded_in :my_trackable_model + end + + MyTrackableModel.track_history(on: [:foo, :my_embed_one_model, :my_embed_many_models]) @persisted_history_options = Mongoid::History.trackable_class_options end before(:each) { Mongoid::History.trackable_class_options = @persisted_history_options } - let(:my_embedded_models) { [MyEmbeddedModel.new(foo: 'embedded-foo-value', bar: 'embedded-bar-value')] } - let(:my_untracked_embedded_models) { [MyUntrackedEmbeddedModel.new(baz: 'untracked-embedded-baz-value')] } - let(:my_model) { MyModel.new(foo: 'foo-value', my_embedded_models: my_embedded_models, my_untracked_embedded_models: my_untracked_embedded_models) } + + let(:my_embed_one_model) { MyEmbedOneModel.new(baz: 'Baz') } + let(:my_embed_many_models) { [MyEmbedManyModel.new(bla: 'Bla')] } + let(:my_trackable_model) do + MyTrackableModel.new(foo: 'Foo', + bar: 'Bar', + my_embed_one_model: my_embed_one_model, + my_embed_many_models: my_embed_many_models) + end describe '#modified_attributes_for_create' do - subject { my_model.send(:modified_attributes_for_create) } + subject { my_trackable_model.send(:modified_attributes_for_create) } - it 'should include tracked embedded objects attributes' do - keys = subject.keys - expect(keys.size).to eq 2 - expect(keys).to include 'foo' - expect(keys).to include 'my_embedded_models' + it 'should include tracked fields' do + expect(subject['foo']).to eq [nil, 'Foo'] + expect(subject['bar']).to be_nil + end - expect(subject['foo']).to eq([nil, 'foo-value']) + it 'should include tracked embeds_one objects attributes' do + expect(subject['my_embed_one_model'][0]).to be_nil + expect(subject['my_embed_one_model'][1]['_id']).to be_a BSON::ObjectId + expect(subject['my_embed_one_model'][1]['baz']).to eq 'Baz' + end - my_embedded_model_attrs = subject['my_embedded_models'] - expect(my_embedded_model_attrs.size).to eq 2 - expect(my_embedded_model_attrs.first).to be_nil - expect(my_embedded_model_attrs.last.size).to eq 1 + it 'should include tracked embeds_many objects attributes' do + expect(subject['my_embed_many_models'][0]).to be_nil + expect(subject['my_embed_many_models'][1].size).to eq 1 + expect(subject['my_embed_many_models'][1][0]['_id']).to be_a BSON::ObjectId + expect(subject['my_embed_many_models'][1][0]['bla']).to eq 'Bla' + end - json = my_embedded_model_attrs.last.first - em_keys = json.keys - expect(em_keys.size).to eq 3 - expect(em_keys).to include '_id' - expect(em_keys).to include 'foo' - expect(em_keys).to include 'bar' + context 'when embeds_one object blank' do + let(:my_embed_one_model) { nil } - expect(json['_id']).to eq my_embedded_models[0].id - expect(json['foo']).to eq 'embedded-foo-value' - expect(json['bar']).to eq 'embedded-bar-value' + it 'should not include embeds_one model key' do + expect(subject.keys).to_not include 'my_embed_one_model' + end end end describe '#modified_attributes_for_destroy' do - subject { my_model.send(:modified_attributes_for_destroy) } + subject { my_trackable_model.send(:modified_attributes_for_destroy) } - it 'should include tracked embedded objects attributes' do - keys = subject.keys - expect(keys.size).to eq 3 - expect(keys).to include '_id' - expect(keys).to include 'foo' - expect(keys).to include 'my_embedded_models' + it 'should track reserved fields' do + expect(subject['_id'][0]).to be_a BSON::ObjectId + expect(subject['_id'][1]).to be_nil + end - expect(subject['_id']).to eq([my_model.id, nil]) - expect(subject['foo']).to eq(['foo-value', nil]) + it 'should include tracked fields' do + expect(subject['foo']).to eq ['Foo', nil] + expect(subject['bar']).to be_nil + end - my_embedded_model_attrs = subject['my_embedded_models'] - expect(my_embedded_model_attrs.size).to eq 2 - expect(my_embedded_model_attrs.first.size).to eq 1 + it 'should include tracked embeds_one objects attributes' do + expect(subject['my_embed_one_model'][0]['_id']).to be_a BSON::ObjectId + expect(subject['my_embed_one_model'][0]['baz']).to eq 'Baz' + expect(subject['my_embed_one_model'][1]).to be_nil + end - json = my_embedded_model_attrs.first.first - em_keys = json.keys - expect(em_keys.size).to eq 3 - expect(em_keys).to include '_id' - expect(em_keys).to include 'foo' - expect(em_keys).to include 'bar' + it 'should include tracked embeds_many objects attributes' do + expect(subject['my_embed_many_models'][0].size).to eq 1 + expect(subject['my_embed_many_models'][0][0]['_id']).to be_a BSON::ObjectId + expect(subject['my_embed_many_models'][0][0]['bla']).to eq 'Bla' + expect(subject['my_embed_many_models'][1]).to be_nil + end - expect(json['_id']).to eq my_embedded_models[0].id - expect(json['foo']).to eq 'embedded-foo-value' - expect(json['bar']).to eq 'embedded-bar-value' + context 'when embeds_one object blank' do + let(:my_embed_one_model) { nil } - expect(my_embedded_model_attrs.last).to be_nil + it 'should not include embeds_one model key' do + expect(subject.keys).to_not include 'my_embed_one_model' + end end end + + after :all do + Object.send(:remove_const, :MyTrackableModel) + Object.send(:remove_const, :MyEmbedOneModel) + Object.send(:remove_const, :MyEmbedManyModel) + end end describe 'SingletonMethods' do - before(:all) { MyModel.track_history(embeds_many: [:my_embedded_models]) } + before :all do + MyTrackableModel = Class.new do + include Mongoid::Document + include Mongoid::History::Trackable + field :foo + field :b, as: :bar + embeds_one :my_embed_one_model, inverse_class_name: 'MyEmbedOneModel' + embeds_one :my_untracked_embed_one_model, inverse_class_name: 'MyUntrackedEmbedOneModel' + embeds_many :my_embed_many_models, inverse_class_name: 'MyEmbedManyModel' + end - describe '#tracked?' do - describe 'embeds_many relation' do - context 'when included in options' do - it { expect(MyModel.tracked?(:my_embedded_models)).to be true } - end + MyEmbedOneModel = Class.new do + include Mongoid::Document + field :baz + embedded_in :my_trackable_model + end - context 'when not included in options' do - context 'and dynamic_field disabled' do - before { allow(MyModel).to receive(:dynamic_enabled?) { false } } - it { expect(MyModel.tracked?(:my_untracked_embedded_models)).to be false } - end - end + MyUntrackedEmbedOneModel = Class.new do + include Mongoid::Document + field :baz + embedded_in :my_trackable_model + end + + MyEmbedManyModel = Class.new do + include Mongoid::Document + field :bla + embedded_in :my_trackable_model end + + MyTrackableModel.track_history(on: [:foo, :my_embed_one_model, :my_embed_many_models, :my_dynamic_field]) end - describe '#tracked_embedded_many?' do - context 'when included in options' do - it { expect(MyModel.tracked_embedded_many?(:my_embedded_models)).to be true } + describe '#tracked?' do + it { expect(MyTrackableModel.tracked?(:foo)).to be true } + it { expect(MyTrackableModel.tracked?(:bar)).to be false } + it { expect(MyTrackableModel.tracked?(:my_embed_one_model)).to be true } + it { expect(MyTrackableModel.tracked?(:my_untracked_embed_one_model)).to be false } + it { expect(MyTrackableModel.tracked?(:my_embed_many_models)).to be true } + it { expect(MyTrackableModel.tracked?(:my_dynamic_field)).to be true } + end + + describe '#tracked_fields' do + it 'should include fields and dynamic fields' do + expect(MyTrackableModel.tracked_fields).to eq %w(foo my_dynamic_field) end + end - context 'when not included in options' do - it { expect(MyModel.tracked_embedded_many?(:my_untracked_embedded_models)).to be false } + describe '#tracked_relation?' do + it 'should return true if a relation is tracked' do + expect(MyTrackableModel.tracked_relation?(:my_embed_one_model)).to be true + expect(MyTrackableModel.tracked_relation?(:my_untracked_embed_one_model)).to be false + expect(MyTrackableModel.tracked_relation?(:my_embed_many_models)).to be true end end + describe '#tracked_embedded_one?' do + it { expect(MyTrackableModel.tracked_embedded_one?(:my_embed_one_model)).to be true } + it { expect(MyTrackableModel.tracked_embedded_one?(:my_untracked_embed_one_model)).to be false } + it { expect(MyTrackableModel.tracked_embedded_one?(:my_embed_many_models)).to be false } + end + + describe '#tracked_embedded_one' do + it { expect(MyTrackableModel.tracked_embedded_one).to include 'my_embed_one_model' } + it { expect(MyTrackableModel.tracked_embedded_one).to_not include 'my_untracked_embed_one_model' } + end + + describe '#tracked_embedded_many?' do + it { expect(MyTrackableModel.tracked_embedded_many?(:my_embed_one_model)).to be false } + it { expect(MyTrackableModel.tracked_embedded_many?(:my_untracked_embed_one_model)).to be false } + it { expect(MyTrackableModel.tracked_embedded_many?(:my_embed_many_models)).to be true } + end + describe '#tracked_embedded_many' do - it { expect(MyModel.tracked_embedded_many).to eq(['my_embedded_models']) } + it { expect(MyTrackableModel.tracked_embedded_many).to eq ['my_embed_many_models'] } + end + + after :all do + Object.send(:remove_const, :MyTrackableModel) + Object.send(:remove_const, :MyEmbedOneModel) + Object.send(:remove_const, :MyUntrackedEmbedOneModel) + Object.send(:remove_const, :MyEmbedManyModel) end end end From 50de994c7047ea16e17bf050af4a42a0d8832e00 Mon Sep 17 00:00:00 2001 From: Jagdeep Singh Date: Thu, 23 Jun 2016 18:43:27 +0530 Subject: [PATCH 07/12] Fix class definitions in rspecs, Rename OptionsCleaner to Options, Add entry in CHANGELOG --- .rubocop_todo.yml | 4 +- .travis.yml | 1 + CHANGELOG.md | 2 + lib/mongoid/history.rb | 2 +- .../options_cleaner.rb => options.rb} | 4 +- lib/mongoid/history/trackable.rb | 6 +- ...ptions_cleaner_spec.rb => options_spec.rb} | 226 +++++++++--------- 7 files changed, 127 insertions(+), 118 deletions(-) rename lib/mongoid/history/{services/options_cleaner.rb => options.rb} (97%) rename spec/unit/{services/options_cleaner_spec.rb => options_spec.rb} (62%) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 53dacc4d..6a023ed2 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -41,8 +41,8 @@ Metrics/PerceivedComplexity: # Offense count: 44 Style/Documentation: Exclude: - - 'lib/mongoid/history/services/options_cleaner.rb' - 'lib/mongoid/history.rb' + - 'lib/mongoid/history/options.rb' - 'lib/mongoid/history/trackable.rb' - 'lib/mongoid/history/tracker.rb' - 'lib/mongoid/history/version.rb' @@ -53,7 +53,7 @@ Style/Documentation: - 'spec/integration/nested_embedded_polymorphic_documents_spec.rb' - 'spec/integration/subclasses_spec.rb' - 'spec/support/mongoid_history.rb' - - 'spec/unit/services/options_cleaner_spec.rb' + - 'spec/unit/options_spec.rb' - 'spec/unit/trackable_spec.rb' - 'spec/unit/tracker_spec.rb' diff --git a/.travis.yml b/.travis.yml index c6258370..98daab62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ rvm: - 2.1.1 - 2.0.0 - 1.9.3 + - rbx-2 - jruby-19mode allow_failures: diff --git a/CHANGELOG.md b/CHANGELOG.md index 15b59a49..c7e6bbac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ * Your contribution here. +* [#150](https://github.com/aq1018/mongoid-history/pull/150): Added support for keeping embedded objects audit history in parent itself - [@JagdeepSingh](https://github.com/JagdeepSingh). + 0.5.0 (2015/09/18) ------------------ diff --git a/lib/mongoid/history.rb b/lib/mongoid/history.rb index e7eabd3f..96170699 100644 --- a/lib/mongoid/history.rb +++ b/lib/mongoid/history.rb @@ -1,6 +1,6 @@ require 'easy_diff' require 'mongoid/compatibility' -require 'mongoid/history/services/options_cleaner' +require 'mongoid/history/options' require 'mongoid/history/version' require 'mongoid/history/tracker' require 'mongoid/history/trackable' diff --git a/lib/mongoid/history/services/options_cleaner.rb b/lib/mongoid/history/options.rb similarity index 97% rename from lib/mongoid/history/services/options_cleaner.rb rename to lib/mongoid/history/options.rb index 46d7b4ff..7e0c87bb 100644 --- a/lib/mongoid/history/services/options_cleaner.rb +++ b/lib/mongoid/history/options.rb @@ -1,6 +1,6 @@ module Mongoid module History - class OptionsCleaner + class Options attr_reader :trackable, :options def initialize(trackable) @@ -23,7 +23,7 @@ def default_options track_destroy: false } end - def clean(options = {}) + def parse(options = {}) @options = default_options.merge(options) prepare_skipped_fields prepare_tracked_fields_and_relations diff --git a/lib/mongoid/history/trackable.rb b/lib/mongoid/history/trackable.rb index 2cef59f0..38a3121e 100644 --- a/lib/mongoid/history/trackable.rb +++ b/lib/mongoid/history/trackable.rb @@ -5,8 +5,8 @@ module Trackable module ClassMethods def track_history(options = {}) - options_cleaner = Mongoid::History::OptionsCleaner.new(self) - options = options_cleaner.clean(options) + options_parser = Mongoid::History::Options.new(self) + options = options_parser.parse(options) field options[:version_field].to_sym, type: Integer @@ -25,7 +25,7 @@ def track_history(options = {}) before_destroy :track_destroy if options[:track_destroy] Mongoid::History.trackable_class_options ||= {} - Mongoid::History.trackable_class_options[options_cleaner.scope] = options + Mongoid::History.trackable_class_options[options_parser.scope] = options end def track_history? diff --git a/spec/unit/services/options_cleaner_spec.rb b/spec/unit/options_spec.rb similarity index 62% rename from spec/unit/services/options_cleaner_spec.rb rename to spec/unit/options_spec.rb index f81f98e6..ec44c8e5 100644 --- a/spec/unit/services/options_cleaner_spec.rb +++ b/spec/unit/options_spec.rb @@ -1,31 +1,31 @@ require 'spec_helper' -describe Mongoid::History::OptionsCleaner do - class MyOptionsCleanerModel - include Mongoid::Document - include Mongoid::History::Trackable - field :foo - field :bar - - embeds_many :my_options_cleaner_embedded_models - embeds_many :my_options_cleaner_two_embedded_models, store_as: :emone - end - - class MyOptionsCleanerEmbeddedModel - include Mongoid::Document - field :baz - - embedded_in :my_options_cleaner_model - end +describe Mongoid::History::Options do + before :all do + MyOptionsModel = Class.new do + include Mongoid::Document + include Mongoid::History::Trackable + store_in collection: :my_options_models + field :foo + field :bar + embeds_many :my_options_embed_many_models, inverse_class_name: 'MyOptionsEmbedManyModel' + embeds_many :my_options_embed_many_two_models, store_as: :emtwo, inverse_class_name: 'MyOptionsEmbedManyTwoModel' + end - class MyOptionsCleanerTwoEmbeddedModel - include Mongoid::Document - field :baz_two + MyOptionsEmbedManyModel = Class.new do + include Mongoid::Document + field :baz + embedded_in :my_options_model + end - embedded_in :my_options_cleaner_model + MyOptionsEmbedManyTwoModel = Class.new do + include Mongoid::Document + field :baz_two + embedded_in :my_options_model + end end - let(:service) { described_class.new(MyOptionsCleanerModel) } + let(:service) { described_class.new(MyOptionsModel) } subject { service } @@ -33,11 +33,11 @@ class MyOptionsCleanerTwoEmbeddedModel it { is_expected.to respond_to :options } describe '#initialize' do - it { expect(service.trackable).to eq MyOptionsCleanerModel } + it { expect(service.trackable).to eq MyOptionsModel } end describe '#scope' do - it { expect(service.scope).to eq :my_options_cleaner_model } + it { expect(service.scope).to eq :my_options_model } end describe '#default_options' do @@ -47,7 +47,7 @@ class MyOptionsCleanerTwoEmbeddedModel modifier_field: :modifier, version_field: :version, changes_method: :changes, - scope: :my_options_cleaner_model, + scope: :my_options_model, track_create: false, track_update: true, track_destroy: false } @@ -55,7 +55,7 @@ class MyOptionsCleanerTwoEmbeddedModel it { expect(service.default_options).to eq expected_options } end - describe '#clean' do + describe '#parse' do context 'when options not passed' do let(:expected_options) do { on: %w(fields), @@ -63,7 +63,7 @@ class MyOptionsCleanerTwoEmbeddedModel modifier_field: :modifier, version_field: :version, changes_method: :changes, - scope: :my_options_cleaner_model, + scope: :my_options_model, track_create: false, track_update: true, track_destroy: false, @@ -71,11 +71,11 @@ class MyOptionsCleanerTwoEmbeddedModel tracked_relations: [], tracked_dynamic: [] } end - it { expect(service.clean).to eq expected_options } + it { expect(service.parse).to eq expected_options } end context 'when options passed' do - subject { service.clean(options) } + subject { service.parse(options) } describe ':on' do let(:options) { { on: value } } @@ -101,68 +101,68 @@ class MyOptionsCleanerTwoEmbeddedModel it { expect(subject[:tracked_dynamic]).to eq [] } end - context 'with :my_options_cleaner_embedded_models' do - let(:value) { :my_options_cleaner_embedded_models } - it { expect(subject[:on]).to eq %w(my_options_cleaner_embedded_models) } + context 'with :my_options_embed_many_models' do + let(:value) { :my_options_embed_many_models } + it { expect(subject[:on]).to eq %w(my_options_embed_many_models) } it { expect(subject[:tracked_fields]).to eq [] } - it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_embed_many_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_embed_many_models) } end - context 'with [:my_options_cleaner_embedded_models]' do - let(:value) { [:my_options_cleaner_embedded_models] } - it { expect(subject[:on]).to eq %w(my_options_cleaner_embedded_models) } + context 'with [:my_options_embed_many_models]' do + let(:value) { [:my_options_embed_many_models] } + it { expect(subject[:on]).to eq %w(my_options_embed_many_models) } it { expect(subject[:tracked_fields]).to eq [] } - it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_embed_many_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_embed_many_models) } end - context 'with [:my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models]' do - let(:value) { [:my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models] } - it { expect(subject[:on]).to eq %w(my_options_cleaner_embedded_models emone) } + context 'with [:my_options_embed_many_models, :my_options_embed_many_two_models]' do + let(:value) { [:my_options_embed_many_models, :my_options_embed_many_two_models] } + it { expect(subject[:on]).to eq %w(my_options_embed_many_models emtwo) } it { expect(subject[:tracked_fields]).to eq [] } - it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models emone) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models emone) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_embed_many_models emtwo) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_embed_many_models emtwo) } end - context 'with [:all, :my_options_cleaner_embedded_models]' do - let(:value) { [:all, :my_options_cleaner_embedded_models] } - it { expect(subject[:on]).to eq %w(fields my_options_cleaner_embedded_models) } + context 'with [:all, :my_options_embed_many_models]' do + let(:value) { [:all, :my_options_embed_many_models] } + it { expect(subject[:on]).to eq %w(fields my_options_embed_many_models) } it { expect(subject[:tracked_fields]).to eq %w(foo bar) } - it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_embed_many_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_embed_many_models) } end - context 'with [:fields, :my_options_cleaner_embedded_models]' do - let(:value) { [:all, :my_options_cleaner_embedded_models] } - it { expect(subject[:on]).to eq %w(fields my_options_cleaner_embedded_models) } + context 'with [:fields, :my_options_embed_many_models]' do + let(:value) { [:all, :my_options_embed_many_models] } + it { expect(subject[:on]).to eq %w(fields my_options_embed_many_models) } it { expect(subject[:tracked_fields]).to eq %w(foo bar) } - it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_embed_many_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_embed_many_models) } end - context 'with [:foo, :my_options_cleaner_embedded_models]' do - let(:value) { [:foo, :my_options_cleaner_embedded_models] } - it { expect(subject[:on]).to eq %w(foo my_options_cleaner_embedded_models) } + context 'with [:foo, :my_options_embed_many_models]' do + let(:value) { [:foo, :my_options_embed_many_models] } + it { expect(subject[:on]).to eq %w(foo my_options_embed_many_models) } it { expect(subject[:tracked_fields]).to eq %w(foo) } - it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_embed_many_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_embed_many_models) } end - context 'with [:foo, :bar, :my_options_cleaner_embedded_models]' do - let(:value) { [:foo, :bar, :my_options_cleaner_embedded_models] } - it { expect(subject[:on]).to eq %w(foo bar my_options_cleaner_embedded_models) } + context 'with [:foo, :bar, :my_options_embed_many_models]' do + let(:value) { [:foo, :bar, :my_options_embed_many_models] } + it { expect(subject[:on]).to eq %w(foo bar my_options_embed_many_models) } it { expect(subject[:tracked_fields]).to eq %w(foo bar) } - it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_embed_many_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_embed_many_models) } end - context 'with [:foo, :bar, :my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models]' do - let(:value) { [:foo, :bar, :my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models] } - it { expect(subject[:on]).to eq %w(foo bar my_options_cleaner_embedded_models emone) } + context 'with [:foo, :bar, :my_options_embed_many_models, :my_options_embed_many_two_models]' do + let(:value) { [:foo, :bar, :my_options_embed_many_models, :my_options_embed_many_two_models] } + it { expect(subject[:on]).to eq %w(foo bar my_options_embed_many_models emtwo) } it { expect(subject[:tracked_fields]).to eq %w(foo bar) } - it { expect(subject[:tracked_relations]).to eq %w(my_options_cleaner_embedded_models emone) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_options_cleaner_embedded_models emone) } + it { expect(subject[:tracked_relations]).to eq %w(my_options_embed_many_models emtwo) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_options_embed_many_models emtwo) } end context 'with :my_dynamic_field' do @@ -205,28 +205,28 @@ class MyOptionsCleanerTwoEmbeddedModel it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field) } end - context 'with [:my_dynamic_field, :my_options_cleaner_embedded_models]' do - let(:value) { [:my_dynamic_field, :my_options_cleaner_embedded_models] } - it { expect(subject[:on]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } + context 'with [:my_dynamic_field, :my_options_embed_many_models]' do + let(:value) { [:my_dynamic_field, :my_options_embed_many_models] } + it { expect(subject[:on]).to eq %w(my_dynamic_field my_options_embed_many_models) } it { expect(subject[:tracked_fields]).to eq [] } - it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field my_options_embed_many_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field my_options_embed_many_models) } end - context 'with [:all, :my_dynamic_field, :my_options_cleaner_embedded_models]' do - let(:value) { [:all, :my_dynamic_field, :my_options_cleaner_embedded_models] } - it { expect(subject[:on]).to eq %w(fields my_dynamic_field my_options_cleaner_embedded_models) } + context 'with [:all, :my_dynamic_field, :my_options_embed_many_models]' do + let(:value) { [:all, :my_dynamic_field, :my_options_embed_many_models] } + it { expect(subject[:on]).to eq %w(fields my_dynamic_field my_options_embed_many_models) } it { expect(subject[:tracked_fields]).to eq %w(foo bar) } - it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field my_options_embed_many_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field my_options_embed_many_models) } end - context 'with [:foo, :some_dynamic_field, :my_options_cleaner_embedded_models]' do - let(:value) { [:foo, :my_dynamic_field, :my_options_cleaner_embedded_models] } - it { expect(subject[:on]).to eq %w(foo my_dynamic_field my_options_cleaner_embedded_models) } + context 'with [:foo, :some_dynamic_field, :my_options_embed_many_models]' do + let(:value) { [:foo, :my_dynamic_field, :my_options_embed_many_models] } + it { expect(subject[:on]).to eq %w(foo my_dynamic_field my_options_embed_many_models) } it { expect(subject[:tracked_fields]).to eq %w(foo) } - it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field my_options_cleaner_embedded_models) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field my_options_embed_many_models) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field my_options_embed_many_models) } end end @@ -254,31 +254,31 @@ class MyOptionsCleanerTwoEmbeddedModel it { expect(subject[:tracked_dynamic]).to eq [] } end - context 'with :my_options_cleaner_embedded_models' do - let(:value) { :my_options_cleaner_embedded_models } - let(:options) { { on: [:foo, :my_options_cleaner_embedded_models], except: value } } - it { expect(subject[:on]).to eq %w(foo my_options_cleaner_embedded_models) } + context 'with :my_options_embed_many_models' do + let(:value) { :my_options_embed_many_models } + let(:options) { { on: [:foo, :my_options_embed_many_models], except: value } } + it { expect(subject[:on]).to eq %w(foo my_options_embed_many_models) } it { expect(subject[:tracked_fields]).to eq %w(foo) } it { expect(subject[:tracked_relations]).to eq [] } it { expect(subject[:tracked_dynamic]).to eq [] } end - context 'with [:my_options_cleaner_embedded_models]' do - let(:value) { [:my_options_cleaner_embedded_models] } - let(:options) { { on: [:foo, :my_options_cleaner_embedded_models], except: value } } - it { expect(subject[:on]).to eq %w(foo my_options_cleaner_embedded_models) } + context 'with [:my_options_embed_many_models]' do + let(:value) { [:my_options_embed_many_models] } + let(:options) { { on: [:foo, :my_options_embed_many_models], except: value } } + it { expect(subject[:on]).to eq %w(foo my_options_embed_many_models) } it { expect(subject[:tracked_fields]).to eq %w(foo) } it { expect(subject[:tracked_relations]).to eq [] } it { expect(subject[:tracked_dynamic]).to eq [] } end - context 'with [:foo, :my_options_cleaner_embedded_models]' do - let(:value) { [:foo, :my_options_cleaner_embedded_models] } - let(:options) { { on: [:all, :my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models], except: value } } - it { expect(subject[:on]).to eq %w(fields my_options_cleaner_embedded_models emone) } + context 'with [:foo, :my_options_embed_many_models]' do + let(:value) { [:foo, :my_options_embed_many_models] } + let(:options) { { on: [:all, :my_options_embed_many_models, :my_options_embed_many_two_models], except: value } } + it { expect(subject[:on]).to eq %w(fields my_options_embed_many_models emtwo) } it { expect(subject[:tracked_fields]).to eq %w(bar) } - it { expect(subject[:tracked_relations]).to eq %w(emone) } - it { expect(subject[:tracked_dynamic]).to eq %w(emone) } + it { expect(subject[:tracked_relations]).to eq %w(emtwo) } + it { expect(subject[:tracked_dynamic]).to eq %w(emtwo) } end context 'with [:foo, :my_dynamic_field]' do @@ -290,22 +290,22 @@ class MyOptionsCleanerTwoEmbeddedModel it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field_two) } end - context 'with [:my_dynamic_field, :my_options_cleaner_embedded_models]' do - let(:value) { [:my_dynamic_field, :my_options_cleaner_embedded_models] } - let(:options) { { on: [:all, :my_dynamic_field, :my_dynamic_field_two, :my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models], except: value } } - it { expect(subject[:on]).to eq %w(fields my_dynamic_field my_dynamic_field_two my_options_cleaner_embedded_models emone) } + context 'with [:my_dynamic_field, :my_options_embed_many_models]' do + let(:value) { [:my_dynamic_field, :my_options_embed_many_models] } + let(:options) { { on: [:all, :my_dynamic_field, :my_dynamic_field_two, :my_options_embed_many_models, :my_options_embed_many_two_models], except: value } } + it { expect(subject[:on]).to eq %w(fields my_dynamic_field my_dynamic_field_two my_options_embed_many_models emtwo) } it { expect(subject[:tracked_fields]).to eq %w(foo bar) } - it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field_two emone) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field_two emone) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field_two emtwo) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field_two emtwo) } end - context 'with [:foo, :my_dynamic_field, :my_options_cleaner_embedded_models]' do - let(:value) { [:foo, :my_dynamic_field, :my_options_cleaner_embedded_models] } - let(:options) { { on: [:all, :my_dynamic_field, :my_dynamic_field_two, :my_options_cleaner_embedded_models, :my_options_cleaner_two_embedded_models], except: value } } - it { expect(subject[:on]).to eq %w(fields my_dynamic_field my_dynamic_field_two my_options_cleaner_embedded_models emone) } + context 'with [:foo, :my_dynamic_field, :my_options_embed_many_models]' do + let(:value) { [:foo, :my_dynamic_field, :my_options_embed_many_models] } + let(:options) { { on: [:all, :my_dynamic_field, :my_dynamic_field_two, :my_options_embed_many_models, :my_options_embed_many_two_models], except: value } } + it { expect(subject[:on]).to eq %w(fields my_dynamic_field my_dynamic_field_two my_options_embed_many_models emtwo) } it { expect(subject[:tracked_fields]).to eq %w(bar) } - it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field_two emone) } - it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field_two emone) } + it { expect(subject[:tracked_relations]).to eq %w(my_dynamic_field_two emtwo) } + it { expect(subject[:tracked_dynamic]).to eq %w(my_dynamic_field_two emtwo) } end end @@ -352,4 +352,10 @@ class MyOptionsCleanerTwoEmbeddedModel end end end + + after :all do + Object.send(:remove_const, :MyOptionsModel) + Object.send(:remove_const, :MyOptionsEmbedManyModel) + Object.send(:remove_const, :MyOptionsEmbedManyTwoModel) + end end From e7c57bcdab1836d39026c3c921ef1412c3fc4291 Mon Sep 17 00:00:00 2001 From: Jagdeep Singh Date: Thu, 23 Jun 2016 18:48:50 +0530 Subject: [PATCH 08/12] Add comment documentation to methods --- lib/mongoid/history/trackable.rb | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/mongoid/history/trackable.rb b/lib/mongoid/history/trackable.rb index 38a3121e..a1672593 100644 --- a/lib/mongoid/history/trackable.rb +++ b/lib/mongoid/history/trackable.rb @@ -338,7 +338,7 @@ def tracked_fields_for_action(action) end end - # Retrieves the memoized base list of tracked fields, and associations excluding reserved fields. + # Retrieves the memoized base list of tracked fields, excluding reserved fields. # # @return [ Array < String > ] the base list of tracked database field names def tracked_fields @@ -356,19 +356,27 @@ def reserved_tracked_fields @reserved_tracked_fields ||= ['_id', history_trackable_options[:version_field].to_s, "#{history_trackable_options[:modifier_field]}_id"] end - # Whether or not the embedded relation should be tracked. + # Whether or not the relation should be tracked. # - # @param [ String | Symbol ] relation The name of the embedded relation + # @param [ String | Symbol ] relation The name of the relation # - # @return [ Boolean ] whether or not the embedded relation is tracked + # @return [ Boolean ] whether or not the relation is tracked def tracked_relation?(relation) tracked_embedded_one?(relation) || tracked_embedded_many?(relation) end + # Whether or not the embeds_one relation should be tracked. + # + # @param [ String | Symbol ] relation The name of the embeds_one relation + # + # @return [ Boolean ] whether or not the embeds_one relation is tracked def tracked_embedded_one?(relation) tracked_embedded_one.include?(database_field_name(relation)) end + # Retrieves the memoized list of tracked embeds_one relations + # + # @return [ Array < String > ] the list of tracked embeds_one relations def tracked_embedded_one @tracked_embedded_one ||= begin reflect_on_all_associations(:embeds_one) @@ -377,10 +385,18 @@ def tracked_embedded_one end end + # Whether or not the embeds_many relation should be tracked. + # + # @param [ String | Symbol ] relation The name of the embeds_many relation + # + # @return [ Boolean ] whether or not the embeds_many relation is tracked def tracked_embedded_many?(relation) tracked_embedded_many.include?(database_field_name(relation)) end + # Retrieves the memoized list of tracked embeds_many relations + # + # @return [ Array < String > ] the list of tracked embeds_many relations def tracked_embedded_many @tracked_embedded_many ||= begin reflect_on_all_associations(:embeds_many) From 211ff847e4a59d4e767b8b1907b16d5e9760a3f2 Mon Sep 17 00:00:00 2001 From: Jagdeep Singh Date: Fri, 24 Jun 2016 12:30:31 +0530 Subject: [PATCH 09/12] Fix rspecs for mongoid-3 --- spec/unit/trackable_spec.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/spec/unit/trackable_spec.rb b/spec/unit/trackable_spec.rb index 89e4f08c..19aa3330 100644 --- a/spec/unit/trackable_spec.rb +++ b/spec/unit/trackable_spec.rb @@ -334,14 +334,14 @@ def my_changes it 'should include tracked embeds_one objects attributes' do expect(subject['my_embed_one_model'][0]).to be_nil - expect(subject['my_embed_one_model'][1]['_id']).to be_a BSON::ObjectId + expect(subject['my_embed_one_model'][1]['_id']).to be_a Moped::BSON::ObjectId expect(subject['my_embed_one_model'][1]['baz']).to eq 'Baz' end it 'should include tracked embeds_many objects attributes' do expect(subject['my_embed_many_models'][0]).to be_nil expect(subject['my_embed_many_models'][1].size).to eq 1 - expect(subject['my_embed_many_models'][1][0]['_id']).to be_a BSON::ObjectId + expect(subject['my_embed_many_models'][1][0]['_id']).to be_a Moped::BSON::ObjectId expect(subject['my_embed_many_models'][1][0]['bla']).to eq 'Bla' end @@ -358,7 +358,7 @@ def my_changes subject { my_trackable_model.send(:modified_attributes_for_destroy) } it 'should track reserved fields' do - expect(subject['_id'][0]).to be_a BSON::ObjectId + expect(subject['_id'][0]).to be_a Moped::BSON::ObjectId expect(subject['_id'][1]).to be_nil end @@ -368,14 +368,14 @@ def my_changes end it 'should include tracked embeds_one objects attributes' do - expect(subject['my_embed_one_model'][0]['_id']).to be_a BSON::ObjectId + expect(subject['my_embed_one_model'][0]['_id']).to be_a Moped::BSON::ObjectId expect(subject['my_embed_one_model'][0]['baz']).to eq 'Baz' expect(subject['my_embed_one_model'][1]).to be_nil end it 'should include tracked embeds_many objects attributes' do expect(subject['my_embed_many_models'][0].size).to eq 1 - expect(subject['my_embed_many_models'][0][0]['_id']).to be_a BSON::ObjectId + expect(subject['my_embed_many_models'][0][0]['_id']).to be_a Moped::BSON::ObjectId expect(subject['my_embed_many_models'][0][0]['bla']).to eq 'Bla' expect(subject['my_embed_many_models'][1]).to be_nil end @@ -430,6 +430,7 @@ def my_changes end describe '#tracked?' do + before { allow(MyTrackableModel).to receive(:dynamic_enabled?) { false } } it { expect(MyTrackableModel.tracked?(:foo)).to be true } it { expect(MyTrackableModel.tracked?(:bar)).to be false } it { expect(MyTrackableModel.tracked?(:my_embed_one_model)).to be true } From 12c7d742d85f71bb0df67ad23ead2727e1d3d000 Mon Sep 17 00:00:00 2001 From: Jagdeep Singh Date: Fri, 24 Jun 2016 12:56:11 +0530 Subject: [PATCH 10/12] Fix bson class for mongoid-4 --- spec/unit/trackable_spec.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/spec/unit/trackable_spec.rb b/spec/unit/trackable_spec.rb index 19aa3330..489d6b6b 100644 --- a/spec/unit/trackable_spec.rb +++ b/spec/unit/trackable_spec.rb @@ -324,6 +324,8 @@ def my_changes my_embed_many_models: my_embed_many_models) end + let(:bson_class) { defined?(BSON::ObjectId) ? BSON::ObjectId : Moped::BSON::ObjectId } + describe '#modified_attributes_for_create' do subject { my_trackable_model.send(:modified_attributes_for_create) } @@ -334,14 +336,14 @@ def my_changes it 'should include tracked embeds_one objects attributes' do expect(subject['my_embed_one_model'][0]).to be_nil - expect(subject['my_embed_one_model'][1]['_id']).to be_a Moped::BSON::ObjectId + expect(subject['my_embed_one_model'][1]['_id']).to be_a bson_class expect(subject['my_embed_one_model'][1]['baz']).to eq 'Baz' end it 'should include tracked embeds_many objects attributes' do expect(subject['my_embed_many_models'][0]).to be_nil expect(subject['my_embed_many_models'][1].size).to eq 1 - expect(subject['my_embed_many_models'][1][0]['_id']).to be_a Moped::BSON::ObjectId + expect(subject['my_embed_many_models'][1][0]['_id']).to be_a bson_class expect(subject['my_embed_many_models'][1][0]['bla']).to eq 'Bla' end @@ -358,7 +360,7 @@ def my_changes subject { my_trackable_model.send(:modified_attributes_for_destroy) } it 'should track reserved fields' do - expect(subject['_id'][0]).to be_a Moped::BSON::ObjectId + expect(subject['_id'][0]).to be_a bson_class expect(subject['_id'][1]).to be_nil end @@ -368,14 +370,14 @@ def my_changes end it 'should include tracked embeds_one objects attributes' do - expect(subject['my_embed_one_model'][0]['_id']).to be_a Moped::BSON::ObjectId + expect(subject['my_embed_one_model'][0]['_id']).to be_a bson_class expect(subject['my_embed_one_model'][0]['baz']).to eq 'Baz' expect(subject['my_embed_one_model'][1]).to be_nil end it 'should include tracked embeds_many objects attributes' do expect(subject['my_embed_many_models'][0].size).to eq 1 - expect(subject['my_embed_many_models'][0][0]['_id']).to be_a Moped::BSON::ObjectId + expect(subject['my_embed_many_models'][0][0]['_id']).to be_a bson_class expect(subject['my_embed_many_models'][0][0]['bla']).to eq 'Bla' expect(subject['my_embed_many_models'][1]).to be_nil end From e4d15ea101973c0d33bd440e8f63830b244befba Mon Sep 17 00:00:00 2001 From: Jagdeep Singh Date: Fri, 24 Jun 2016 13:55:29 +0530 Subject: [PATCH 11/12] Move allow_failures inside matrix namespace --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 98daab62..0c6462f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,9 @@ rvm: - rbx-2 - jruby-19mode -allow_failures: - - rvm: rbx-2 +matrix: + allow_failures: + - rvm: rbx-2 env: - MONGOID_VERSION=3 From dc2bf2e87539d0d9f85bb55cfce22f85a1600a20 Mon Sep 17 00:00:00 2001 From: Jagdeep Singh Date: Fri, 24 Jun 2016 15:43:36 +0530 Subject: [PATCH 12/12] Add memoization rspecs --- spec/unit/trackable_spec.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/unit/trackable_spec.rb b/spec/unit/trackable_spec.rb index 489d6b6b..8e03ecdc 100644 --- a/spec/unit/trackable_spec.rb +++ b/spec/unit/trackable_spec.rb @@ -476,6 +476,25 @@ def my_changes it { expect(MyTrackableModel.tracked_embedded_many).to eq ['my_embed_many_models'] } end + describe '#clear_trackable_memoization' do + before do + MyTrackableModel.instance_variable_set(:@reserved_tracked_fields, %w(_id _type)) + MyTrackableModel.instance_variable_set(:@history_trackable_options, on: %w(fields)) + MyTrackableModel.instance_variable_set(:@tracked_fields, %w(foo)) + MyTrackableModel.instance_variable_set(:@tracked_embedded_one, %w(my_embed_one_model)) + MyTrackableModel.instance_variable_set(:@tracked_embedded_many, %w(my_embed_many_models)) + MyTrackableModel.clear_trackable_memoization + end + + it 'should clear all the trackable memoization' do + expect(MyTrackableModel.instance_variable_get(:@reserved_tracked_fields)).to be_nil + expect(MyTrackableModel.instance_variable_get(:@history_trackable_options)).to be_nil + expect(MyTrackableModel.instance_variable_get(:@tracked_fields)).to be_nil + expect(MyTrackableModel.instance_variable_get(:@tracked_embedded_one)).to be_nil + expect(MyTrackableModel.instance_variable_get(:@tracked_embedded_many)).to be_nil + end + end + after :all do Object.send(:remove_const, :MyTrackableModel) Object.send(:remove_const, :MyEmbedOneModel)