1
0
Fork 0
mirror of https://github.com/paper-trail-gem/paper_trail.git synced 2022-11-09 11:33:19 -05:00

Condensing code for Hash args to the :ignore and :only options; also clarifying README

This commit is contained in:
Ben Atkins 2013-09-19 16:18:02 -04:00
parent 567f21b5a7
commit 22dd0cc69d
4 changed files with 50 additions and 48 deletions

View file

@ -1,5 +1,7 @@
## 3.0.0 (Unreleased) ## 3.0.0 (Unreleased)
- [#273](https://github.com/airblade/paper_trail/pull/273) - Make the `only` and `ignore` options accept `Hash` arguments;
allows for conditional tracking.
- [#264](https://github.com/airblade/paper_trail/pull/264) - Allow unwrapped symbol to be passed in to the `on` option. - [#264](https://github.com/airblade/paper_trail/pull/264) - Allow unwrapped symbol to be passed in to the `on` option.
- [#224](https://github.com/airblade/paper_trail/issues/224)/[#236](https://github.com/airblade/paper_trail/pull/236) - - [#224](https://github.com/airblade/paper_trail/issues/224)/[#236](https://github.com/airblade/paper_trail/pull/236) -
Fixed compatibility with [ActsAsTaggableOn](https://github.com/mbleigh/acts-as-taggable-on). Fixed compatibility with [ActsAsTaggableOn](https://github.com/mbleigh/acts-as-taggable-on).

View file

@ -268,7 +268,7 @@ You can ignore changes to certain attributes like this:
```ruby ```ruby
class Article < ActiveRecord::Base class Article < ActiveRecord::Base
has_paper_trail :ignore => [:title, :rating => Proc.new { |obj| obj.raiting == 0 } ] has_paper_trail :ignore => [:title, :rating]
end end
``` ```
@ -277,13 +277,10 @@ This means that changes to just the `title` or `rating` will not store another v
```ruby ```ruby
>> a = Article.create >> a = Article.create
>> a.versions.length # 1 >> a.versions.length # 1
>> a.update_attributes :title => 'My Title', :rating => 0
>> a.versions.length # 1
>> a.update_attributes :title => 'My Title', :rating => 3 >> a.update_attributes :title => 'My Title', :rating => 3
>> a.versions.length # 2 >> a.versions.length # 1
>> a.previous_version.raiting # nil
>> a.update_attributes :title => 'Greeting', :content => 'Hello' >> a.update_attributes :title => 'Greeting', :content => 'Hello'
>> a.versions.length # 3 >> a.versions.length # 2
>> a.previous_version.title # 'My Title' >> a.previous_version.title # 'My Title'
``` ```
@ -291,11 +288,11 @@ Or, you can specify a list of all attributes you care about:
```ruby ```ruby
class Article < ActiveRecord::Base class Article < ActiveRecord::Base
has_paper_trail :only => [:title, :author => Proc.new { |obj| obj.author.present? }] has_paper_trail :only => [:title]
end end
``` ```
This means that only changes to the `title` and non-empty `author` will save a version of the article: This means that only changes to the `title` will save a version of the article:
```ruby ```ruby
>> a = Article.create >> a = Article.create
@ -305,11 +302,31 @@ This means that only changes to the `title` and non-empty `author` will save a v
>> a.update_attributes :content => 'Hello' >> a.update_attributes :content => 'Hello'
>> a.versions.length # 2 >> a.versions.length # 2
>> a.previous_version.content # nil >> a.previous_version.content # nil
>> a.update_attributes :author => 'Me' ```
The `:ignore` and `:only` options can also accept `Hash` arguments, where the :
```ruby
class Article < ActiveRecord::Base
has_paper_trail :only => [:title => Proc.new { |obj| !obj.title.blank? } ]
end
```
This means that if the `title` is not blank, then only changes to the `title` will save a version of the article:
```ruby
>> a = Article.create
>> a.versions.length # 1
>> a.update_attributes :content => 'Hello'
>> a.versions.length # 2
>> a.update_attributes :title => 'My Title'
>> a.versions.length # 3 >> a.versions.length # 3
>> a.update_attributes :author => '' >> a.update_attributes :content => 'Hai'
>> a.versions.length # 3 >> a.versions.length # 3
>> a.previous_version.author # 'Me' >> a.previous_version.content # "Hello"
>> a.update_attributes :title => 'Dif Title'
>> a.versions.length # 4
>> a.previous_version.content # "Hai"
``` ```
Passing both `:ignore` and `:only` options will result in the article being saved if a changed attribute is included in `:only` but not in `:ignore`. Passing both `:ignore` and `:only` options will result in the article being saved if a changed attribute is included in `:only` but not in `:ignore`.
@ -973,6 +990,7 @@ Many thanks to:
* [Bradley Priest](https://github.com/bradleypriest) * [Bradley Priest](https://github.com/bradleypriest)
* [David Butler](https://github.com/dwbutler) * [David Butler](https://github.com/dwbutler)
* [Paul Belt](https://github.com/belt) * [Paul Belt](https://github.com/belt)
* [Vlad Bokov](https://github.com/razum2um)
## Inspirations ## Inspirations

View file

@ -15,12 +15,12 @@ module PaperTrail
# `:create`, `:update`, `:destroy` as desired. # `:create`, `:update`, `:destroy` as desired.
# :class_name the name of a custom Version class. This class should inherit from Version. # :class_name the name of a custom Version class. This class should inherit from Version.
# :ignore an array of attributes for which a new `Version` will not be created if only they change. # :ignore an array of attributes for which a new `Version` will not be created if only they change.
# it can also has a Hash as an item: the key is the attribute to ignore, the value - a Proc # it can also aceept a Hash as an argument where the key is the attribute to ignore (a `String` or `Symbol`),
# given a current state of object before save, only if it results in true - attribute will be ignored # which will only be ignored if the value is a `Proc` which returns truthily.
# :if, :unless Procs that allow to specify conditions when to save versions for an object # :if, :unless Procs that allow to specify conditions when to save versions for an object
# :only inverse of `ignore` - a new `Version` will be created only for these attributes if supplied # :only inverse of `ignore` - a new `Version` will be created only for these attributes if supplied
# it can also has a Hash as an item: the key is the attribute to track, the value - a Proc # it can also aceept a Hash as an argument where the key is the attribute to track (a `String` or `Symbol`),
# given a current state of object before save, only if it results in true - attribute will create a version # which will only be counted if the value is a `Proc` which returns truthily.
# :skip fields to ignore completely. As with `ignore`, updates to these fields will not create # :skip fields to ignore completely. As with `ignore`, updates to these fields will not create
# a new `Version`. In addition, these fields will not be included in the serialized versions # a new `Version`. In addition, these fields will not be included in the serialized versions
# of the object whenever a new `Version` is created. # of the object whenever a new `Version` is created.
@ -50,15 +50,7 @@ module PaperTrail
[:ignore, :skip, :only].each do |k| [:ignore, :skip, :only].each do |k|
paper_trail_options[k] = paper_trail_options[k] =
([paper_trail_options[k]].flatten.compact || []).map do |attr| ([paper_trail_options[k]].flatten.compact || []).map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s }
if attr.is_a? Hash
Hash[attr.map do |attr_name, condition|
[attr_name.to_s, condition]
end]
else
attr.to_s
end
end
end end
paper_trail_options[:meta] ||= {} paper_trail_options[:meta] ||= {}
@ -285,7 +277,7 @@ module PaperTrail
end end
def object_to_string(object) def object_to_string(object)
_attrs = object.attributes.except(*self.class.paper_trail_options[:skip]).tap do |attributes| _attrs = object.attributes.except(*self.paper_trail_options[:skip]).tap do |attributes|
self.class.serialize_attributes_for_paper_trail attributes self.class.serialize_attributes_for_paper_trail attributes
end end
PaperTrail.serializer.dump(_attrs) PaperTrail.serializer.dump(_attrs)
@ -296,31 +288,21 @@ module PaperTrail
end end
def notably_changed def notably_changed
only = [] only = self.paper_trail_options[:only].dup
self.class.paper_trail_options[:only].each do |attr| # remove Hash arguments and then evaluate whether the attributes (the keys of the hash) should also get pushed into the collection
if attr.is_a? Hash only.delete_if do |obj|
attr.each do |attr_name, condition| obj.is_a?(Hash) && obj.each { |attr, condition| only << attr if condition.respond_to?(:call) && condition.call(self) }
only << attr_name if condition.respond_to?(:call) && condition.call(self)
end
else
only << attr
end
end end
only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only) only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only)
end end
def changed_and_not_ignored def changed_and_not_ignored
ignore = [] ignore = self.paper_trail_options[:ignore].dup
self.class.paper_trail_options[:ignore].each do |attr| # remove Hash arguments and then evaluate whether the attributes (the keys of the hash) should also get pushed into the collection
if attr.is_a? Hash ignore.delete_if do |obj|
attr.each do |attr_name, condition| obj.is_a?(Hash) && obj.each { |attr, condition| ignore << attr if condition.respond_to?(:call) && condition.call(self) }
ignore << attr_name if condition.respond_to?(:call) && condition.call(self)
end end
else skip = self.paper_trail_options[:skip]
ignore << attr
end
end
skip = self.class.paper_trail_options[:skip]
changed - ignore - skip changed - ignore - skip
end end
@ -329,8 +311,8 @@ module PaperTrail
end end
def save_version? def save_version?
if_condition = self.class.paper_trail_options[:if] if_condition = self.paper_trail_options[:if]
unless_condition = self.class.paper_trail_options[:unless] unless_condition = self.paper_trail_options[:unless]
(if_condition.blank? || if_condition.call(self)) && !unless_condition.try(:call, self) (if_condition.blank? || if_condition.call(self)) && !unless_condition.try(:call, self)
end end
end end

View file

@ -1,6 +1,6 @@
class Article < ActiveRecord::Base class Article < ActiveRecord::Base
has_paper_trail :ignore => [:title, { :abstract => Proc.new { |obj| ['ignore abstract', 'Other abstract'].include? obj.abstract } }], has_paper_trail :ignore => [:title, :abstract => Proc.new { |obj| ['ignore abstract', 'Other abstract'].include? obj.abstract } ],
:only => [:content, { :abstract => Proc.new { |obj| obj.abstract.present? } }], :only => [:content, :abstract => Proc.new { |obj| obj.abstract.present? } ],
:skip => [:file_upload], :skip => [:file_upload],
:meta => { :meta => {
:answer => 42, :answer => 42,