startback 0.4.5 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/startback/audit/trailer.rb +145 -0
- data/lib/startback/audit.rb +1 -0
- data/lib/startback/bus/bunny/async.rb +117 -0
- data/lib/startback/bus/bunny.rb +1 -0
- data/lib/startback/bus/memory/async.rb +40 -0
- data/lib/startback/bus/memory/sync.rb +30 -0
- data/lib/startback/bus/memory.rb +2 -0
- data/lib/startback/bus.rb +94 -0
- data/lib/startback/caching/entity_cache.rb +80 -0
- data/lib/startback/caching/store.rb +34 -0
- data/lib/startback/context/middleware.rb +1 -1
- data/lib/startback/context.rb +93 -4
- data/lib/startback/event.rb +43 -0
- data/lib/startback/operation.rb +39 -6
- data/lib/startback/support/fake_logger.rb +18 -0
- data/lib/startback/support/hooks.rb +48 -0
- data/lib/startback/support/operation_runner.rb +150 -0
- data/lib/startback/support/robustness.rb +153 -0
- data/lib/startback/support.rb +3 -0
- data/lib/startback/version.rb +2 -2
- data/lib/startback/web/api.rb +3 -4
- data/lib/startback/web/catch_all.rb +12 -5
- data/lib/startback/web/middleware.rb +13 -0
- data/lib/startback.rb +2 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/audit/test_trailer.rb +88 -0
- data/spec/unit/bus/memory/test_async.rb +41 -0
- data/spec/unit/bus/memory/test_sync.rb +41 -0
- data/spec/unit/caching/test_entity_cache.rb +109 -0
- data/spec/unit/context/test_abstraction_factory.rb +64 -0
- data/spec/unit/support/hooks/test_after_hook.rb +54 -0
- data/spec/unit/support/hooks/test_before_hook.rb +54 -0
- data/spec/unit/support/operation_runner/test_around_run.rb +157 -0
- data/spec/unit/support/operation_runner/test_before_after_call.rb +48 -0
- data/spec/unit/support/test_robusteness.rb +209 -0
- data/spec/unit/test_context.rb +51 -0
- data/spec/unit/test_event.rb +69 -0
- data/spec/unit/test_operation.rb +0 -3
- metadata +32 -4
@@ -0,0 +1,43 @@
|
|
1
|
+
module Startback
|
2
|
+
#
|
3
|
+
# An Event occuring a given context and having a type and attached data.
|
4
|
+
#
|
5
|
+
# Event instances have String types that are by default unrelated to ruby
|
6
|
+
# classes. Also, this Event class has a `json` information contract that
|
7
|
+
# allows dumping & reloading them easily. A context or context_factory may
|
8
|
+
# be provided in dress world to reload the event context from data, but
|
9
|
+
# that logic is opaque to this class.
|
10
|
+
#
|
11
|
+
# This class is intended to be subclassed if a more specific event protocol
|
12
|
+
# is wanted.
|
13
|
+
#
|
14
|
+
class Event
|
15
|
+
|
16
|
+
def initialize(type, data, context = nil)
|
17
|
+
@type = type.to_s
|
18
|
+
@data = OpenStruct.new(data)
|
19
|
+
@context = context
|
20
|
+
end
|
21
|
+
attr_reader :context, :type, :data
|
22
|
+
|
23
|
+
def self.json(src, world = {})
|
24
|
+
parsed = JSON.parse(src)
|
25
|
+
context = if world[:context]
|
26
|
+
world[:context]
|
27
|
+
elsif world[:context_factory]
|
28
|
+
world[:context_factory].call(parsed)
|
29
|
+
end
|
30
|
+
Event.new(parsed['type'], parsed['data'], context)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_json(*args, &bl)
|
34
|
+
h = {
|
35
|
+
type: self.type,
|
36
|
+
data: data.to_h
|
37
|
+
}
|
38
|
+
h[:context] = context if context
|
39
|
+
h.to_json(*args, &bl)
|
40
|
+
end
|
41
|
+
|
42
|
+
end # class Event
|
43
|
+
end # module Startback
|
data/lib/startback/operation.rb
CHANGED
@@ -1,16 +1,45 @@
|
|
1
1
|
module Startback
|
2
|
+
#
|
3
|
+
# High-level Operation abstraction, that is a piece of code that executes
|
4
|
+
# on demand and (generally) changes the state of the software system.
|
5
|
+
#
|
6
|
+
# An operation is basically an object that respond to `call`, but that
|
7
|
+
# executes within a given world (see `bind`). It also has before and
|
8
|
+
# after hooks that allows specifying what needs to be done before invoking
|
9
|
+
# call and after having invoked it. All this protocol is actually under
|
10
|
+
# the responsibility of an `OperationRunner`. Operations should not be
|
11
|
+
# called manually by third-party code.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# class SayHello < Startback::Operation
|
16
|
+
#
|
17
|
+
# before_call do
|
18
|
+
# # e.g. check_some_permissions
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# def call
|
22
|
+
# puts "Hello"
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# after_call do
|
26
|
+
# # e.g. log and/or emit something on a bus
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# end
|
30
|
+
#
|
2
31
|
class Operation
|
3
32
|
include Errors
|
33
|
+
include Support::OperationRunner
|
34
|
+
include Support::Hooks.new(:call)
|
4
35
|
|
5
36
|
attr_accessor :world
|
6
|
-
|
7
37
|
protected :world=
|
8
38
|
|
9
39
|
def bind(world)
|
10
40
|
return self unless world
|
11
|
-
|
12
|
-
|
13
|
-
}
|
41
|
+
self.world = world
|
42
|
+
self
|
14
43
|
end
|
15
44
|
|
16
45
|
def method_missing(name, *args, &bl)
|
@@ -19,10 +48,14 @@ module Startback
|
|
19
48
|
world.fetch(name){ super }
|
20
49
|
end
|
21
50
|
|
51
|
+
def respond_to?(name, *args)
|
52
|
+
super || (world && world.has_key?(name))
|
53
|
+
end
|
54
|
+
|
22
55
|
protected
|
23
56
|
|
24
|
-
def
|
25
|
-
|
57
|
+
def operation_world(op)
|
58
|
+
self.world
|
26
59
|
end
|
27
60
|
|
28
61
|
end # class Operation
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Startback
|
2
|
+
module Support
|
3
|
+
class FakeLogger < Logger
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
@last_msg = nil
|
7
|
+
end
|
8
|
+
attr_reader :last_msg
|
9
|
+
|
10
|
+
[:debug, :info, :warn, :error, :fatal].each do |meth|
|
11
|
+
define_method(meth) do |msg|
|
12
|
+
@last_msg = msg
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end # class Logger
|
17
|
+
end # module Support
|
18
|
+
end # module Startback
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Startback
|
2
|
+
module Support
|
3
|
+
class Hooks < Module
|
4
|
+
|
5
|
+
def initialize(suffix)
|
6
|
+
@suffix = suffix
|
7
|
+
define_method :"before_#{suffix}" do
|
8
|
+
self.class.__befores.each do |bl|
|
9
|
+
instance_exec(&bl)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
define_method :"after_#{suffix}" do
|
13
|
+
self.class.__afters.each do |bl|
|
14
|
+
instance_exec(&bl)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
attr_reader :suffix
|
19
|
+
|
20
|
+
def included(by)
|
21
|
+
by.instance_eval %Q{
|
22
|
+
def __befores(create = false)
|
23
|
+
if create
|
24
|
+
@__befores ||= (superclass.respond_to?(:__befores, false) ? superclass.__befores.dup : [])
|
25
|
+
end
|
26
|
+
@__befores || (superclass.respond_to?(:__befores, false) ? superclass.__befores : [])
|
27
|
+
end
|
28
|
+
|
29
|
+
def __afters(create = false)
|
30
|
+
if create
|
31
|
+
@__afters ||= (superclass.respond_to?(:__afters, false) ? superclass.__afters.dup : [])
|
32
|
+
end
|
33
|
+
@__afters || (superclass.respond_to?(:__afters, false) ? superclass.__afters : [])
|
34
|
+
end
|
35
|
+
|
36
|
+
def before_#{suffix}(&bl)
|
37
|
+
__befores(true) << bl
|
38
|
+
end
|
39
|
+
|
40
|
+
def after_#{suffix}(&bl)
|
41
|
+
__afters(true) << bl
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
end # class Hooks
|
47
|
+
end # module Support
|
48
|
+
end # module Startback
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module Startback
|
2
|
+
module Support
|
3
|
+
#
|
4
|
+
# Support module for high-level architectural components that
|
5
|
+
# execute operations as part of their logic, see e.g. Web::Api.
|
6
|
+
#
|
7
|
+
# This module contributes a `run` instance method that allows
|
8
|
+
# binding an operation with a world, and executing it while
|
9
|
+
# supporting around runners.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# class HighLevelComponent
|
14
|
+
# include Startback::Support::OperationRunner
|
15
|
+
#
|
16
|
+
# def some_method
|
17
|
+
# # Runs the operation passed after some binding
|
18
|
+
# run SomeOperation.new
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# protected
|
22
|
+
#
|
23
|
+
# # Overriden to inject some extra world
|
24
|
+
# def operation_world(op)
|
25
|
+
# super(op).merge({ hello: "world" })
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# # Execute this around op
|
29
|
+
# around_run do |op, then_block|
|
30
|
+
# puts "About to run #{op.inspect}"
|
31
|
+
# then_block.call
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# # SomeClass#call will be called with the operation
|
35
|
+
# # as first parameter and a block as continuation
|
36
|
+
# around_run SomeClass.new
|
37
|
+
#
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
module OperationRunner
|
41
|
+
|
42
|
+
# Contributes the hook DSL methods to classes that include
|
43
|
+
# the OperationRunner module
|
44
|
+
module ClassMethods
|
45
|
+
|
46
|
+
# Registers a callable to be executed around operation running.
|
47
|
+
#
|
48
|
+
# In its block form, the callable is `instance_exec`uted on the
|
49
|
+
# runner instance, with the operation passed as first parameter
|
50
|
+
# and a then_block callable as second parameter (continuation):
|
51
|
+
#
|
52
|
+
# around_run do |op,then_block|
|
53
|
+
# # do whatever you want with the op (already bounded)
|
54
|
+
# puts op.inspect
|
55
|
+
#
|
56
|
+
# # do not forget to call the continuation block
|
57
|
+
# then_block.call
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# With a parameter responding to `#call`, the latter is invoked
|
61
|
+
# with the operation as parameter and a block as continuation:
|
62
|
+
#
|
63
|
+
# class Arounder
|
64
|
+
#
|
65
|
+
# def call(op)
|
66
|
+
# # do whatever you want with the op (already bounded)
|
67
|
+
# puts op.inspect
|
68
|
+
#
|
69
|
+
# # do not forget to call the continuation block
|
70
|
+
# yield
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
def around_run(arounder = nil, &bl)
|
76
|
+
raise ArgumentError, "Arg or block required" unless arounder || bl
|
77
|
+
arounds(true) << [arounder || bl, arounder.nil?]
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def arounds(create = false)
|
83
|
+
if create
|
84
|
+
@arounds ||= superclass.respond_to?(:arounds, true) \
|
85
|
+
? superclass.send(:arounds, true).dup \
|
86
|
+
: []
|
87
|
+
end
|
88
|
+
@arounds || (superclass.respond_to?(:arounds, true) ? superclass.send(:arounds, true) : [])
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
# When included by a class/module, install the DSL methods
|
94
|
+
def self.included(by)
|
95
|
+
by.extend(ClassMethods)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Runs `operation`, taking care of binding it and executing
|
99
|
+
# hooks.
|
100
|
+
#
|
101
|
+
# This method is NOT intended to be overriden. Use hooks and
|
102
|
+
# `operation_world` to impact default behavior.
|
103
|
+
def run(operation)
|
104
|
+
op_world = operation_world(operation)
|
105
|
+
op_bound = operation.bind(op_world)
|
106
|
+
_run_befores(op_bound)
|
107
|
+
r = _run_with_arounds(op_bound, self.class.send(:arounds))
|
108
|
+
_run_afters(op_bound)
|
109
|
+
r
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
|
114
|
+
# Returns the world to use to bind an operation.
|
115
|
+
#
|
116
|
+
# The default implementation returns an empty hash. This is
|
117
|
+
# intended to be overriden by classes including this module.
|
118
|
+
def operation_world(op)
|
119
|
+
{}
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def _run_befores(op_bound)
|
125
|
+
op_bound.before_call if op_bound.respond_to?(:before_call, true)
|
126
|
+
end
|
127
|
+
|
128
|
+
def _run_with_arounds(operation, arounds = [])
|
129
|
+
if arounds.empty?
|
130
|
+
operation.call
|
131
|
+
else
|
132
|
+
arounder, iexec = arounds.first
|
133
|
+
after_first = ->() {
|
134
|
+
_run_with_arounds(operation, arounds[1..-1])
|
135
|
+
}
|
136
|
+
if iexec
|
137
|
+
self.instance_exec(operation, after_first, &arounder)
|
138
|
+
else
|
139
|
+
arounder.call(self, operation, &after_first)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def _run_afters(op_bound)
|
145
|
+
op_bound.after_call if op_bound.respond_to?(:after_call, true)
|
146
|
+
end
|
147
|
+
|
148
|
+
end # module OperationRunner
|
149
|
+
end # module Support
|
150
|
+
end # module Startback
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module Startback
|
2
|
+
module Support
|
3
|
+
#
|
4
|
+
# This module provides helper methods for robustness of a software design.
|
5
|
+
#
|
6
|
+
# It is included by main Startback abstractions, and can be included by
|
7
|
+
# specific software components who needs fine-tuning of monitoring, logging
|
8
|
+
# and error handling.
|
9
|
+
#
|
10
|
+
# All public methods here follow the following free args parameters:
|
11
|
+
#
|
12
|
+
# 1. First (& second) argument(s) form the log message.
|
13
|
+
#
|
14
|
+
# A full log message is a Hash having :op (required), :op_took (optional),
|
15
|
+
# and :op_data (optional) keys.
|
16
|
+
#
|
17
|
+
# If a String (or two) are used instead, a log message will be built taking
|
18
|
+
# the former as the executer (a class or instance) and the second as a method.
|
19
|
+
# `{ op: "executer#method" }`
|
20
|
+
#
|
21
|
+
# 2. The second (or third) argument should be a Logger instance, a Context,
|
22
|
+
# or an instance knowing its context. The best logger is extracted from it
|
23
|
+
# and used for actual logging.
|
24
|
+
#
|
25
|
+
# Examples:
|
26
|
+
#
|
27
|
+
# log(op: "hello", op_data: {foo: 12}) => logged as such on STDOUT
|
28
|
+
# log("A simple message") => { op: "A simple message" } on STDOUT
|
29
|
+
# log(Startback, "hello") => { op: "Startback#hello" } on STDOUT
|
30
|
+
# log(Event.new, "hello") => { op: "Event#hello" } on STDOUT
|
31
|
+
# log(self, context) => { op: "..." } on context's logger or STDOUT
|
32
|
+
# log(self, event) => { op: "..." } on event context's logger or STDOUT
|
33
|
+
# ...
|
34
|
+
#
|
35
|
+
module Robustness
|
36
|
+
|
37
|
+
# Included to avoid poluting the space of the including
|
38
|
+
# classes.
|
39
|
+
module Tools
|
40
|
+
|
41
|
+
def default_logger
|
42
|
+
@@default_logger ||= ::Logger.new(STDOUT)
|
43
|
+
from = caller.reject{|x| x =~ /lib\/startback/ }.first
|
44
|
+
@@default_logger.debug("Watch out, using default logger from: #{from}")
|
45
|
+
@@default_logger
|
46
|
+
end
|
47
|
+
module_function :default_logger
|
48
|
+
|
49
|
+
def logger_for(arg)
|
50
|
+
return arg if arg.is_a?(::Logger)
|
51
|
+
return arg.logger if arg.is_a?(Context) && arg.logger
|
52
|
+
return logger_for(arg.context) if arg.respond_to?(:context, false)
|
53
|
+
default_logger
|
54
|
+
end
|
55
|
+
module_function :logger_for
|
56
|
+
|
57
|
+
def parse_args(log_msg, method = nil, context = nil, extra = nil)
|
58
|
+
method, context, extra = nil, method, context unless method.is_a?(String)
|
59
|
+
context, extra = nil, context if context.is_a?(Hash) && extra.nil?
|
60
|
+
logger = logger_for(context) || logger_for(log_msg)
|
61
|
+
log_msg = if log_msg.is_a?(Hash)
|
62
|
+
log_msg.dup
|
63
|
+
elsif log_msg.is_a?(String)
|
64
|
+
log_msg = { op: "#{log_msg}#{method.nil? ? '' : '#'+method.to_s}" }
|
65
|
+
else
|
66
|
+
log_msg = log_msg.class unless log_msg.is_a?(Module)
|
67
|
+
log_msg = { op: "#{log_msg.name}##{method}" }
|
68
|
+
end
|
69
|
+
log_msg.merge!(extra) if extra
|
70
|
+
[ log_msg, logger ]
|
71
|
+
end
|
72
|
+
module_function :parse_args
|
73
|
+
|
74
|
+
[:debug, :info, :warn, :error, :fatal].each do |meth|
|
75
|
+
define_method(meth) do |args, extra = nil, &bl|
|
76
|
+
act_args = args + [extra]
|
77
|
+
log_msg, logger = parse_args(*act_args)
|
78
|
+
logger.send(meth, log_msg)
|
79
|
+
end
|
80
|
+
module_function(meth)
|
81
|
+
end
|
82
|
+
|
83
|
+
end # module Tools
|
84
|
+
|
85
|
+
# Logs a specific message with a given severity.
|
86
|
+
#
|
87
|
+
# Severity can be :debug, :info, :warn, :error or :fatal.
|
88
|
+
# The args must follow module's conventions, see above.
|
89
|
+
def log(severity, *args)
|
90
|
+
Tools.send(severity, args)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Calls the block and monitors then log its execution time.
|
94
|
+
#
|
95
|
+
# The args must follow module's conventions, see above.
|
96
|
+
def monitor(*args, &bl)
|
97
|
+
result = nil
|
98
|
+
took = Benchmark.realtime {
|
99
|
+
result = bl.call
|
100
|
+
}
|
101
|
+
Tools.debug(args, op_took: took)
|
102
|
+
result
|
103
|
+
rescue => ex
|
104
|
+
Tools.error(args, error: ex)
|
105
|
+
raise
|
106
|
+
end
|
107
|
+
|
108
|
+
# Executes the block without letting errors propagate.
|
109
|
+
# Errors are logged, though. Nothing is logged if everything
|
110
|
+
# goes fine.
|
111
|
+
#
|
112
|
+
# The args must follow module's conventions, see above.
|
113
|
+
def stop_errors(*args, &bl)
|
114
|
+
result = nil
|
115
|
+
took = Benchmark.realtime {
|
116
|
+
result = bl.call
|
117
|
+
}
|
118
|
+
result
|
119
|
+
rescue => ex
|
120
|
+
Tools.error(args, op_took: took, error: ex)
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
# Tries executing the block up to `n` times, until an attempt
|
125
|
+
# succeeds (then returning the result). Logs the first and last
|
126
|
+
# fatal error, if any.
|
127
|
+
#
|
128
|
+
# The args must follow module's conventions, see above.
|
129
|
+
def try_max_times(n, *args, &bl)
|
130
|
+
retried = 0
|
131
|
+
took = 0
|
132
|
+
begin
|
133
|
+
result = nil
|
134
|
+
took += Benchmark.realtime {
|
135
|
+
result = bl.call
|
136
|
+
}
|
137
|
+
result
|
138
|
+
rescue => ex
|
139
|
+
Tools.error(args + [{op_took: took, error: ex}]) if retried == 0
|
140
|
+
retried += 1
|
141
|
+
if retried < n
|
142
|
+
sleep(retried)
|
143
|
+
retry
|
144
|
+
else
|
145
|
+
Tools.fatal(args + [{op_took: took, error: ex}])
|
146
|
+
raise
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end # module Robustness
|
152
|
+
end # module Support
|
153
|
+
end # module Startback
|
data/lib/startback/support.rb
CHANGED
data/lib/startback/version.rb
CHANGED
data/lib/startback/web/api.rb
CHANGED
@@ -20,11 +20,10 @@ module Startback
|
|
20
20
|
###
|
21
21
|
### Facade over third party tools
|
22
22
|
###
|
23
|
+
include Support::OperationRunner
|
23
24
|
|
24
|
-
def
|
25
|
-
|
26
|
-
.bind({ context: context })
|
27
|
-
.call
|
25
|
+
def operation_world(op)
|
26
|
+
{ context: context }
|
28
27
|
end
|
29
28
|
|
30
29
|
###
|
@@ -19,6 +19,7 @@ module Startback
|
|
19
19
|
#
|
20
20
|
class CatchAll < Rack::Robustness
|
21
21
|
include Errors
|
22
|
+
include Support::Robustness
|
22
23
|
|
23
24
|
FATAL_ERROR = {
|
24
25
|
code: "Startback::Errors::InternalServerError",
|
@@ -32,12 +33,18 @@ module Startback
|
|
32
33
|
self.body FATAL_ERROR
|
33
34
|
|
34
35
|
self.ensure(true) do |ex|
|
36
|
+
STDERR.puts(ex.message)
|
35
37
|
context = env[Context::Middleware::RACK_ENV_KEY]
|
36
|
-
|
37
|
-
context.error_handler.
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
begin
|
39
|
+
if context && context.respond_to?(:error_handler, true) && context.error_handler
|
40
|
+
context.error_handler.fatal(ex)
|
41
|
+
else
|
42
|
+
log(:fatal, self, "ensure", error: ex)
|
43
|
+
end
|
44
|
+
rescue => ex2
|
45
|
+
STDERR.puts(ex2.message)
|
46
|
+
STDERR.puts(ex2.backtrace[0..10].join("\n"))
|
47
|
+
raise
|
41
48
|
end
|
42
49
|
end
|
43
50
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Startback
|
2
|
+
module Web
|
3
|
+
module Middleware
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def context(env = @env)
|
8
|
+
::Startback::Context::Middleware.context(env) || Errors.server_error!("Unable to find context!!")
|
9
|
+
end
|
10
|
+
|
11
|
+
end # module Middleware
|
12
|
+
end # module Web
|
13
|
+
end # module Startback
|
data/lib/startback.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'startback/audit'
|
3
|
+
module Startback
|
4
|
+
module Audit
|
5
|
+
describe Trailer do
|
6
|
+
|
7
|
+
let(:trailer) {
|
8
|
+
Trailer.new("/tmp/trail.log")
|
9
|
+
}
|
10
|
+
|
11
|
+
describe "op_data" do
|
12
|
+
|
13
|
+
def op_data(op, trailer = self.trailer)
|
14
|
+
trailer.send(:op_data, op)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'uses to_trail in priority if provided' do
|
18
|
+
op = OpenStruct.new(to_trail: { foo: "bar" }, input: 12, request: 13)
|
19
|
+
expect(op_data(op)).to eql({ foo: "bar" })
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'uses input then' do
|
23
|
+
op = OpenStruct.new(input: { foo: "bar" }, request: 13)
|
24
|
+
expect(op_data(op)).to eql({ foo: "bar" })
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'uses request then' do
|
28
|
+
op = OpenStruct.new(request: { foo: "bar" })
|
29
|
+
expect(op_data(op)).to eql({ foo: "bar" })
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'applies default blacklists for security reasons' do
|
33
|
+
op = OpenStruct.new(input: {
|
34
|
+
token: "will not be dumped",
|
35
|
+
a_token: "will not be dumped",
|
36
|
+
AToken: "will not be dumped",
|
37
|
+
password: "will not be dumped",
|
38
|
+
secret: "will not be dumped",
|
39
|
+
credentials: "will not be dumped",
|
40
|
+
foo: "bar"
|
41
|
+
})
|
42
|
+
expect(op_data(op)).to eql({
|
43
|
+
foo: "bar"
|
44
|
+
})
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'applies default blacklists to data arrays too' do
|
48
|
+
op = OpenStruct.new(input: [{
|
49
|
+
token: "will not be dumped",
|
50
|
+
a_token: "will not be dumped",
|
51
|
+
AToken: "will not be dumped",
|
52
|
+
password: "will not be dumped",
|
53
|
+
secret: "will not be dumped",
|
54
|
+
credentials: "will not be dumped",
|
55
|
+
foo: "bar"
|
56
|
+
}])
|
57
|
+
expect(op_data(op)).to eql([{
|
58
|
+
foo: "bar"
|
59
|
+
}])
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'uses the stop words provided at construction' do
|
63
|
+
t = Trailer.new("/tmp/trail.log", blacklist: "hello and world")
|
64
|
+
op = OpenStruct.new(request: { Hello: "bar", World: "foo", foo: "bar" })
|
65
|
+
expect(op_data(op, t)).to eql({ foo: "bar" })
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "op_context" do
|
71
|
+
|
72
|
+
def op_context(op, trailer = self.trailer)
|
73
|
+
trailer.send(:op_context, op)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'applies default blacklists for security reasons' do
|
77
|
+
op = OpenStruct.new(context: {
|
78
|
+
token: "will not be dumped",
|
79
|
+
foo: "bar"
|
80
|
+
})
|
81
|
+
expect(op_context(op)).to eql({ foo: "bar" })
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|