-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathtransition_auditing.rb
86 lines (74 loc) · 2.79 KB
/
transition_auditing.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#
# This module inserts hooks into the state machine.
# The transition_class is the class (optionally specified with the :class option) for the model which
# records audit information from one single transition i.e. SubscriptionStateTransition.
#
# Multiple `SubscriptionStateTransition`s compose the 'audit_trail'.
#
module StateMachines::AuditTrail::TransitionAuditing
class InitialTransition
attr_reader :namespace, :to
def initialize(namespace:, to:)
@namespace = namespace
@to = to
end
def from
nil
end
def event
nil
end
def machine
nil
end
def args
nil
end
end
# Hook for audit_trail inside a a state_machine declaration.
#
# options:
# - :class - custom state transition class
# - :owner_class - the class which is to own the persisted transition objects
# - :context - methods to call/store in field of same name in the state transition class
# - :initial - if false, won't log null => initial state transition upon instantiation
#
def audit_trail(options = {})
state_machine = self
if options[:class].presence
raise ":class option[#{options[:class]}] must be a class (not a string)." unless options[:class].is_a? Class
end
transition_class = options[:class] || default_transition_class
owner_class = options[:owner_class] || self.owner_class
# backend implements #log to store transition information
@backend = StateMachines::AuditTrail::Backend.create_for(transition_class, owner_class, options.slice(:context, :as))
# Initial state logging can be turned off. Very useful for a model with multiple state_machines using a single TransitionState object for logging
unless options[:initial] == false
unless state_machine.action == nil
# Log the initial transition from null => initial (upon object instantiation)
state_machine.owner_class.after_initialize do |object|
if state_machine.backend.new_record? object
current_state = object.send(state_machine.attribute)
if !current_state.nil?
state_machine.backend.log(object, InitialTransition.new(namespace: state_machine.namespace, to: current_state))
end
end
end
end
end
# Log any transition (other than initial)
state_machine.after_transition do |object, transition|
state_machine.backend.log(object, transition)
end
end
# Public returns an instance of the class which does the actual audit trail logging
def backend
@backend
end
private
def default_transition_class
owner_class_or_base_class = owner_class.respond_to?(:base_class) ? owner_class.base_class : owner_class
name = "#{owner_class_or_base_class.name}#{attribute.to_s.camelize}Transition"
name.constantize
end
end