startback-websocket 0.14.0 → 0.14.1
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 +4 -4
- data/Gemfile +3 -2
- data/README.md +64 -9
- 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 +20 -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
|