attributes_history 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 047e72c287edac9652bb432b71b3fe4b2e0e7822
|
4
|
+
data.tar.gz: fe7bcfe8813faff091440a3f591467c6d6c79dc0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07329646bcc03cfc94c4687339bf3f6296f3ddf85c8ec3c4d11114b72f15eb6c0dc0cd10492e35288c7c3a105b9ed958ec7f7fec9f32faee923376ae19f4133d
|
7
|
+
data.tar.gz: e09f2bf36bd530e5894c51f3c4cc575e6fe11e346acc395d00eea6afd08b277ec8af2285e5f77b7f75f233a45145e00348af80adcb4dc965857fb945425471b2
|
data/README.md
CHANGED
@@ -96,6 +96,24 @@ This is similar to how [paper_trail](https://github.com/airblade/paper_trail)
|
|
96
96
|
works in that the versions represent past data, and only the current regular
|
97
97
|
model record (contact in this case) has the current state.
|
98
98
|
|
99
|
+
## Multiple `has_attributes_history` calls per class
|
100
|
+
|
101
|
+
If you want to have some attributes (or groups of attributes) stored in
|
102
|
+
different tables grouped by semantic meaning or because their size or rate of
|
103
|
+
change is different, you can specify multiple `has_attributes_history` calls per
|
104
|
+
class. The lists of attributes for the different calls can't have any
|
105
|
+
overlapping attributes though or that will confuse the lookup logic.
|
106
|
+
|
107
|
+
Here's an example of tracking `notes` in a separate log from `status` and
|
108
|
+
`pledge`:
|
109
|
+
|
110
|
+
```
|
111
|
+
class Contact < ActiveRecord::Base
|
112
|
+
has_attributes_history for: [:status, :pledge], with_model: PartnerStatusLog
|
113
|
+
has_attributes_history for: [:notes], with_model: ContactNotesLog
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
99
117
|
## Enabling and disabling
|
100
118
|
|
101
119
|
By default the history logging is enabled once you set it up, but you can
|
@@ -6,22 +6,51 @@ module AttributesHistory
|
|
6
6
|
module HasAttributesHistory
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
|
+
included do
|
10
|
+
class_attribute :history_models, :history_associations
|
11
|
+
self.history_models ||= {}
|
12
|
+
self.history_associations ||= {}
|
13
|
+
end
|
14
|
+
|
9
15
|
module ClassMethods
|
10
16
|
# The options should include the keys :attributes and :version_class
|
11
17
|
def has_attributes_history(options)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
self.history_association = history_model.name.underscore.pluralize.to_sym
|
18
|
+
history_model = options[:with_model]
|
19
|
+
history_attributes = options[:for].map(&:to_s)
|
20
|
+
history_association = history_model.name.underscore.pluralize.to_sym
|
16
21
|
|
17
22
|
has_many history_association
|
18
|
-
|
19
|
-
|
23
|
+
|
24
|
+
store_history_options(history_model, history_attributes, history_association)
|
25
|
+
setup_history_callback(history_attributes, history_model)
|
26
|
+
define_attribute_on_date_methods(history_attributes)
|
27
|
+
end
|
28
|
+
|
29
|
+
def history_model(attribute)
|
30
|
+
self.history_models[attribute]
|
31
|
+
end
|
32
|
+
|
33
|
+
def history_association(attribute)
|
34
|
+
self.history_associations[attribute]
|
20
35
|
end
|
21
36
|
|
22
37
|
private
|
23
38
|
|
24
|
-
def
|
39
|
+
def store_history_options(history_model, history_attributes, history_association)
|
40
|
+
history_attributes.each do |attribute|
|
41
|
+
self.history_associations[attribute] = history_association
|
42
|
+
self.history_models[attribute] = history_model
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def setup_history_callback(history_attributes, history_model)
|
47
|
+
after_update do
|
48
|
+
HistorySaver.new(self, history_attributes, history_model)
|
49
|
+
.save_if_needed
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def define_attribute_on_date_methods(history_attributes)
|
25
54
|
define_method :attribute_on_date do |attribute, date|
|
26
55
|
@history_retriever ||= HistoryRetriever.new(self)
|
27
56
|
@history_retriever.attribute_on_date(attribute, date)
|
@@ -6,14 +6,18 @@ module AttributesHistory
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def attribute_on_date(attribute, date)
|
9
|
-
|
9
|
+
history_association = @object.class.history_association(attribute)
|
10
|
+
|
11
|
+
history_entry = @cached_history[[history_association, date]] ||=
|
12
|
+
find_entry_on(history_association, date)
|
13
|
+
|
10
14
|
history_entry.public_send(attribute)
|
11
15
|
end
|
12
16
|
|
13
17
|
private
|
14
18
|
|
15
|
-
def find_entry_on(date)
|
16
|
-
@object.public_send(
|
19
|
+
def find_entry_on(history_association, date)
|
20
|
+
@object.public_send(history_association)
|
17
21
|
.where('recorded_on > ?', date).order(:recorded_on).first || @object
|
18
22
|
end
|
19
23
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
module AttributesHistory
|
2
2
|
class HistorySaver
|
3
|
-
def initialize(changed_object)
|
3
|
+
def initialize(changed_object, history_attributes, history_model)
|
4
4
|
@object = changed_object
|
5
|
+
@history_attributes = history_attributes
|
6
|
+
@history_model = history_model
|
5
7
|
end
|
6
8
|
|
7
9
|
def save_if_needed
|
@@ -12,11 +14,14 @@ module AttributesHistory
|
|
12
14
|
private
|
13
15
|
|
14
16
|
def history_attributes_changed?
|
15
|
-
(@object.changed & @
|
17
|
+
(@object.changed & @history_attributes).present?
|
16
18
|
end
|
17
19
|
|
18
20
|
def save_history_entry
|
19
|
-
|
21
|
+
history_association =
|
22
|
+
@object.class.history_association(@history_attributes.first)
|
23
|
+
|
24
|
+
history_entry = @object.public_send(history_association)
|
20
25
|
.find_or_initialize_by(recorded_on: Date.current)
|
21
26
|
|
22
27
|
# If there is an existing history record for today, just leave it as is,
|
@@ -25,7 +30,7 @@ module AttributesHistory
|
|
25
30
|
end
|
26
31
|
|
27
32
|
def history_params
|
28
|
-
Hash[@
|
33
|
+
Hash[@history_attributes.map { |f| [f, history_value_for(f)] }]
|
29
34
|
end
|
30
35
|
|
31
36
|
def history_value_for(attribute)
|