track_history 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/track_history.rb +109 -0
- data/lib/track_history/version.rb +5 -0
- metadata +81 -0
@@ -0,0 +1,109 @@
|
|
1
|
+
module TrackHistory
|
2
|
+
|
3
|
+
autoload :VERSION, File.join(File.dirname(__FILE__), 'track_history', 'version')
|
4
|
+
require 'active_record'
|
5
|
+
|
6
|
+
def self.install
|
7
|
+
ActiveRecord::Base.send(:include, self)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
base.extend ActsAsMethods
|
12
|
+
base.send(:include, InstanceMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
module ActsAsMethods
|
16
|
+
|
17
|
+
# Make a model historical
|
18
|
+
# Takes a hash of options, which can only be :model_name to force a different model name
|
19
|
+
# Default model name is ModelHistory
|
20
|
+
def track_history(options = {})
|
21
|
+
@historical_fields = []
|
22
|
+
@historical_tracks = {}
|
23
|
+
define_historical_model(self, options[:model_name])
|
24
|
+
yield if block_given?
|
25
|
+
end
|
26
|
+
|
27
|
+
def annotate(field, &block) # haha
|
28
|
+
@historical_tracks ||= []
|
29
|
+
@historical_tracks[field] = block
|
30
|
+
unless @klass_reference.columns_hash.has_key?(field.is_a?(Symbol) ? field.to_s : field)
|
31
|
+
raise ActiveRecord::StatementInvalid.new("No such attribute '#{field}' on #{@klass_reference.name}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def historical_fields
|
36
|
+
@historical_fields
|
37
|
+
end
|
38
|
+
|
39
|
+
def historical_tracks
|
40
|
+
@historical_tracks
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def define_historical_model(base, class_name)
|
46
|
+
|
47
|
+
class_name ||= "#{base.name}History"
|
48
|
+
klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
|
49
|
+
@klass_reference = klass
|
50
|
+
|
51
|
+
# infer fields
|
52
|
+
klass.columns_hash.each_key do |k|
|
53
|
+
matches = k.match(/(.+?)_before$/)
|
54
|
+
if matches && matches.size == 2 && field_name = matches[1]
|
55
|
+
@historical_fields << field_name if klass.columns_hash.has_key?("#{field_name}_after")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# create the history class
|
60
|
+
rel = base.name.singularize.underscore.downcase.to_sym
|
61
|
+
klass.belongs_to rel
|
62
|
+
klass.send(:alias_method, :historical_relation, rel)
|
63
|
+
klass.send(:include, HistoricalHelpers)
|
64
|
+
|
65
|
+
# tell the other class about us
|
66
|
+
# purposely don't define these until after getting historical_fields
|
67
|
+
has_many :histories, :class_name => class_name, :order => 'created_at desc'
|
68
|
+
before_save :record_historical_changes
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
module HistoricalHelpers
|
75
|
+
|
76
|
+
# Get a list of the modifications in a given history
|
77
|
+
def modifications
|
78
|
+
historical_relation.class.historical_fields.reject do |field|
|
79
|
+
send(:"#{field}_before") == send(:"#{field}_after")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
module InstanceMethods
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def record_historical_changes
|
90
|
+
return if self.class.historical_fields.empty?
|
91
|
+
# go through each and build the hashes
|
92
|
+
attributes = {}
|
93
|
+
self.class.historical_fields.each do |field|
|
94
|
+
next unless send(:"#{field}_changed?")
|
95
|
+
attributes.merge! :"#{field}_before" => send(:"#{field}_was"), :"#{field}_after" => send(field.to_sym)
|
96
|
+
end
|
97
|
+
return if attributes.empty? # nothing changed - skip out
|
98
|
+
# then go through each track
|
99
|
+
self.class.historical_tracks.each do |field, block|
|
100
|
+
attributes[field] = block.nil? ? send(field) : (block.arity == 1 ? block.call(self) : instance_eval(&block)) # give access to the user object
|
101
|
+
end
|
102
|
+
self.histories.create(attributes)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
TrackHistory::install
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: track_history
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- John Crepezzi
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-05 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
description: Smart, performant model auditing
|
36
|
+
email: john.crepezzi@patch.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- lib/track_history/version.rb
|
45
|
+
- lib/track_history.rb
|
46
|
+
has_rdoc: true
|
47
|
+
homepage: http://github.com/seejohnrun/historical
|
48
|
+
licenses: []
|
49
|
+
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 3
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
requirements: []
|
74
|
+
|
75
|
+
rubyforge_project: historical
|
76
|
+
rubygems_version: 1.3.7
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: Smart model auditing
|
80
|
+
test_files: []
|
81
|
+
|