kanal 0.3.0
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 +7 -0
- data/.DS_Store +0 -0
- data/.rspec +3 -0
- data/.rubocop.yml +14 -0
- data/.ruby-version +1 -0
- data/.vscode/settings.json +8 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +26 -0
- data/Gemfile.lock +108 -0
- data/LICENSE.txt +21 -0
- data/README.md +45 -0
- data/Rakefile +12 -0
- data/kanal.gemspec +39 -0
- data/lib/kanal/core/conditions/condition.rb +36 -0
- data/lib/kanal/core/conditions/condition_creator.rb +32 -0
- data/lib/kanal/core/conditions/condition_pack.rb +52 -0
- data/lib/kanal/core/conditions/condition_pack_creator.rb +41 -0
- data/lib/kanal/core/conditions/condition_storage.rb +58 -0
- data/lib/kanal/core/core.rb +214 -0
- data/lib/kanal/core/helpers/condition_finder.rb +26 -0
- data/lib/kanal/core/helpers/parameter_bag.rb +22 -0
- data/lib/kanal/core/helpers/parameter_bag_with_registrator.rb +46 -0
- data/lib/kanal/core/helpers/parameter_finder_with_method_missing_mixin.rb +38 -0
- data/lib/kanal/core/helpers/parameter_registrator.rb +48 -0
- data/lib/kanal/core/helpers/router_proc_parser.rb +47 -0
- data/lib/kanal/core/hooks/hook_storage.rb +109 -0
- data/lib/kanal/core/input/input.rb +20 -0
- data/lib/kanal/core/interfaces/interface.rb +65 -0
- data/lib/kanal/core/logger/logger.rb +12 -0
- data/lib/kanal/core/output/output.rb +31 -0
- data/lib/kanal/core/output/output_creator.rb +17 -0
- data/lib/kanal/core/plugins/plugin.rb +44 -0
- data/lib/kanal/core/router/router.rb +97 -0
- data/lib/kanal/core/router/router_node.rb +131 -0
- data/lib/kanal/core/router/router_storage.rb +33 -0
- data/lib/kanal/core/services/service_container.rb +97 -0
- data/lib/kanal/interfaces/simple_cli/simple_cli_interface.rb +51 -0
- data/lib/kanal/plugins/batteries/batteries_plugin.rb +116 -0
- data/lib/kanal/version.rb +5 -0
- data/lib/kanal.rb +9 -0
- data/sig/kanal/core/conditions/condition_pack.rbs +9 -0
- data/sig/kanal/core/conditions/condition_storage.rbs +9 -0
- data/sig/kanal.rbs +4 -0
- metadata +90 -0
@@ -0,0 +1,214 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./conditions/condition_storage"
|
4
|
+
require_relative "./conditions/condition_pack_creator"
|
5
|
+
require_relative "./router/router_storage"
|
6
|
+
require_relative "./hooks/hook_storage"
|
7
|
+
require_relative "./helpers/parameter_registrator"
|
8
|
+
require_relative "./plugins/plugin"
|
9
|
+
require_relative "./input/input"
|
10
|
+
require_relative "./services/service_container"
|
11
|
+
|
12
|
+
module Kanal
|
13
|
+
module Core
|
14
|
+
#
|
15
|
+
# Main class that end users of Kanal will be using for
|
16
|
+
# initialization, plugin registration, conditions registration, etc
|
17
|
+
#
|
18
|
+
# TODO: consider hiding properties used inside
|
19
|
+
# and provide those dependencies explicitly
|
20
|
+
# e.g.: output_parameter_registrator is needed for router
|
21
|
+
# to create output. It provides the parameters registration
|
22
|
+
# info for the Output object. Can it be explicitly passed into the router
|
23
|
+
# instead of exposing in in Core?
|
24
|
+
#
|
25
|
+
# @!attribute [r] hooks
|
26
|
+
# @return [Kanal::Core::Hooks::HookStorage] storage which can be used to register hooks
|
27
|
+
# @!attribute [r] services
|
28
|
+
# @return [Kanal::Core::Services::ServiceContainer] service container for registering and getting services
|
29
|
+
#
|
30
|
+
class Core
|
31
|
+
include Conditions
|
32
|
+
include Router
|
33
|
+
include Helpers
|
34
|
+
include Plugins
|
35
|
+
include Hooks
|
36
|
+
include Services
|
37
|
+
|
38
|
+
# @return [Kanal::Core::Conditions::ConditionStorage]
|
39
|
+
attr_reader :condition_storage
|
40
|
+
# @return [Kanal::Core::Router::RouterStorage]
|
41
|
+
attr_reader :router_storage
|
42
|
+
# @return [Kanal::Core::Helpers::ParameterRegistrator]
|
43
|
+
attr_reader :input_parameter_registrator
|
44
|
+
# @return [Kanal::Core::Helpers::ParameterRegistrator]
|
45
|
+
attr_reader :output_parameter_registrator
|
46
|
+
# @return [Kanal::Core::Hooks::HookStorage]
|
47
|
+
attr_reader :hooks
|
48
|
+
# @return [Kanal::Core::Services::ServiceContainer]
|
49
|
+
attr_reader :services
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
@hooks = HookStorage.new
|
53
|
+
register_hooks
|
54
|
+
|
55
|
+
@condition_storage = ConditionStorage.new
|
56
|
+
@router_storage = RouterStorage.new self
|
57
|
+
|
58
|
+
@input_parameter_registrator = ParameterRegistrator.new
|
59
|
+
@output_parameter_registrator = ParameterRegistrator.new
|
60
|
+
|
61
|
+
@plugins = []
|
62
|
+
|
63
|
+
@services = ServiceContainer.new
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Method for registering plugins. Plugins should be of type
|
68
|
+
# Kanal::Core::Plugins::Plugin. Meaning that any dervied types
|
69
|
+
# would be accepted
|
70
|
+
#
|
71
|
+
# @param [Kanal::Core::Plugins::Plugin] plugin
|
72
|
+
#
|
73
|
+
# @return [void]
|
74
|
+
#
|
75
|
+
def register_plugin(plugin)
|
76
|
+
unless plugin.is_a? Plugin
|
77
|
+
raise "Plugin must be of type Kanal::Core::Plugin or be a class that inherits base Plugin class"
|
78
|
+
end
|
79
|
+
|
80
|
+
begin
|
81
|
+
# Checking if name was provided.
|
82
|
+
name = plugin.name
|
83
|
+
|
84
|
+
# TODO: _log that plugin already registered with such name
|
85
|
+
return if !name.nil? && plugin_registered?(name)
|
86
|
+
|
87
|
+
plugin.setup(self)
|
88
|
+
|
89
|
+
@plugins.append plugin
|
90
|
+
# NOTE: Catching here Exception because metho.name can raise ScriptError (derived from Exception)
|
91
|
+
# and method .setup can raise ANY type of error.
|
92
|
+
# Despite the warnings from linters about "please catch explicitly error or catch StandardError"
|
93
|
+
# - sorry, no can't do here
|
94
|
+
rescue Exception => e
|
95
|
+
name = nil
|
96
|
+
|
97
|
+
begin
|
98
|
+
name = plugin.name
|
99
|
+
rescue Exception
|
100
|
+
name = "CANT_GET_NAME_DUE_TO_ERROR"
|
101
|
+
end
|
102
|
+
|
103
|
+
# TODO: _log this info in critical error instead of raising exception
|
104
|
+
raise "There was a problem while registering plugin named: #{name}. Error: `#{e}`.
|
105
|
+
Remember, plugin errors are often due to .name method not overriden or
|
106
|
+
having faulty code inside .setup overriden method"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Get registered plugin for modification of some sort
|
112
|
+
#
|
113
|
+
# @param [Symbol] name <description>
|
114
|
+
#
|
115
|
+
# @return [<Type>] <description>
|
116
|
+
#
|
117
|
+
def get_plugin(name)
|
118
|
+
@plugins.find { |p| p.name == name }
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# <Description>
|
123
|
+
#
|
124
|
+
# @param [Symbol] name <description>
|
125
|
+
#
|
126
|
+
# @return [Boolean] <description>
|
127
|
+
#
|
128
|
+
def plugin_registered?(name)
|
129
|
+
!get_plugin(name).nil?
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# Method creates instance of Kanal::Core::Input::Input
|
134
|
+
# Note that right before input returned, hook :input_just_created
|
135
|
+
# called
|
136
|
+
#
|
137
|
+
# @return [Kanal::Core::Input::Input] <description>
|
138
|
+
#
|
139
|
+
def create_input
|
140
|
+
input = Input::Input.new @input_parameter_registrator
|
141
|
+
|
142
|
+
@hooks.call :input_just_created, input
|
143
|
+
|
144
|
+
input
|
145
|
+
end
|
146
|
+
|
147
|
+
#
|
148
|
+
# Method registers parameters that can be set/get in Kanal::Core::Input::Input
|
149
|
+
#
|
150
|
+
# @param [Symbol] name <description>
|
151
|
+
# @param [Boolean] readonly <description>
|
152
|
+
#
|
153
|
+
# @return [void] <description>
|
154
|
+
#
|
155
|
+
def register_input_parameter(name, readonly: false)
|
156
|
+
@input_parameter_registrator.register_parameter name, readonly: readonly
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# The same as registering input, but for Kanal::Core::Output::Output
|
161
|
+
#
|
162
|
+
# @param [Symbol] name <description>
|
163
|
+
# @param [Boolean] readonly <description>
|
164
|
+
#
|
165
|
+
# @return [void] <description>
|
166
|
+
#
|
167
|
+
def register_output_parameter(name, readonly: false)
|
168
|
+
@output_parameter_registrator.register_parameter name, readonly: readonly
|
169
|
+
end
|
170
|
+
|
171
|
+
#
|
172
|
+
# Handy method with DSL (Domain Specific Language) that allows
|
173
|
+
# condition makers to easily create condition packs and
|
174
|
+
# conditions.
|
175
|
+
#
|
176
|
+
# @param [Symbol] name <description>
|
177
|
+
# @yield block with inner DSL for adding conditions to condition pack
|
178
|
+
#
|
179
|
+
# @return [void] <description>
|
180
|
+
#
|
181
|
+
def add_condition_pack(name, &block)
|
182
|
+
creator = ConditionPackCreator.new name
|
183
|
+
|
184
|
+
pack = creator.create(&block)
|
185
|
+
|
186
|
+
@condition_storage.register_condition_pack pack
|
187
|
+
end
|
188
|
+
|
189
|
+
#
|
190
|
+
# Gets router by the name or if no argument
|
191
|
+
# provided, creates and gets :default router
|
192
|
+
# This method usually used without arguments,
|
193
|
+
# only in special cases you might need to create separate routers
|
194
|
+
# TODO: is creating different routers actually needed? Within the same core
|
195
|
+
# What cases are possible for such behaviour? Maybe we should remove the ability...
|
196
|
+
#
|
197
|
+
# @param [Symbol] name <description>
|
198
|
+
#
|
199
|
+
# @return [Kanal::Core::Router::Router] <description>
|
200
|
+
#
|
201
|
+
def router(name = :default)
|
202
|
+
@router_storage.get_or_create_router name
|
203
|
+
end
|
204
|
+
|
205
|
+
def register_hooks
|
206
|
+
@hooks.register :input_just_created # input
|
207
|
+
@hooks.register :input_before_router # input
|
208
|
+
@hooks.register :output_before_returned # input, output
|
209
|
+
end
|
210
|
+
|
211
|
+
private :register_hooks
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Kanal
|
2
|
+
module Core
|
3
|
+
module Helpers
|
4
|
+
module ConditionFinder
|
5
|
+
class ConditionFindResult
|
6
|
+
attr_reader :found_condition_pack,
|
7
|
+
:found_condition
|
8
|
+
|
9
|
+
def initialize(found_condition_pack: false, found_condition: false)
|
10
|
+
@found_condition_pack = found_condition_pack
|
11
|
+
@found_condition = found_condition
|
12
|
+
end
|
13
|
+
|
14
|
+
def found_anything?
|
15
|
+
@found_condition || @found_condition_pack
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class ConditionFinder
|
20
|
+
def find_by_name(name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kanal
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
# Generic parameter bag class that stores named parameters
|
7
|
+
class ParameterBag
|
8
|
+
def initialize
|
9
|
+
@parameters = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(name)
|
13
|
+
@parameters[name]
|
14
|
+
end
|
15
|
+
|
16
|
+
def set(name, value)
|
17
|
+
@parameters[name] = value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative "parameter_bag"
|
2
|
+
|
3
|
+
module Kanal
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
# Parameter bag but it checks registrator for existence of parameters
|
7
|
+
# and if they are has needed by registrator allowances, types etc, whatever
|
8
|
+
# registrator rules are stored for property
|
9
|
+
class ParameterBagWithRegistrator < ParameterBag
|
10
|
+
def initialize(registrator)
|
11
|
+
super()
|
12
|
+
@registrator = registrator
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(name)
|
16
|
+
validate_parameter_registration name
|
17
|
+
|
18
|
+
super name
|
19
|
+
end
|
20
|
+
|
21
|
+
def set(name, value)
|
22
|
+
validate_parameter_registration name
|
23
|
+
|
24
|
+
readonly = @registrator.get_parameter_registration_if_exists(name).readonly?
|
25
|
+
|
26
|
+
if readonly
|
27
|
+
value_exists = !get(name).nil?
|
28
|
+
|
29
|
+
if value_exists
|
30
|
+
raise "Parameter #{name} is marked readonly! You tried to set it's value, but
|
31
|
+
it already has value."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
super name, value
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_parameter_registration(name)
|
39
|
+
unless @registrator.parameter_registered? name
|
40
|
+
raise "Parameter #{name} was not registered! Did you forget to register that parameter?"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kanal
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
# Module assumes that class where it was included has methods
|
7
|
+
# set(name, value)
|
8
|
+
# get(name)
|
9
|
+
# transforms unknown methods to setters/getters for parameters
|
10
|
+
module ParameterFinderWithMethodMissingMixin
|
11
|
+
def method_missing(symbol, *args)
|
12
|
+
parameter_name = symbol.to_s
|
13
|
+
parameter_name.sub! "=", ""
|
14
|
+
|
15
|
+
parameter_name = parameter_name.to_sym
|
16
|
+
|
17
|
+
# standard workflow with settings properties with
|
18
|
+
# input.prop = 123
|
19
|
+
if symbol.to_s.include? "="
|
20
|
+
@parameter_bag.set parameter_name, args.first
|
21
|
+
else
|
22
|
+
# this approach can be used also in dsl
|
23
|
+
# like that
|
24
|
+
# setters: prop value
|
25
|
+
# getters: prop
|
26
|
+
if !args.empty?
|
27
|
+
# means it is used as setter in dsl,
|
28
|
+
# method call with argument
|
29
|
+
@parameter_bag.set(parameter_name, *args)
|
30
|
+
else
|
31
|
+
@parameter_bag.get parameter_name
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Kanal
|
2
|
+
module Core
|
3
|
+
module Helpers
|
4
|
+
# For registered property info,
|
5
|
+
# this class is used for future additions,
|
6
|
+
# maybe type validations or something
|
7
|
+
class ParameterRegistration
|
8
|
+
def initialize(readonly)
|
9
|
+
@readonly = readonly
|
10
|
+
end
|
11
|
+
|
12
|
+
def readonly?
|
13
|
+
@readonly
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Class holds parameter names that are allowed
|
18
|
+
# to be used.
|
19
|
+
class ParameterRegistrator
|
20
|
+
def initialize
|
21
|
+
@parameters_by_name = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
# readonly paramaeter means that once it was initialized - it cannot
|
25
|
+
# be changed. handy for input parameters populated by interface or
|
26
|
+
# whatever
|
27
|
+
def register_parameter(name, readonly: false)
|
28
|
+
raise "Parameter named #{name} already registered!" if @parameters_by_name.key? name
|
29
|
+
|
30
|
+
registration = ParameterRegistration.new readonly
|
31
|
+
|
32
|
+
@parameters_by_name[name] = registration
|
33
|
+
end
|
34
|
+
|
35
|
+
# returns nil if no parameter registered
|
36
|
+
def get_parameter_registration_if_exists(name)
|
37
|
+
return nil unless @parameters_by_name.key? name
|
38
|
+
|
39
|
+
@parameters_by_name[name]
|
40
|
+
end
|
41
|
+
|
42
|
+
def parameter_registered?(name)
|
43
|
+
!get_parameter_registration_if_exists(name).nil?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "method_source"
|
2
|
+
|
3
|
+
module Kanal
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
# Class helps with parsing router procs for
|
7
|
+
# helping forming handy DSL without commas
|
8
|
+
class RouterProcParser
|
9
|
+
def get_conditions_method_names_from_block(&block)
|
10
|
+
source = block.source.to_s
|
11
|
+
|
12
|
+
method_names = []
|
13
|
+
|
14
|
+
lines = source.split "\n"
|
15
|
+
|
16
|
+
lines.each do |l|
|
17
|
+
names = get_method_names_from_line l
|
18
|
+
|
19
|
+
method_names.concat names
|
20
|
+
end
|
21
|
+
|
22
|
+
method_names.uniq
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_method_names_from_line(line)
|
26
|
+
method_names = []
|
27
|
+
|
28
|
+
line = line.lstrip
|
29
|
+
|
30
|
+
return method_names unless line.start_with? "on"
|
31
|
+
|
32
|
+
words = line.split
|
33
|
+
|
34
|
+
condition_pack = words[1]
|
35
|
+
condition = words[2]
|
36
|
+
|
37
|
+
method_names.append condition_pack
|
38
|
+
method_names.append condition
|
39
|
+
|
40
|
+
method_names
|
41
|
+
end
|
42
|
+
|
43
|
+
private :get_method_names_from_line
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kanal
|
4
|
+
module Core
|
5
|
+
module Hooks
|
6
|
+
#
|
7
|
+
# Allows hooks registration,
|
8
|
+
# attaching to hooks, calling hooks with arguments
|
9
|
+
#
|
10
|
+
class HookStorage
|
11
|
+
def initialize
|
12
|
+
@listeners = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
# Registers hook in storage. All you need is name
|
17
|
+
#
|
18
|
+
# @example Registering a hook
|
19
|
+
# hook_storage.register(:my_hook) # That is all
|
20
|
+
#
|
21
|
+
# @param [Symbol] name <description>
|
22
|
+
#
|
23
|
+
# @return [void] <description>
|
24
|
+
#
|
25
|
+
# TODO: think about requiring optional string about hook arguments?
|
26
|
+
# like register(:my_hook, "name, last_name")
|
27
|
+
# or is it too weird and unneeded? I mean besides the documentation
|
28
|
+
# there is no way to learn which arguments are used in specific hook.
|
29
|
+
# Wondrous world of dynamic languages 🌈🦄
|
30
|
+
#
|
31
|
+
def register(name)
|
32
|
+
return if hook_exists? name
|
33
|
+
|
34
|
+
@listeners[name] = []
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Calling hook with any arguments you want.
|
39
|
+
# Well, when you registered hooks you basically had in mind
|
40
|
+
# which arguments should be used when calling them
|
41
|
+
#
|
42
|
+
# @example Calling hook with arguments
|
43
|
+
# # Considering names variables (old_name, new_name) are available when calling a hook
|
44
|
+
# # This one will call the :name_changed hook with passed arguments.
|
45
|
+
# # What does that mean? That possibly there is a listener attached to this hook
|
46
|
+
# # @see #attach for next step of this example
|
47
|
+
# hook_storage.call :name_changed, old_name, new_name
|
48
|
+
#
|
49
|
+
# @param [Symbol] name <description>
|
50
|
+
# @param [Array] args <description>
|
51
|
+
#
|
52
|
+
# @return [void] <description>
|
53
|
+
#
|
54
|
+
def call(name, *args)
|
55
|
+
raise "Cannot call hook that is not registered: #{name}" unless hook_exists? name
|
56
|
+
|
57
|
+
@listeners[name].each do |l|
|
58
|
+
l.method(:hook_block).call(*args)
|
59
|
+
end
|
60
|
+
rescue RuntimeError => e
|
61
|
+
raise "There was a problem with calling hooks #{name}. Args: #{args}. More info: #{e}"
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Attaches block to a specific hook.
|
66
|
+
# You can learn about available hooks by
|
67
|
+
# looking into documentation of the specific libraries,
|
68
|
+
# using this class.
|
69
|
+
#
|
70
|
+
# @example Attaching to name changing hook, the next step after @see #call example
|
71
|
+
# hook_storage.attach :name_changed do |old_name, new_name|
|
72
|
+
# # Here you do something with the provided arguments
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# NOTE: about weird saving objects with methods instead of blocks
|
76
|
+
# see more info in Condition class, you will learn why (hint: LocalJumpError)
|
77
|
+
#
|
78
|
+
# @param [Symbol] name <description>
|
79
|
+
# @yield block that will be executed upon calling hook it was registered to
|
80
|
+
#
|
81
|
+
# @return [void] <description>
|
82
|
+
#
|
83
|
+
def attach(name, &block)
|
84
|
+
unless hook_exists? name
|
85
|
+
raise "You cannot listen to hook that does not exist! Hook in question: #{name}"
|
86
|
+
end
|
87
|
+
|
88
|
+
proc_to_lambda_object = Object.new
|
89
|
+
proc_to_lambda_object.define_singleton_method(:hook_block, &block)
|
90
|
+
|
91
|
+
@listeners[name].append proc_to_lambda_object
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Self explanatory name of method
|
96
|
+
#
|
97
|
+
# @param [Symbol] name <description>
|
98
|
+
#
|
99
|
+
# @return [Boolean] <description>
|
100
|
+
#
|
101
|
+
def hook_exists?(name)
|
102
|
+
!@listeners[name].nil?
|
103
|
+
end
|
104
|
+
|
105
|
+
private :hook_exists?
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../helpers/parameter_finder_with_method_missing_mixin"
|
4
|
+
require_relative "../helpers/parameter_bag_with_registrator"
|
5
|
+
|
6
|
+
module Kanal
|
7
|
+
module Core
|
8
|
+
module Input
|
9
|
+
# This class contains all the needed input properties
|
10
|
+
class Input
|
11
|
+
include Helpers
|
12
|
+
include Helpers::ParameterFinderWithMethodMissingMixin
|
13
|
+
|
14
|
+
def initialize(parameter_registrator)
|
15
|
+
@parameter_bag = ParameterBagWithRegistrator.new parameter_registrator
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../core"
|
4
|
+
|
5
|
+
module Kanal
|
6
|
+
module Core
|
7
|
+
module Interfaces
|
8
|
+
# Basic class of interface - interface is basically
|
9
|
+
# what is creating inputs and consumes output from the routers
|
10
|
+
class Interface
|
11
|
+
attr_reader :core
|
12
|
+
|
13
|
+
#
|
14
|
+
# Interface makes all neded configuration, plugin registrations,
|
15
|
+
# properties registration in the constructor, which requires core.
|
16
|
+
# Originally, there was thoughts of interfaces as top level building
|
17
|
+
# blocks for ecosystem, this is why interfaces are doing stuff with core
|
18
|
+
# inside constructors. It was originally thought that main application
|
19
|
+
# file will host like multiple interfaces.
|
20
|
+
# This is also why interfaces had no argument core in constructor,
|
21
|
+
# because they created cores inside.
|
22
|
+
#
|
23
|
+
# @param [Kanal::Core::Core] core <description>
|
24
|
+
#
|
25
|
+
def initialize(core)
|
26
|
+
@core = core
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Returns default router from core
|
31
|
+
#
|
32
|
+
# @return [Kanal::Core::Router::Router] <description>
|
33
|
+
#
|
34
|
+
def router
|
35
|
+
@core.router
|
36
|
+
end
|
37
|
+
|
38
|
+
def modify_core(&block)
|
39
|
+
block.call @core
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Starting the interface, all the needed
|
44
|
+
# machinery for it to fire up goes here
|
45
|
+
#
|
46
|
+
# @return [void] <description>
|
47
|
+
#
|
48
|
+
def start
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Yet to be discovered how to use this.
|
54
|
+
# If method #start executes some synchronous code, how would
|
55
|
+
# we stop it from outside? I mean maybe with some kind of flag variable inside?
|
56
|
+
#
|
57
|
+
# @return [<Type>] <description>
|
58
|
+
#
|
59
|
+
def stop
|
60
|
+
raise NotImplementedError
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../helpers/parameter_finder_with_method_missing_mixin"
|
4
|
+
require_relative "../helpers/parameter_bag_with_registrator"
|
5
|
+
|
6
|
+
module Kanal
|
7
|
+
module Core
|
8
|
+
module Output
|
9
|
+
# Base class for constructing output that will be given
|
10
|
+
# from router node
|
11
|
+
class Output
|
12
|
+
include Helpers
|
13
|
+
include Helpers::ParameterFinderWithMethodMissingMixin
|
14
|
+
|
15
|
+
attr_reader :input, :core
|
16
|
+
|
17
|
+
def initialize(parameter_registrator, input, core)
|
18
|
+
@input = input
|
19
|
+
@core = core
|
20
|
+
@parameter_bag = ParameterBagWithRegistrator.new parameter_registrator
|
21
|
+
end
|
22
|
+
|
23
|
+
def configure_dsl(&block)
|
24
|
+
instance_eval(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
private :core
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|