stenotype 0.1.0 → 0.1.6
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/.gitignore +3 -0
- data/.rubocop.yml +3 -2
- data/CHANGELOG.md +43 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +111 -60
- data/README.md +48 -17
- data/Rakefile +2 -2
- data/TODO.md +18 -0
- data/bin/console +3 -3
- data/lib/generators/USAGE +8 -0
- data/lib/generators/stenotype/initializer/initializer_generator.rb +23 -0
- data/lib/generators/stenotype/initializer/templates/initializer.rb.erb +82 -0
- data/lib/stenotype.rb +24 -85
- data/lib/stenotype/adapters.rb +3 -3
- data/lib/stenotype/adapters/base.rb +25 -2
- data/lib/stenotype/adapters/google_cloud.rb +70 -22
- data/lib/stenotype/adapters/stdout_adapter.rb +36 -2
- data/lib/stenotype/at_exit.rb +8 -0
- data/lib/stenotype/configuration.rb +127 -36
- data/lib/stenotype/context_handlers.rb +6 -8
- data/lib/stenotype/context_handlers/base.rb +14 -3
- data/lib/stenotype/context_handlers/collection.rb +78 -34
- data/lib/stenotype/context_handlers/rails/active_job.rb +3 -11
- data/lib/stenotype/context_handlers/rails/controller.rb +10 -11
- data/lib/stenotype/dispatcher.rb +1 -2
- data/lib/stenotype/emitter.rb +166 -0
- data/lib/stenotype/event.rb +48 -25
- data/lib/stenotype/event_serializer.rb +31 -11
- data/lib/stenotype/frameworks/rails/action_controller.rb +44 -21
- data/lib/stenotype/frameworks/rails/active_job.rb +3 -5
- data/lib/stenotype/railtie.rb +37 -0
- data/lib/stenotype/version.rb +1 -1
- data/stenotype.gemspec +30 -26
- metadata +70 -19
- data/lib/stenotype/exceptions.rb +0 -31
- data/lib/stenotype/frameworks/object_ext.rb +0 -145
@@ -5,12 +5,46 @@ module Stenotype
|
|
5
5
|
#
|
6
6
|
# An adapter implementing method {#publish} to send data to STDOUT
|
7
7
|
#
|
8
|
+
# @example
|
9
|
+
# class SomeClassWithEvents
|
10
|
+
# def method_emitting_enent
|
11
|
+
# result_of_calculations = collect_some_data
|
12
|
+
# # This will print the data to STDOUT by default
|
13
|
+
# stdout_adapter.publish(result_of_calculation, additional: :data, more: :data)
|
14
|
+
# result_of_calculations
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# def stdout_adapter
|
18
|
+
# Stenotype::Adapters::StdoutAdapter.new
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
8
22
|
class StdoutAdapter < Base
|
9
23
|
#
|
10
24
|
# @param event_data {Hash} The data to be published to STDOUT
|
11
25
|
#
|
12
|
-
|
13
|
-
|
26
|
+
# @example Publishing to default client (STDOUT)
|
27
|
+
# adapter = Stenotype::Adapters::StdoutAdapter.new
|
28
|
+
# adapter.publish({ event: :data }, { additional: :data })
|
29
|
+
#
|
30
|
+
# @example Publishing to custom client (STDERR)
|
31
|
+
# adapter = Stenotype::Adapters::StdoutAdapter.new(client: STDERR)
|
32
|
+
# adapter.publish({ event: :data }, { additional: :data })
|
33
|
+
#
|
34
|
+
def publish(event_data, **additional_attrs)
|
35
|
+
client.info("[Stenotype::Event] emitted with the following attributes") do
|
36
|
+
{
|
37
|
+
**event_data,
|
38
|
+
**additional_attrs,
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Does nothing
|
45
|
+
#
|
46
|
+
def flush!
|
47
|
+
# noop
|
14
48
|
end
|
15
49
|
|
16
50
|
private
|
@@ -4,46 +4,137 @@ module Stenotype
|
|
4
4
|
#
|
5
5
|
# A module containing freshly-event gem configuration
|
6
6
|
#
|
7
|
+
# @example Configuring the library
|
8
|
+
# Stenotype.configure do |config|
|
9
|
+
# config.enabled = true
|
10
|
+
# config.targets = [Target1.new, Target2.new]
|
11
|
+
# config.uuid_generator = SecureRandom
|
12
|
+
#
|
13
|
+
# config.google_cloud do |gc|
|
14
|
+
# gc.credentials = "abc"
|
15
|
+
# gc.project_id = "project"
|
16
|
+
# gc.topic = "42"
|
17
|
+
# gc.async = true
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# config.rails do |rc|
|
21
|
+
# rc.enable_action_controller_ext = true
|
22
|
+
# rc.enable_active_job_ext = false
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
7
26
|
module Configuration
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
extend Spicerack::Configurable
|
28
|
+
|
29
|
+
# @!attribute graceful_error_handling
|
30
|
+
# @return {true, false} a flag for suppressing error raised withing the gem
|
31
|
+
|
32
|
+
# @!attribute logger
|
33
|
+
# @return {Logger} a logger with default severity methods to output gem level messages
|
34
|
+
|
35
|
+
# @!attribute enabled
|
36
|
+
# @return {true, false} a flag indicating whether event emission is enabled
|
37
|
+
|
38
|
+
# @!attribute targets
|
39
|
+
# @return {Array<#publish>} a list of targets responding to method [#publish]
|
40
|
+
|
41
|
+
# @!attribute [rw] dispatcher
|
42
|
+
# @return {#publish} as object responding to method [#publish]
|
43
|
+
|
44
|
+
# @!attribute [rw] uuid_generator
|
45
|
+
# @return {#uuid} an object responding to method [#uuid]
|
46
|
+
|
47
|
+
# @!attribute [rw] google_cloud
|
48
|
+
# @return [NestedConfiguration] google cloud configuration.
|
49
|
+
|
50
|
+
# @!attribute [rw] google_cloud.credentials
|
51
|
+
# @return {String} a string with GC API credential. Refer to GC PubSub documentation
|
52
|
+
|
53
|
+
# @!attribute [rw] google_cloud.project_id
|
54
|
+
# @return {String} a name of the project in GC PubSub
|
55
|
+
|
56
|
+
# @!attribute [rw] google_cloud.topic
|
57
|
+
# @return {String} a name of the topic in GC PubSub
|
58
|
+
|
59
|
+
# @!attribute [rw] google_cloud.async
|
60
|
+
# @return [true, false] GC publish mode, either async if true, sync if false
|
61
|
+
|
62
|
+
|
63
|
+
# @!attribute [rw] rails
|
64
|
+
# @return [NestedConfiguration] Rails configuration.
|
65
|
+
|
66
|
+
# @!attribute [rw] rails.enable_action_controller_ext
|
67
|
+
# @return [true, false] A flag of whether ActionController ext is enabled
|
68
|
+
|
69
|
+
# @!attribute [rw] rails.enable_active_job_ext
|
70
|
+
# @return [true, false] A flag of whether ActiveJob ext is enabled
|
71
|
+
|
72
|
+
configuration_options do
|
73
|
+
option :graceful_error_handling, default: true
|
74
|
+
option :enabled, default: true
|
75
|
+
option :targets, default: []
|
76
|
+
option :dispatcher, default: Stenotype::Dispatcher
|
77
|
+
option :uuid_generator, default: SecureRandom
|
78
|
+
option :logger
|
79
|
+
|
80
|
+
nested :google_cloud do
|
81
|
+
option :credentials, default: nil
|
82
|
+
option :project_id, default: nil
|
83
|
+
option :topic, default: nil
|
84
|
+
option :async, default: true
|
32
85
|
end
|
33
86
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
#
|
38
|
-
def targets
|
39
|
-
if @targets.nil? || @targets.empty?
|
40
|
-
raise Stenotype::Exceptions::NoTargetsSpecified,
|
41
|
-
'Please configure a target(s) for events to be sent to. ' \
|
42
|
-
'See Stenotype::Configuration for reference.'
|
43
|
-
else
|
44
|
-
@targets
|
45
|
-
end
|
87
|
+
nested :rails do
|
88
|
+
option :enable_action_controller_ext, default: true
|
89
|
+
option :enable_active_job_ext, default: true
|
46
90
|
end
|
47
91
|
end
|
92
|
+
|
93
|
+
module_function
|
94
|
+
|
95
|
+
#
|
96
|
+
# @example With default logger
|
97
|
+
# Stenotype.configure do |config|
|
98
|
+
# # config.logger = nil # logger not set manually
|
99
|
+
# end
|
100
|
+
# Stenotype.config.logger #=> `Logger.new(STDOUT)` instance
|
101
|
+
#
|
102
|
+
# @example With custom logger
|
103
|
+
# Stenotype.configure do |config|
|
104
|
+
# config.logger = custom_logger_instance
|
105
|
+
# end
|
106
|
+
# Stenotype.config.logger #=> custom_logger_instance
|
107
|
+
#
|
108
|
+
# @return [{Logger, CustomLogger}] a logger object. Logger.new(STDOUT) by
|
109
|
+
# default if another is not set during configuration
|
110
|
+
#
|
111
|
+
def logger
|
112
|
+
return config.logger if config.logger
|
113
|
+
config.logger || Logger.new(STDOUT)
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# @example When at least one target is present
|
118
|
+
# Stenotype.configure do |config|
|
119
|
+
# config.targets = [Target1.new, Target2.new]
|
120
|
+
# end
|
121
|
+
# Stenotype.config.targets #=> [target_obj1, target_obj2]
|
122
|
+
#
|
123
|
+
# @example When no targets have been configured
|
124
|
+
# Stenotype.configure { |config| config.targets = [] }
|
125
|
+
# Stenotype.config.targets #=> Stenotype::NoTargetsSpecifiedError
|
126
|
+
#
|
127
|
+
# @raise {Stenotype::NoTargetsSpecifiedError} in case no targets are configured
|
128
|
+
# @return {Array<#publish>} An array of targets implementing method [#publish]
|
129
|
+
#
|
130
|
+
# @todo THIS NEVER GETS CALLED, needs a fix
|
131
|
+
#
|
132
|
+
def targets
|
133
|
+
return config.targets unless config.targets.empty?
|
134
|
+
|
135
|
+
raise Stenotype::NoTargetsSpecifiedError,
|
136
|
+
"Please configure a target(s) for events to be sent to. "\
|
137
|
+
"See #{Stenotype::Configuration} for reference."
|
138
|
+
end
|
48
139
|
end
|
49
140
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
3
|
+
require "stenotype/context_handlers/base"
|
4
|
+
require "stenotype/context_handlers/rails/controller"
|
5
|
+
require "stenotype/context_handlers/rails/active_job"
|
6
|
+
require "stenotype/context_handlers/klass"
|
7
|
+
require "stenotype/context_handlers/collection"
|
8
8
|
|
9
9
|
module Stenotype
|
10
10
|
#
|
@@ -24,9 +24,7 @@ module Stenotype
|
|
24
24
|
#
|
25
25
|
# @param handler {#publish} A handler with implemented method [#publish]
|
26
26
|
#
|
27
|
-
|
28
|
-
known.register(handler)
|
29
|
-
end
|
27
|
+
delegate :register, to: :known
|
30
28
|
end
|
31
29
|
end
|
32
30
|
end
|
@@ -9,6 +9,18 @@ module Stenotype
|
|
9
9
|
# @attr_reader {Object} context A context in which the event was emitted
|
10
10
|
# @attr_reader {Hash} options A hash of additional options
|
11
11
|
#
|
12
|
+
# @example Defining a custom Handler
|
13
|
+
# class MyCustomHandler < Stenotype::ContextHandlers::Base
|
14
|
+
# self.context_name = :custom_handler
|
15
|
+
#
|
16
|
+
# def as_json(*_args)
|
17
|
+
# {
|
18
|
+
# value1: context.value1,
|
19
|
+
# value2: context.value2
|
20
|
+
# }
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
12
24
|
class Base
|
13
25
|
attr_reader :context, :options
|
14
26
|
|
@@ -18,7 +30,7 @@ module Stenotype
|
|
18
30
|
#
|
19
31
|
# @return {#as_json} A context handler implementing [#as_json]
|
20
32
|
#
|
21
|
-
def initialize(context, options
|
33
|
+
def initialize(context, options: {})
|
22
34
|
@context = context
|
23
35
|
@options = options
|
24
36
|
end
|
@@ -43,8 +55,7 @@ module Stenotype
|
|
43
55
|
# @raise {NotImplementedError} in case handler name is not specified.
|
44
56
|
#
|
45
57
|
def handler_name
|
46
|
-
@handler_name || raise(NotImplementedError,
|
47
|
-
"Please, specify the handler_name of #{self}")
|
58
|
+
@handler_name || raise(NotImplementedError, "Please, specify the handler_name of #{self}")
|
48
59
|
end
|
49
60
|
end
|
50
61
|
end
|
@@ -4,61 +4,105 @@ module Stenotype
|
|
4
4
|
module ContextHandlers
|
5
5
|
#
|
6
6
|
# A class representing a list of available context handlers
|
7
|
+
# @example Complete usage overview
|
8
|
+
# class MyCustomHander
|
9
|
+
# self.handler_name = :custom_handler
|
7
10
|
#
|
8
|
-
|
11
|
+
# def as_json(*_args)
|
12
|
+
# {
|
13
|
+
# key: :value
|
14
|
+
# }
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# collection = Stenotype::ContextHandlers::Collection.new
|
19
|
+
# collection.register(MyCustomHandler)
|
20
|
+
#
|
21
|
+
# collection.registered?(MyCustomHandler) #=> true
|
22
|
+
# collection.choose(handler_name: :custom_handler) #=> MyCustomHandler
|
23
|
+
# collection.unregister(MyCustomHandler)
|
24
|
+
# collection.registered?(MyCustomHandler) #=> false
|
25
|
+
#
|
26
|
+
# collection.register(SomeRandomClass) #=> ArgumentError
|
27
|
+
# collection.unregister(SomeRandomClass) #=> ArgumentError
|
28
|
+
# collection.choose(handler_name: :unknown) #=> Stenotype::UnknownHandlerError
|
29
|
+
#
|
30
|
+
class Collection < ::Collectible::CollectionBase
|
31
|
+
#
|
32
|
+
# Return a handler with given handler_name if found in collection,
|
33
|
+
# raises if a handler is not registered
|
9
34
|
#
|
10
35
|
# @param handler_name {Symbol} a handler to be found in the collection
|
11
|
-
# @raise {Stenotype::
|
36
|
+
# @raise {Stenotype::Errors::UnknownHandler} in case a handler is not registered
|
12
37
|
# @return {#as_json} A handler which respond to #as_json
|
13
38
|
#
|
39
|
+
# @example When a handler is present in the collection
|
40
|
+
# collection = Stenotype::ContextHandlers::Collection.new
|
41
|
+
# collection.register(MyCustomHandler) # with handler_name = :custom_handler
|
42
|
+
# collection.choose(handler_name: :custom_handler) #=> MyCustomHandler
|
43
|
+
#
|
44
|
+
# @example When a handler is not present in the collection
|
45
|
+
# collection = Stenotype::ContextHandlers::Collection.new
|
46
|
+
# collection.choose(handler_name: :custom_handler) #=> MyCustomHandler
|
47
|
+
#
|
14
48
|
def choose(handler_name:)
|
15
|
-
handler =
|
16
|
-
handler || raise(
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
49
|
+
handler = find_by(handler_name: handler_name)
|
50
|
+
handler || raise(
|
51
|
+
Stenotype::UnknownHandlerError,
|
52
|
+
"Handler '#{handler_name}' is not found. "\
|
53
|
+
"Please make sure the handler you've specified is "\
|
54
|
+
"registered in the list of known handlers. "\
|
55
|
+
"See #{Stenotype::ContextHandlers} for more information.",
|
56
|
+
)
|
21
57
|
end
|
22
58
|
|
23
59
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
60
|
+
# @!method register(handler)
|
61
|
+
# Registers a new handler.
|
62
|
+
# @!scope instance
|
63
|
+
# @param handler {#as_json} a new handler to be added to collection
|
64
|
+
# @return {Stenotype::ContextHandlers::Collection} a collection object
|
65
|
+
# @example Registering a new handler
|
66
|
+
# collection = Stenotype::ContextHandlers::Collection.new
|
67
|
+
# collection.register(MyCustomHandler) # with handler_name = :custom_handler
|
27
68
|
#
|
28
|
-
|
29
|
-
unless handler < Stenotype::ContextHandlers::Base
|
30
|
-
raise ArgumentError,
|
31
|
-
"Handler must inherit from #{Stenotype::ContextHandlers::Base}, " \
|
32
|
-
"but inherited from #{handler.superclass}"
|
33
|
-
end
|
34
|
-
|
35
|
-
push(handler) unless registered?(handler)
|
36
|
-
self
|
37
|
-
end
|
69
|
+
alias_method :register, :push
|
38
70
|
|
71
|
+
#
|
72
|
+
# Removes a registered handler.
|
73
|
+
#
|
74
|
+
# @example Register and unregister a handler
|
75
|
+
# collection = Stenotype::ContextHandlers::Collection.new
|
76
|
+
# collection.register(MyCustomHandler) # with handler_name = :custom_handler
|
77
|
+
# collection.unregister(MyCustomHandler) # removes MyCustomHandler from the list of handlers
|
39
78
|
#
|
40
79
|
# @param handler {#as_json} a handler to be removed from the collection of known handlers
|
41
|
-
# @raise {ArgumentError} in case handler does not inherit from {Stenotype::ContextHandlers::Base}
|
42
80
|
# @return {Stenotype::ContextHandlers::Collection} a collection object
|
43
81
|
#
|
82
|
+
# @todo Add delete to the collectible delegation list
|
83
|
+
# and then use `alias_method :unregister, :delete`
|
44
84
|
def unregister(handler)
|
45
|
-
|
46
|
-
raise ArgumentError,
|
47
|
-
"Handler must inherit from #{Stenotype::ContextHandlers::Base}, " \
|
48
|
-
"but inherited from #{handler.superclass}"
|
49
|
-
end
|
50
|
-
|
51
|
-
delete(handler) if registered?(handler)
|
85
|
+
items.delete(handler)
|
52
86
|
self
|
53
87
|
end
|
54
88
|
|
55
89
|
#
|
56
|
-
#
|
57
|
-
#
|
90
|
+
# @!method registered?(handler)
|
91
|
+
# Checks whether a given handler is registered in collection
|
92
|
+
# @!scope instance
|
93
|
+
# @param handler {#as_json} a handler to be checked for presence in a collection
|
94
|
+
# @return [true, false]
|
95
|
+
# @example When a handler is registered
|
96
|
+
# collection = Stenotype::ContextHandlers::Collection.new
|
97
|
+
# collection.register(MyCustomHandler) # with handler_name = :custom_handler
|
98
|
+
# collection.registered?(MyCustomHandler) # true
|
58
99
|
#
|
59
|
-
|
60
|
-
|
61
|
-
|
100
|
+
# @example When a handler is not registered
|
101
|
+
# collection = Stenotype::ContextHandlers::Collection.new
|
102
|
+
# collection.registered?(UnregisteredHandler) # false
|
103
|
+
#
|
104
|
+
|
105
|
+
alias_method :registered?, :include?
|
62
106
|
end
|
63
107
|
end
|
64
108
|
end
|
@@ -22,21 +22,13 @@ module Stenotype
|
|
22
22
|
def as_json(*_args)
|
23
23
|
{
|
24
24
|
job_id: job_id,
|
25
|
-
enqueued_at: Time.now,
|
25
|
+
enqueued_at: Time.now.utc,
|
26
26
|
queue_name: queue_name,
|
27
|
-
class: context.class.name
|
27
|
+
class: context.class.name,
|
28
28
|
}
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
def job_id
|
34
|
-
context.job_id
|
35
|
-
end
|
36
|
-
|
37
|
-
def queue_name
|
38
|
-
context.queue_name
|
39
|
-
end
|
31
|
+
delegate :job_id, :queue_name, to: :context
|
40
32
|
end
|
41
33
|
end
|
42
34
|
end
|