startback-websocket 0.14.0 → 0.14.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -2
- data/README.md +64 -9
- data/dist/client.js +1 -0
- data/lib/startback/ext/context.rb +6 -0
- data/lib/startback/ext.rb +1 -2
- data/lib/startback/websocket/app.rb +82 -0
- data/lib/startback/websocket/hub/app.rb +28 -0
- data/lib/startback/websocket/hub/builder.rb +55 -0
- data/lib/startback/websocket/hub/errors.rb +9 -0
- data/lib/startback/websocket/hub/message.rb +29 -0
- data/lib/startback/websocket/hub/middleware/command_handler.rb +34 -0
- data/lib/startback/websocket/hub/middleware/room_handler.rb +30 -0
- data/lib/startback/websocket/hub/middleware.rb +12 -0
- data/lib/startback/websocket/hub/participant.rb +16 -0
- data/lib/startback/websocket/hub/room.rb +46 -0
- data/lib/startback/websocket/hub.rb +15 -0
- data/lib/startback/websocket.rb +8 -0
- data/spec/spec_helper.rb +21 -32
- data/spec/unit/hub/test_builder.rb +141 -0
- data/spec/unit/hub/test_room.rb +27 -0
- data/spec/unit/test_app.rb +35 -0
- data/tasks/test.rake +0 -1
- metadata +21 -91
- data/lib/startback/audit/prometheus.rb +0 -87
- data/lib/startback/audit/shared.rb +0 -17
- data/lib/startback/audit/trailer.rb +0 -129
- data/lib/startback/audit.rb +0 -3
- data/lib/startback/caching/entity_cache.rb +0 -157
- data/lib/startback/caching/no_store.rb +0 -28
- data/lib/startback/caching/store.rb +0 -34
- data/lib/startback/context/h_factory.rb +0 -43
- data/lib/startback/context/middleware.rb +0 -53
- data/lib/startback/context.rb +0 -122
- data/lib/startback/errors.rb +0 -197
- data/lib/startback/event/agent.rb +0 -84
- data/lib/startback/event/bus/bunny/async.rb +0 -162
- data/lib/startback/event/bus/bunny.rb +0 -1
- data/lib/startback/event/bus/memory/async.rb +0 -45
- data/lib/startback/event/bus/memory/sync.rb +0 -35
- data/lib/startback/event/bus/memory.rb +0 -2
- data/lib/startback/event/bus.rb +0 -100
- data/lib/startback/event/engine.rb +0 -94
- data/lib/startback/event/ext/context.rb +0 -5
- data/lib/startback/event/ext/operation.rb +0 -13
- data/lib/startback/event.rb +0 -47
- data/lib/startback/ext/date_time.rb +0 -9
- data/lib/startback/ext/time.rb +0 -9
- data/lib/startback/model.rb +0 -6
- data/lib/startback/operation/error_operation.rb +0 -19
- data/lib/startback/operation/multi_operation.rb +0 -28
- data/lib/startback/operation.rb +0 -78
- data/lib/startback/services.rb +0 -11
- data/lib/startback/support/data_object.rb +0 -71
- data/lib/startback/support/env.rb +0 -41
- data/lib/startback/support/fake_logger.rb +0 -18
- data/lib/startback/support/hooks.rb +0 -48
- data/lib/startback/support/log_formatter.rb +0 -34
- data/lib/startback/support/logger.rb +0 -34
- data/lib/startback/support/operation_runner.rb +0 -150
- data/lib/startback/support/robustness.rb +0 -157
- data/lib/startback/support/transaction_manager.rb +0 -25
- data/lib/startback/support/transaction_policy.rb +0 -33
- data/lib/startback/support/world.rb +0 -54
- data/lib/startback/support.rb +0 -26
- data/lib/startback/version.rb +0 -8
- data/lib/startback/web/api.rb +0 -99
- data/lib/startback/web/auto_caching.rb +0 -85
- data/lib/startback/web/catch_all.rb +0 -52
- data/lib/startback/web/cors_headers.rb +0 -80
- data/lib/startback/web/health_check.rb +0 -49
- data/lib/startback/web/magic_assets/ng_html_transformer.rb +0 -80
- data/lib/startback/web/magic_assets/rake_tasks.rb +0 -64
- data/lib/startback/web/magic_assets.rb +0 -98
- data/lib/startback/web/middleware.rb +0 -13
- data/lib/startback/web/prometheus.rb +0 -16
- data/lib/startback/web/shield.rb +0 -58
- data/lib/startback.rb +0 -43
- data/spec/unit/audit/test_prometheus.rb +0 -72
- data/spec/unit/audit/test_trailer.rb +0 -105
- data/spec/unit/caching/test_entity_cache.rb +0 -136
- data/spec/unit/context/test_abstraction_factory.rb +0 -64
- data/spec/unit/context/test_dup.rb +0 -42
- data/spec/unit/context/test_fork.rb +0 -37
- data/spec/unit/context/test_h_factory.rb +0 -31
- data/spec/unit/context/test_middleware.rb +0 -45
- data/spec/unit/context/test_with_world.rb +0 -20
- data/spec/unit/context/test_world.rb +0 -17
- data/spec/unit/event/bus/memory/test_async.rb +0 -43
- data/spec/unit/event/bus/memory/test_sync.rb +0 -43
- data/spec/unit/support/hooks/test_after_hook.rb +0 -54
- data/spec/unit/support/hooks/test_before_hook.rb +0 -54
- data/spec/unit/support/operation_runner/test_around_run.rb +0 -156
- data/spec/unit/support/operation_runner/test_before_after_call.rb +0 -48
- data/spec/unit/support/test_data_object.rb +0 -156
- data/spec/unit/support/test_env.rb +0 -75
- data/spec/unit/support/test_robusteness.rb +0 -229
- data/spec/unit/support/test_transaction_manager.rb +0 -64
- data/spec/unit/support/test_world.rb +0 -72
- data/spec/unit/test_event.rb +0 -62
- data/spec/unit/test_operation.rb +0 -55
- data/spec/unit/test_support.rb +0 -40
- data/spec/unit/web/fixtures/assets/app/hello.es6 +0 -4
- data/spec/unit/web/fixtures/assets/app/hello.html +0 -1
- data/spec/unit/web/fixtures/assets/index.es6 +0 -1
- data/spec/unit/web/test_api.rb +0 -82
- data/spec/unit/web/test_auto_caching.rb +0 -81
- data/spec/unit/web/test_catch_all.rb +0 -77
- data/spec/unit/web/test_cors_headers.rb +0 -88
- data/spec/unit/web/test_healthcheck.rb +0 -59
- data/spec/unit/web/test_magic_assets.rb +0 -82
data/lib/startback/operation.rb
DELETED
@@ -1,78 +0,0 @@
|
|
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
|
-
#
|
31
|
-
class Operation
|
32
|
-
extend Support::TransactionPolicy
|
33
|
-
include Errors
|
34
|
-
include Support::OperationRunner
|
35
|
-
include Support::Hooks.new(:call)
|
36
|
-
|
37
|
-
attr_accessor :world
|
38
|
-
protected :world=
|
39
|
-
|
40
|
-
def initialize(input = {})
|
41
|
-
@input = input
|
42
|
-
end
|
43
|
-
attr_reader :input
|
44
|
-
|
45
|
-
def bind(world)
|
46
|
-
return self unless world
|
47
|
-
self.world = world
|
48
|
-
self
|
49
|
-
end
|
50
|
-
|
51
|
-
def method_missing(name, *args, &bl)
|
52
|
-
return super unless args.empty? and bl.nil?
|
53
|
-
return super unless world
|
54
|
-
world.fetch(name){ super }
|
55
|
-
end
|
56
|
-
|
57
|
-
def respond_to?(name, *args)
|
58
|
-
super || (world && world.has_key?(name))
|
59
|
-
end
|
60
|
-
|
61
|
-
def with_context(ctx = nil)
|
62
|
-
old_world = self.world
|
63
|
-
self.world = self.world.merge(context: ctx || old_world.context.dup)
|
64
|
-
result = ctx ? yield : yield(self.world.context)
|
65
|
-
self.world = old_world
|
66
|
-
result
|
67
|
-
end
|
68
|
-
|
69
|
-
protected
|
70
|
-
|
71
|
-
def operation_world(op)
|
72
|
-
self.world
|
73
|
-
end
|
74
|
-
|
75
|
-
end # class Operation
|
76
|
-
end # module Startback
|
77
|
-
require_relative 'operation/error_operation'
|
78
|
-
require_relative 'operation/multi_operation'
|
data/lib/startback/services.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
module Startback
|
2
|
-
module Support
|
3
|
-
module DataObject
|
4
|
-
|
5
|
-
def initialize(data = {})
|
6
|
-
@_data = data.dup.freeze
|
7
|
-
end
|
8
|
-
|
9
|
-
attr_writer :_data
|
10
|
-
protected :_data=
|
11
|
-
|
12
|
-
def method_missing(name, *args, &bl)
|
13
|
-
return super unless args.empty? && bl.nil?
|
14
|
-
return super unless pair = _data_key_for(name)
|
15
|
-
|
16
|
-
pair.last ? !!@_data[pair.first] : @_data[pair.first]
|
17
|
-
end
|
18
|
-
|
19
|
-
def [](name)
|
20
|
-
return nil unless pair = _data_key_for(name, false, false)
|
21
|
-
|
22
|
-
@_data[pair.first]
|
23
|
-
end
|
24
|
-
|
25
|
-
def respond_to?(name)
|
26
|
-
super || !_data_key_for(name).nil?
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_data
|
30
|
-
@_data
|
31
|
-
end
|
32
|
-
alias :to_h :to_data
|
33
|
-
|
34
|
-
def to_json(*args, &bl)
|
35
|
-
to_data.to_json(*args, &bl)
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def _data_key_for(key, try_camelize = _data_allow_camelize, try_query = _data_allow_query)
|
41
|
-
if @_data.key?(key)
|
42
|
-
[key, false]
|
43
|
-
elsif @_data.key?(key.to_s)
|
44
|
-
[key.to_s, false]
|
45
|
-
elsif key.is_a?(String) && @_data.key?(key.to_sym)
|
46
|
-
[key.to_sym, false]
|
47
|
-
elsif try_camelize
|
48
|
-
cam = key.to_s.gsub(/_([a-z])/){ $1.upcase }.to_sym
|
49
|
-
_data_key_for(cam, false, true)
|
50
|
-
elsif try_query && key.to_s =~ /\?$/
|
51
|
-
got = _data_key_for(key[0...-1].to_sym, false, false)
|
52
|
-
got ? [got.first, true] : _data_key_not_found(key)
|
53
|
-
else
|
54
|
-
_data_key_not_found(key)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def _data_allow_camelize
|
59
|
-
true
|
60
|
-
end
|
61
|
-
|
62
|
-
def _data_allow_query
|
63
|
-
true
|
64
|
-
end
|
65
|
-
|
66
|
-
def _data_key_not_found(key)
|
67
|
-
nil
|
68
|
-
end
|
69
|
-
end # module DataObject
|
70
|
-
end # module Support
|
71
|
-
end # module Startback
|
@@ -1,41 +0,0 @@
|
|
1
|
-
module Startback
|
2
|
-
module Support
|
3
|
-
# This method provides the `env` and `env!` methods that
|
4
|
-
# help querying environment variables easily.
|
5
|
-
module Env
|
6
|
-
|
7
|
-
# Returns an environment variable or raise an error if
|
8
|
-
# not set.
|
9
|
-
#
|
10
|
-
# The result is always a String with no leading/trailing
|
11
|
-
# spaces.
|
12
|
-
#
|
13
|
-
# If a block is given, the environment variable is yield
|
14
|
-
# and the result of the block returned.
|
15
|
-
def env!(key, default = nil, &bl)
|
16
|
-
v = ENV[key].to_s.strip
|
17
|
-
raise Startback::Error, "Missing ENV var `#{key}`" if v.empty?
|
18
|
-
|
19
|
-
env(key, default, &bl)
|
20
|
-
end
|
21
|
-
module_function :env!
|
22
|
-
|
23
|
-
# Returns an environment variable or the default value
|
24
|
-
# passed as second argument.
|
25
|
-
#
|
26
|
-
# The result is always a String with no leading/trailing
|
27
|
-
# spaces.
|
28
|
-
#
|
29
|
-
# If a block is given, the environment variable is yield
|
30
|
-
# and the result of the block returned.
|
31
|
-
def env(key, default = nil, &bl)
|
32
|
-
v = ENV[key].to_s.strip
|
33
|
-
v = v.empty? ? default : v
|
34
|
-
v = bl.call(v) if bl && v
|
35
|
-
v
|
36
|
-
end
|
37
|
-
module_function :env
|
38
|
-
|
39
|
-
end # module Env
|
40
|
-
end # module Support
|
41
|
-
end # module Startback
|
@@ -1,18 +0,0 @@
|
|
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
|
@@ -1,48 +0,0 @@
|
|
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
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module Startback
|
2
|
-
module Support
|
3
|
-
class LogFormatter
|
4
|
-
|
5
|
-
def call(severity, time, progname, msg)
|
6
|
-
msg = { message: msg } if msg.is_a?(String)
|
7
|
-
msg = { error: msg } if msg.is_a?(Exception)
|
8
|
-
{
|
9
|
-
severity: severity,
|
10
|
-
time: time
|
11
|
-
}.merge(msg)
|
12
|
-
.merge(error: error_to_json(msg[:error], severity))
|
13
|
-
.compact
|
14
|
-
.to_json << "\n"
|
15
|
-
end
|
16
|
-
|
17
|
-
def error_to_json(error, severity = nil)
|
18
|
-
return error if error.nil?
|
19
|
-
return error if error.is_a?(String)
|
20
|
-
return error.to_s unless error.is_a?(Exception)
|
21
|
-
|
22
|
-
backtrace = error.backtrace[0..25] if severity == "FATAL"
|
23
|
-
causes = error.causes.map{|c| error_to_json(c) } if error.respond_to?(:causes)
|
24
|
-
causes = nil if causes && causes.empty?
|
25
|
-
{
|
26
|
-
message: error.message,
|
27
|
-
backtrace: backtrace,
|
28
|
-
causes: causes
|
29
|
-
}.compact
|
30
|
-
end
|
31
|
-
|
32
|
-
end # class LogFormatter
|
33
|
-
end # module Support
|
34
|
-
end # module Startback
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module Startback
|
2
|
-
module Support
|
3
|
-
#
|
4
|
-
# A Logger extension that sends info and debug messages to STDOUT
|
5
|
-
# and other messages to STDERR. This is not configurable.
|
6
|
-
#
|
7
|
-
class Logger < ::Logger
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
super(STDOUT)
|
11
|
-
@err_logger = ::Logger.new(STDERR)
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.level=(level)
|
15
|
-
super.tap{
|
16
|
-
@err_logger.level = level
|
17
|
-
}
|
18
|
-
end
|
19
|
-
|
20
|
-
def warn(*args, &bl)
|
21
|
-
@err_logger.warn(*args, &bl)
|
22
|
-
end
|
23
|
-
|
24
|
-
def error(*args, &bl)
|
25
|
-
@err_logger.error(*args, &bl)
|
26
|
-
end
|
27
|
-
|
28
|
-
def fatal(*args, &bl)
|
29
|
-
@err_logger.fatal(*args, &bl)
|
30
|
-
end
|
31
|
-
|
32
|
-
end # class Logger
|
33
|
-
end # module Support
|
34
|
-
end # module Startback
|
@@ -1,150 +0,0 @@
|
|
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
|
@@ -1,157 +0,0 @@
|
|
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(:info, op: "hello", op_data: {foo: 12}) => logged as such on STDOUT
|
28
|
-
# log(:info, "A simple message") => { op: "A simple message" } on STDOUT
|
29
|
-
# log(:info, Startback, "hello") => { op: "Startback#hello" } on STDOUT
|
30
|
-
# log(:info, Event.new, "hello") => { op: "Event#hello" } on STDOUT
|
31
|
-
# log(:info, Event.new, "hello", "hello world") => { op: "Event#hello", op_data: { message: "hello world" } } on STDOUT
|
32
|
-
# log(:info, self, context) => { op: "..." } on context's logger or STDOUT
|
33
|
-
# log(:info, self, event) => { op: "..." } on event context's logger or STDOUT
|
34
|
-
# ...
|
35
|
-
#
|
36
|
-
module Robustness
|
37
|
-
|
38
|
-
# Included to avoid poluting the space of the including
|
39
|
-
# classes.
|
40
|
-
module Tools
|
41
|
-
|
42
|
-
def default_logger
|
43
|
-
@@default_logger ||= begin
|
44
|
-
l = ::Logger.new(STDOUT)
|
45
|
-
l.formatter = LogFormatter.new
|
46
|
-
l.warn(op: "#{self}", op_data: { msg: "Using default logger to STDOUT" })
|
47
|
-
@@default_logger = l
|
48
|
-
end
|
49
|
-
@@default_logger
|
50
|
-
end
|
51
|
-
module_function :default_logger
|
52
|
-
|
53
|
-
def logger_for(arg)
|
54
|
-
return arg if arg.is_a?(::Logger)
|
55
|
-
return arg.logger if arg.is_a?(Context) && arg.logger
|
56
|
-
return logger_for(arg.context) if arg.respond_to?(:context, false)
|
57
|
-
default_logger
|
58
|
-
end
|
59
|
-
module_function :logger_for
|
60
|
-
|
61
|
-
def parse_args(log_msg, method = nil, context = nil, extra = nil)
|
62
|
-
method, context, extra = nil, method, context unless method.is_a?(String)
|
63
|
-
context, extra = nil, context if context.is_a?(Hash) || context.is_a?(String) && extra.nil?
|
64
|
-
extra = { op_data: { message: extra } } if extra.is_a?(String)
|
65
|
-
logger = logger_for(context) || logger_for(log_msg)
|
66
|
-
log_msg = if log_msg.is_a?(Hash)
|
67
|
-
log_msg.dup
|
68
|
-
elsif log_msg.is_a?(String)
|
69
|
-
log_msg = { op: "#{log_msg}#{method.nil? ? '' : '#'+method.to_s}" }
|
70
|
-
elsif log_msg.is_a?(Exception)
|
71
|
-
log_msg = { error: log_msg }
|
72
|
-
else
|
73
|
-
log_msg = log_msg.class unless log_msg.is_a?(Module)
|
74
|
-
log_msg = { op: "#{log_msg.name}##{method}" }
|
75
|
-
end
|
76
|
-
log_msg.merge!(extra) if extra
|
77
|
-
[ log_msg, logger ]
|
78
|
-
end
|
79
|
-
module_function :parse_args
|
80
|
-
|
81
|
-
[:debug, :info, :warn, :error, :fatal].each do |meth|
|
82
|
-
define_method(meth) do |args, extra = nil, &bl|
|
83
|
-
act_args = (args + [extra]).compact
|
84
|
-
log_msg, logger = parse_args(*act_args)
|
85
|
-
logger.send(meth, log_msg)
|
86
|
-
end
|
87
|
-
module_function(meth)
|
88
|
-
end
|
89
|
-
|
90
|
-
end # module Tools
|
91
|
-
|
92
|
-
# Logs a specific message with a given severity.
|
93
|
-
#
|
94
|
-
# Severity can be :debug, :info, :warn, :error or :fatal.
|
95
|
-
# The args must follow module's conventions, see above.
|
96
|
-
def log(severity, *args)
|
97
|
-
Tools.send(severity, args)
|
98
|
-
end
|
99
|
-
|
100
|
-
# Calls the block and monitors then log its execution time.
|
101
|
-
#
|
102
|
-
# The args must follow module's conventions, see above.
|
103
|
-
def monitor(*args, &bl)
|
104
|
-
result = nil
|
105
|
-
took = Benchmark.realtime {
|
106
|
-
result = bl.call
|
107
|
-
}
|
108
|
-
Tools.info(args, op_took: took)
|
109
|
-
result
|
110
|
-
end
|
111
|
-
|
112
|
-
# Executes the block without letting errors propagate.
|
113
|
-
# Errors are logged, though. Nothing is logged if everything
|
114
|
-
# goes fine.
|
115
|
-
#
|
116
|
-
# The args must follow module's conventions, see above.
|
117
|
-
def stop_errors(*args, &bl)
|
118
|
-
result = nil
|
119
|
-
took = Benchmark.realtime {
|
120
|
-
result = bl.call
|
121
|
-
}
|
122
|
-
result
|
123
|
-
rescue => ex
|
124
|
-
Tools.fatal(args, op_took: took, error: ex)
|
125
|
-
nil
|
126
|
-
end
|
127
|
-
|
128
|
-
# Tries executing the block up to `n` times, until an attempt
|
129
|
-
# succeeds (then returning the result). Logs the first and last
|
130
|
-
# fatal error, if any.
|
131
|
-
#
|
132
|
-
# The args must follow module's conventions, see above.
|
133
|
-
def try_max_times(n, *args, &bl)
|
134
|
-
retried = 0
|
135
|
-
took = 0
|
136
|
-
begin
|
137
|
-
result = nil
|
138
|
-
took += Benchmark.realtime {
|
139
|
-
result = bl.call
|
140
|
-
}
|
141
|
-
result
|
142
|
-
rescue => ex
|
143
|
-
Tools.error(args + [{op_took: took, error: ex}]) if retried == 0
|
144
|
-
retried += 1
|
145
|
-
if retried < n
|
146
|
-
sleep(retried)
|
147
|
-
retry
|
148
|
-
else
|
149
|
-
Tools.fatal(args + [{op_took: took, error: ex}])
|
150
|
-
raise
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
end # module Robustness
|
156
|
-
end # module Support
|
157
|
-
end # module Startback
|
@@ -1,25 +0,0 @@
|
|
1
|
-
module Startback
|
2
|
-
module Support
|
3
|
-
class TransactionManager
|
4
|
-
|
5
|
-
def initialize(db, method = :transaction)
|
6
|
-
@db = db
|
7
|
-
@method = method
|
8
|
-
end
|
9
|
-
|
10
|
-
def call(runner, op, &then_block)
|
11
|
-
raise ArgumentError, "A block is required" unless then_block
|
12
|
-
|
13
|
-
before = (op.class.transaction_policy == :before_call)
|
14
|
-
if before
|
15
|
-
@db.send(@method) do
|
16
|
-
then_block.call
|
17
|
-
end
|
18
|
-
else
|
19
|
-
then_block.call
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
end # class TransactionManager
|
24
|
-
end # module Support
|
25
|
-
end # module Startback
|