active-audit 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/active-audit.rb +34 -0
- data/lib/active_audit/audit.rb +83 -0
- data/lib/active_audit/audit_pusher.rb +10 -0
- data/lib/active_audit/audit_repository.rb +38 -0
- data/lib/active_audit/base.rb +82 -0
- data/lib/active_audit/dirty_association.rb +95 -0
- data/lib/active_audit/errors.rb +5 -0
- data/lib/active_audit/storage_adapters/active_record_adapter.rb +28 -0
- data/lib/active_audit/storage_adapters/elasticsearch_adapter.rb +66 -0
- data/lib/active_audit/storage_adapters/mongo_adapter.rb +22 -0
- data/lib/active_audit/storage_adapters/test_adapter.rb +17 -0
- data/lib/active_audit/storage_adapters.rb +10 -0
- data/lib/active_audit/sweeper.rb +55 -0
- data/lib/generators/active_audit/install_generator.rb +34 -0
- data/lib/generators/active_audit/templates/adapter_config/elastic_search_config.yml +12 -0
- data/lib/generators/active_audit/templates/adapter_config/mongo_config.yml +10 -0
- data/lib/generators/active_audit/templates/initializer.rb +9 -0
- data/lib/generators/active_audit/templates/migration.rb +22 -0
- data/lib/generators/active_audit/templates/mongo_config.yml +10 -0
- metadata +91 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e1fca837bb3e2d692f664310ebe1beffad3484bd
|
4
|
+
data.tar.gz: a7f74a56a41b4443b00de87699df32bef7278d48
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5cc11c9ece3877bac607d8083b56bf40e649a929c91768abe22174cdf32527b93c60bc4c917d6e529f79e704957cbd0fc509b27061c3608dba2cc82938fbe8e6
|
7
|
+
data.tar.gz: 9d089b1ac422adf173792fa6a4ae1c326c9bc62cbf1d951bdda6bb9ea9ae92bd811add470032b166530606dd12718316b2f53870ff5c91d2d7f52d3e287af24b
|
data/lib/active-audit.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_audit/audit_repository'
|
3
|
+
|
4
|
+
module ActiveAudit
|
5
|
+
extend ActiveSupport::Autoload
|
6
|
+
|
7
|
+
eager_autoload { autoload :Base }
|
8
|
+
autoload :StorageAdapters
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
attr_accessor :storage_adapter, :current_user_method, :ignored_attributes, :job_queue, :delayed_auditing, :default_user, :extract_user_profile
|
13
|
+
|
14
|
+
def configure
|
15
|
+
@current_user_method = :current_user
|
16
|
+
@ignored_attributes = %w(created_at updated_at)
|
17
|
+
@job_queue = :audits
|
18
|
+
@delayed_auditing = false
|
19
|
+
@extract_user_profile = lambda { |user| { id: user.id } }
|
20
|
+
self.eager_load!
|
21
|
+
yield(self) if block_given?
|
22
|
+
AuditRepository.storage_adapter = storage_adapter if storage_adapter
|
23
|
+
end
|
24
|
+
|
25
|
+
def session
|
26
|
+
Thread.current[:auditing_store] ||= {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_hint comment
|
30
|
+
self.session[:comment] = comment
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'rails/observers/active_model/active_model'
|
2
|
+
require 'active_model'
|
3
|
+
require 'virtus'
|
4
|
+
|
5
|
+
module ActiveAudit
|
6
|
+
class Audit
|
7
|
+
include Virtus.model
|
8
|
+
|
9
|
+
extend ActiveModel::Callbacks
|
10
|
+
define_model_callbacks :create, :save
|
11
|
+
|
12
|
+
include ActiveModel::Observing
|
13
|
+
|
14
|
+
attribute :item_id, Integer
|
15
|
+
attribute :event, String
|
16
|
+
attribute :type, String
|
17
|
+
attribute :changes, Hash
|
18
|
+
attribute :user, Hash, default: lambda {|o,a| ActiveAudit.default_user }
|
19
|
+
attribute :comment, String
|
20
|
+
attribute :recorded_at, Time, default: lambda { |o,a| Time.now.utc }
|
21
|
+
|
22
|
+
before_save do
|
23
|
+
self.recorded_at = Time.at(self.recorded_at) if self.recorded_at.is_a? Integer
|
24
|
+
self.user = ActiveAudit.extract_user_profile.call(self.user) unless self.user.nil? || self.user.is_a?(Hash)
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize *args
|
28
|
+
attributes = args.extract_options!
|
29
|
+
if attributes.empty?
|
30
|
+
if args.count == 2
|
31
|
+
initialize_from_record(*args)
|
32
|
+
else
|
33
|
+
raise ArgumentError, "You need to supply at least one attribute"
|
34
|
+
end
|
35
|
+
else
|
36
|
+
super attributes
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private def initialize_from_record event, record
|
41
|
+
if event == 'update'
|
42
|
+
if record.respond_to?(:aasm) && record.aasm.current_event
|
43
|
+
event = record.aasm.current_event.to_s.sub(/!$/, '')
|
44
|
+
end
|
45
|
+
self.changes = record.previous_changes.select { |key, value| record.class.auditing_options[:only].include? key }
|
46
|
+
self.changes.merge!(record.association_previous_changes.select { |key, value| record.class.auditing_options[:associations].include? key })
|
47
|
+
self.changes = self.changes.map do |key, values|
|
48
|
+
if values.any? { |v| v.is_a?(Time) }
|
49
|
+
[key, values.map { |x| x && x.utc.iso8601 }]
|
50
|
+
else
|
51
|
+
[key, values]
|
52
|
+
end
|
53
|
+
end.to_h
|
54
|
+
end
|
55
|
+
self.event = event
|
56
|
+
self.type = record.class.auditing_options[:type]
|
57
|
+
self.item_id = record.id
|
58
|
+
set_default_attributes
|
59
|
+
end
|
60
|
+
|
61
|
+
def changed?
|
62
|
+
['create', 'destroy'].include?(self.event) || changes.present?
|
63
|
+
end
|
64
|
+
|
65
|
+
def save options={}
|
66
|
+
self.run_callbacks :save do
|
67
|
+
yield if changed?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.create(attributes, options={})
|
72
|
+
object = self.new(attributes)
|
73
|
+
object.run_callbacks :create do
|
74
|
+
object.save(options)
|
75
|
+
object
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def serialize
|
80
|
+
self.attributes.select {|k,v| v.present?}.merge(recorded_at: self.recorded_at.to_i, type: self.type)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ActiveAudit
|
2
|
+
class AuditRepository
|
3
|
+
class << self
|
4
|
+
def storage_adapter=(repo_name)
|
5
|
+
@@storage_adapter = load_adapter(repo_name)
|
6
|
+
end
|
7
|
+
|
8
|
+
def storage_adapter
|
9
|
+
@@storage_adapter ||= ActiveAudit::StorageAdapters::TestAdapter
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_by_record record, options={}
|
13
|
+
storage_adapter.find_by_record(record, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create attributes
|
17
|
+
save Audit.new(attributes)
|
18
|
+
end
|
19
|
+
|
20
|
+
def save audit
|
21
|
+
audit.save do
|
22
|
+
storage_adapter.save audit
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def load_adapter name
|
28
|
+
config = \
|
29
|
+
begin
|
30
|
+
Rails.application.config_for(:active_audit)
|
31
|
+
rescue RuntimeError
|
32
|
+
{}
|
33
|
+
end
|
34
|
+
return "ActiveAudit::StorageAdapters::#{name.to_s.camelize}Adapter".constantize.new config
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'active_audit/errors'
|
2
|
+
require 'active_audit/audit'
|
3
|
+
require 'active_audit/audit_pusher'
|
4
|
+
require 'active_audit/dirty_association'
|
5
|
+
require 'active_audit/sweeper'
|
6
|
+
|
7
|
+
module ActiveAudit
|
8
|
+
module Base
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
include DirtyAssociation
|
11
|
+
|
12
|
+
class_methods do
|
13
|
+
|
14
|
+
def auditing_options
|
15
|
+
@auditing_options ||= {
|
16
|
+
type: self.to_s.underscore,
|
17
|
+
only: self.attribute_names - ActiveAudit.ignored_attributes
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def audit *args
|
22
|
+
options = args.extract_options!.dup
|
23
|
+
options.assert_valid_keys(:type, :except, :associations, :unless)
|
24
|
+
options[:type] = options[:type].to_s if options[:type]
|
25
|
+
except = options[:except] ? options[:except].map(&:to_s) : []
|
26
|
+
only = if args.present? then args.map(&:to_s) else auditing_options[:only] end
|
27
|
+
options[:only] = only - except
|
28
|
+
if options[:associations]
|
29
|
+
options[:associations] = options[:associations].map(&:to_s)
|
30
|
+
stain *options[:associations]
|
31
|
+
end
|
32
|
+
auditing_options.update options
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
included do
|
38
|
+
if self < ActiveRecord::Base
|
39
|
+
after_commit on: [:create] { write_audit('create') }
|
40
|
+
after_commit on: [:update] { write_audit('update') }
|
41
|
+
after_commit on: [:destroy] { write_audit('destroy') }
|
42
|
+
elsif defined?(Mongoid::Document) && self < Mongoid::Document
|
43
|
+
after_create { write_audit('create') }
|
44
|
+
after_update { write_audit('update') }
|
45
|
+
after_destroy { write_audit('destroy') }
|
46
|
+
else
|
47
|
+
raise Errors::UnsupportedModel, "can audit ActiveRecord and Mongoid models only"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def audits options={}
|
52
|
+
AuditRepository.find_by_record self, options
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def audited?
|
57
|
+
if condition = self.class.auditing_options[:unless]
|
58
|
+
case condition
|
59
|
+
when Symbol, String
|
60
|
+
self.public_send condition
|
61
|
+
when Proc
|
62
|
+
condition.call self
|
63
|
+
end
|
64
|
+
end
|
65
|
+
return true
|
66
|
+
end
|
67
|
+
|
68
|
+
def write_audit event
|
69
|
+
if audited?
|
70
|
+
audit = Audit.new event, self
|
71
|
+
if audit.changed?
|
72
|
+
if ActiveAudit.delayed_auditing
|
73
|
+
AuditPusher.perform_later audit.serialize
|
74
|
+
else
|
75
|
+
AuditRepository.save(audit)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'active_support/hash_with_indifferent_access'
|
2
|
+
|
3
|
+
module ActiveAudit
|
4
|
+
module DirtyAssociation
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
class_methods do
|
8
|
+
def stain *association_names
|
9
|
+
association_names.each do |association_name|
|
10
|
+
if reflection = _reflect_on_association(association_name)
|
11
|
+
if reflection.collection? && through_refl = reflection.try(:through_reflection)
|
12
|
+
through_id = reflection.foreign_key
|
13
|
+
through_name = through_refl.name
|
14
|
+
define_association_method_suffix association_name
|
15
|
+
ActiveRecord::Associations::Builder::CollectionAssociation.define_callback(self, :after_add, through_name, before_add: lambda do |model, relation|
|
16
|
+
puts "addubng"
|
17
|
+
puts relation.attributes
|
18
|
+
DirtyAssociation.record_add association_name, model, relation, through_name, through_id
|
19
|
+
end)
|
20
|
+
ActiveRecord::Associations::Builder::CollectionAssociation.define_callback(self, :before_remove, through_name, before_remove: lambda do |model, relation|
|
21
|
+
DirtyAssociation.record_remove association_name, model, relation, through_name, through_id
|
22
|
+
end)
|
23
|
+
else
|
24
|
+
raise ArgumentError, "'#{association_name}' is not a many-to-many association."
|
25
|
+
end
|
26
|
+
else
|
27
|
+
raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
private
|
34
|
+
def define_association_method_suffix association_name
|
35
|
+
%w(_changed? _change _was _previously_changed? _previous_change).each do |suffix|
|
36
|
+
self.send :define_method, "#{association_name}#{suffix}" do
|
37
|
+
send "association#{suffix}", association_name
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :association_previous_changes
|
44
|
+
|
45
|
+
def association_changes
|
46
|
+
@association_changes ||= ActiveSupport::HashWithIndifferentAccess.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def association_changed? association_name
|
50
|
+
!!association_changes[association_name]
|
51
|
+
end
|
52
|
+
|
53
|
+
def association_change association_name
|
54
|
+
association_changes[association_name]
|
55
|
+
end
|
56
|
+
|
57
|
+
def association_was association_name
|
58
|
+
association_changes[association_name].try '[]', 0
|
59
|
+
end
|
60
|
+
|
61
|
+
def association_previously_changed? association_name
|
62
|
+
!!association_previous_changes[association_name]
|
63
|
+
end
|
64
|
+
|
65
|
+
def association_previous_change association_name
|
66
|
+
association_previous_changes[association_name]
|
67
|
+
end
|
68
|
+
|
69
|
+
def changes_applied
|
70
|
+
super
|
71
|
+
@association_previous_changes = association_changes
|
72
|
+
@association_changes = ActiveSupport::HashWithIndifferentAccess.new
|
73
|
+
end
|
74
|
+
|
75
|
+
module_function
|
76
|
+
def init_association_change association_name, model, through, attribute
|
77
|
+
old_attributes = model.send(through).map {|r| r.send attribute }
|
78
|
+
model.association_changes[association_name] = [old_attributes, old_attributes.dup]
|
79
|
+
end
|
80
|
+
|
81
|
+
def record_add association_name, model, relation, through, attribute
|
82
|
+
unless model.association_changes[association_name]
|
83
|
+
init_association_change association_name, model, through, attribute
|
84
|
+
end
|
85
|
+
model.association_changes[association_name][1].push relation.send(attribute)
|
86
|
+
end
|
87
|
+
|
88
|
+
def record_remove association_name, model, relation, through, attribute
|
89
|
+
unless model.association_changes[association_name]
|
90
|
+
init_association_change association_name, model, through, attribute
|
91
|
+
end
|
92
|
+
model.association_changes[association_name][1].delete relation.send(attribute)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ActiveAudit
|
2
|
+
module StorageAdapters
|
3
|
+
class ActiveRecordAdapter
|
4
|
+
|
5
|
+
def initialize options={}
|
6
|
+
@connection = ::ActiveRecord::Base.connection
|
7
|
+
end
|
8
|
+
|
9
|
+
def find_by_record record, options={}
|
10
|
+
result = @connection.exec_query "SELECT * FROM audits WHERE item_id=$1 AND type=$2 ORDER BY recorded_at DESC;", nil, [[nil, record.id], [nil,record.class.auditing_options[:type]]]
|
11
|
+
result.map { |attributes| ActiveAudit::Audit.new attributes }
|
12
|
+
end
|
13
|
+
|
14
|
+
def save audit
|
15
|
+
@connection.exec_query('INSERT INTO audits("item_id", "event", "type", "changes", "user_id", "user", "comment", "recorded_at") VALUES ($1,$2,$3,$4,$5,$6,$7,$8);', nil, [
|
16
|
+
[nil, audit.item_id],
|
17
|
+
[nil, audit.event],
|
18
|
+
[nil, audit.type],
|
19
|
+
[nil, audit.changes.to_json],
|
20
|
+
[nil, audit.user[:id]],
|
21
|
+
[nil, audit.user.to_json],
|
22
|
+
[nil, audit.comment],
|
23
|
+
[nil, audit.recorded_at]
|
24
|
+
])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'elasticsearch/persistence'
|
2
|
+
|
3
|
+
module ActiveAudit
|
4
|
+
module StorageAdapters
|
5
|
+
class ElasticsearchAdapter
|
6
|
+
include ::Elasticsearch::Persistence::Repository
|
7
|
+
|
8
|
+
def initialize options={}
|
9
|
+
index options['index'] || :auditing
|
10
|
+
client ::Elasticsearch::Client.new url: options['url'] || 'localhost:9200'
|
11
|
+
klass ActiveAudit::Audit
|
12
|
+
end
|
13
|
+
|
14
|
+
type :_default_
|
15
|
+
|
16
|
+
settings do
|
17
|
+
mappings _all: { enabled: false }, date_detection: false, dynamic_templates: [ {
|
18
|
+
strings: {
|
19
|
+
mapping: {
|
20
|
+
index: "not_analyzed",
|
21
|
+
type: "string"
|
22
|
+
},
|
23
|
+
match: "*",
|
24
|
+
match_mapping_type: "string"
|
25
|
+
}
|
26
|
+
} ] do
|
27
|
+
indexes :item_id, type: "long"
|
28
|
+
indexes :event, type: "string", index: "not_analyzed"
|
29
|
+
indexes :changes, type: "object", dynamic: true
|
30
|
+
indexes :user, type: "object", properties: {
|
31
|
+
id: { type: "long" },
|
32
|
+
name: {type: "string", index: "not_analyzed"}
|
33
|
+
}
|
34
|
+
indexes :comment, type: "string", index: "not_analyzed"
|
35
|
+
indexes :recorded_at, type: "date"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_by_record record, options={}
|
40
|
+
self.type "#{record.class.auditing_options[:type]}_events"
|
41
|
+
query = {
|
42
|
+
query: { filtered: { filter: { bool: { must: [{ term: { item_id: record.id }}]}}}},
|
43
|
+
sort: { recorded_at: { order: "desc" }}
|
44
|
+
}
|
45
|
+
if options[:from] && options[:to]
|
46
|
+
query[:query][:filtered][:filter][:bool][:must].push range: { recorded_at: { gte: options[:from], lt: options[:to] }}
|
47
|
+
elsif options[:from]
|
48
|
+
query[:query][:filtered][:filter][:bool][:must].push range: { recorded_at: { gte: options[:from] }}
|
49
|
+
elsif options[:to]
|
50
|
+
query[:query][:filtered][:filter][:bool][:must].push range: { recorded_at: { lt: options[:to] }}
|
51
|
+
end
|
52
|
+
if options[:comment]
|
53
|
+
query[:query][:filtered][:filter][:bool][:must].push wildcard: { comment: "*#{options[:comment]}*" }
|
54
|
+
end
|
55
|
+
search(query).to_a
|
56
|
+
end
|
57
|
+
|
58
|
+
define_method :gateway_save, self.new.gateway.method(:save).to_proc
|
59
|
+
|
60
|
+
def save audit
|
61
|
+
self.send :gateway_save, audit, type: "#{audit.type}_events"
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
|
3
|
+
module ActiveAudit
|
4
|
+
module StorageAdapters
|
5
|
+
class MongoAdapter
|
6
|
+
|
7
|
+
def initialize options={}
|
8
|
+
client = ::Mongo::Client.new options['hosts'], options['options'].symbolize_keys
|
9
|
+
@collection = client[:audits]
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_by_record record, options={}
|
13
|
+
result = @collection.find(type: record.class.auditing_options[:type], item_id: record.id)
|
14
|
+
result.map { |audit| ActiveAudit::Audit.new audit.symbolize_keys }
|
15
|
+
end
|
16
|
+
|
17
|
+
def save audit
|
18
|
+
@collection.insert_one(audit.attributes)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveAudit
|
2
|
+
module StorageAdapters
|
3
|
+
class TestAdapter
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def find_by_record record, options={}
|
7
|
+
raise NotImplementedError.new "can not retrieve audits with `TestAdapter`."
|
8
|
+
end
|
9
|
+
|
10
|
+
def save audit
|
11
|
+
Rails.logger.info "audit -> #{audit.attributes}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "rails/observers/activerecord/active_record"
|
2
|
+
require "rails/observers/action_controller/caching"
|
3
|
+
|
4
|
+
module ActiveAudit
|
5
|
+
class Sweeper < ActionController::Caching::Sweeper
|
6
|
+
observe ActiveAudit::Audit
|
7
|
+
|
8
|
+
def around controller
|
9
|
+
begin
|
10
|
+
self.controller = controller
|
11
|
+
self.user = current_user
|
12
|
+
yield
|
13
|
+
ensure
|
14
|
+
self.controller = nil
|
15
|
+
self.user = nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def before_create audit
|
20
|
+
audit.user = self.user if self.user
|
21
|
+
audit.comment = controller.params[:comment] if controller.respond_to?(:params, true)
|
22
|
+
audit.comment ||= ActiveAudit.session[:comment]
|
23
|
+
end
|
24
|
+
|
25
|
+
def current_user
|
26
|
+
current_user = controller.send(ActiveAudit.current_user_method) if controller.respond_to?(ActiveAudit.current_user_method, true)
|
27
|
+
end
|
28
|
+
|
29
|
+
def controller
|
30
|
+
ActiveAudit.session[:current_controller]
|
31
|
+
end
|
32
|
+
|
33
|
+
def controller=(value)
|
34
|
+
ActiveAudit.session[:current_controller] = value
|
35
|
+
end
|
36
|
+
|
37
|
+
def user
|
38
|
+
ActiveAudit.session[:current_user]
|
39
|
+
end
|
40
|
+
|
41
|
+
def user=(value)
|
42
|
+
ActiveAudit.session[:current_user] = value
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
ActiveSupport.on_load(:action_controller) do
|
48
|
+
if defined?(ActionController::Base)
|
49
|
+
ActionController::Base.around_action ActiveAudit::Sweeper.instance
|
50
|
+
end
|
51
|
+
if defined?(ActionController::API)
|
52
|
+
ActionController::API.around_action ActiveAudit::Sweeper.instance
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/active_record'
|
3
|
+
|
4
|
+
module ActiveAudit
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
include ActiveRecord::Generators::Migration
|
8
|
+
|
9
|
+
desc "Create initializer and config files for active audit base on the adapter you define. In case of 'active_record' adapter a migration will be generated as well."
|
10
|
+
|
11
|
+
argument :adapter, required: true, type: :string, desc: "The name of the storage adapter you want to use.",
|
12
|
+
:banner => "adapter_name"
|
13
|
+
|
14
|
+
source_root File.expand_path("../templates", __FILE__)
|
15
|
+
|
16
|
+
def copy_initializer_file
|
17
|
+
template "initializer.rb", "config/initializers/active_audit.rb"
|
18
|
+
end
|
19
|
+
|
20
|
+
def copy_config_file
|
21
|
+
unless adapter == 'active_record'
|
22
|
+
copy_file "#{adapter}_config.yml", "config/active_audit.yml"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def generate_migration
|
27
|
+
if adapter == 'active_record'
|
28
|
+
migration_template 'migration.rb', 'db/migrate/create_audits.rb'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
development:
|
2
|
+
host: 'http://localhost:9200'
|
3
|
+
index: auditing
|
4
|
+
test:
|
5
|
+
url: 'http://localhost:9200'
|
6
|
+
index: auditing
|
7
|
+
staging:
|
8
|
+
url: 'http://localhost:9200'
|
9
|
+
index: auditing
|
10
|
+
production:
|
11
|
+
url: <%= ENV["AUDIT_STORE_HOST"] %>
|
12
|
+
index: <%= ENV["AUDIT_STORE_INDEX"] %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
ActiveAudit.configure do |config|
|
2
|
+
config.storage_adapter = :<%= adapter.to_sym %>
|
3
|
+
#config.current_user_method = :current_user
|
4
|
+
#config.ignored_attributes = %w(created_at updated_at)
|
5
|
+
#config.job_queue = :audits
|
6
|
+
#config.delayed_auditing = false
|
7
|
+
#config.default_user = { id: 1 }
|
8
|
+
#config.extract_user_profile = lambda { |user| { id: user.id } }
|
9
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :audits, :force => true do |t|
|
4
|
+
t.column :item_id, :integer
|
5
|
+
t.column :type, :string
|
6
|
+
t.column :event, :string
|
7
|
+
t.column :changes, :json
|
8
|
+
t.column :user_id, :integer
|
9
|
+
t.column :user, :json
|
10
|
+
t.column :comment, :string
|
11
|
+
t.column :recorded_at, :datetime
|
12
|
+
end
|
13
|
+
|
14
|
+
add_index :audits, [:type, :item_id], :name => 'item_index'
|
15
|
+
add_index :audits, :user_id, :name => 'user_index'
|
16
|
+
add_index :audits, :recorded_at
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.down
|
20
|
+
drop_table :audits
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active-audit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tariq Abughofa
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails-observers
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.1.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.1.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: virtus
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
description: ORM extension to track model changes. It also support keeping record
|
42
|
+
of who made the changes and why.
|
43
|
+
email: mhdtariqabughofa@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- lib/active-audit.rb
|
49
|
+
- lib/active_audit/audit.rb
|
50
|
+
- lib/active_audit/audit_pusher.rb
|
51
|
+
- lib/active_audit/audit_repository.rb
|
52
|
+
- lib/active_audit/base.rb
|
53
|
+
- lib/active_audit/dirty_association.rb
|
54
|
+
- lib/active_audit/errors.rb
|
55
|
+
- lib/active_audit/storage_adapters.rb
|
56
|
+
- lib/active_audit/storage_adapters/active_record_adapter.rb
|
57
|
+
- lib/active_audit/storage_adapters/elasticsearch_adapter.rb
|
58
|
+
- lib/active_audit/storage_adapters/mongo_adapter.rb
|
59
|
+
- lib/active_audit/storage_adapters/test_adapter.rb
|
60
|
+
- lib/active_audit/sweeper.rb
|
61
|
+
- lib/generators/active_audit/install_generator.rb
|
62
|
+
- lib/generators/active_audit/templates/adapter_config/elastic_search_config.yml
|
63
|
+
- lib/generators/active_audit/templates/adapter_config/mongo_config.yml
|
64
|
+
- lib/generators/active_audit/templates/initializer.rb
|
65
|
+
- lib/generators/active_audit/templates/migration.rb
|
66
|
+
- lib/generators/active_audit/templates/mongo_config.yml
|
67
|
+
homepage: http://rubygems.org/gems/active-audit
|
68
|
+
licenses:
|
69
|
+
- MIT
|
70
|
+
metadata: {}
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 2.4.8
|
88
|
+
signing_key:
|
89
|
+
specification_version: 4
|
90
|
+
summary: ORM extension to track model changes.
|
91
|
+
test_files: []
|