kanal 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|