active-audit 0.2.0
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.
- 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: []
|