kanal 0.3.0 → 0.4.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/CHANGELOG.md +10 -1
- data/Gemfile.lock +2 -2
- data/kanal.gemspec +1 -0
- data/lib/kanal/core/conditions/condition.rb +11 -1
- data/lib/kanal/core/conditions/condition_creator.rb +21 -2
- data/lib/kanal/core/conditions/condition_pack.rb +23 -3
- data/lib/kanal/core/conditions/condition_pack_creator.rb +17 -2
- data/lib/kanal/core/conditions/condition_storage.rb +18 -2
- data/lib/kanal/core/core.rb +19 -1
- data/lib/kanal/core/helpers/condition_finder.rb +3 -2
- data/lib/kanal/core/helpers/parameter_bag_with_registrator.rb +10 -0
- data/lib/kanal/core/helpers/parameter_finder_with_method_missing_mixin.rb +6 -8
- data/lib/kanal/core/helpers/parameter_registrator.rb +13 -1
- data/lib/kanal/core/helpers/queue.rb +38 -0
- data/lib/kanal/core/helpers/response_block.rb +20 -0
- data/lib/kanal/core/helpers/response_execution_block.rb +77 -0
- data/lib/kanal/core/helpers/router_proc_parser.rb +2 -0
- data/lib/kanal/core/hooks/hook_storage.rb +11 -4
- data/lib/kanal/core/interfaces/interface.rb +13 -0
- data/lib/kanal/core/logger/logging.rb +26 -0
- data/lib/kanal/core/output/output_creator.rb +3 -3
- data/lib/kanal/core/plugins/plugin.rb +1 -1
- data/lib/kanal/core/router/router.rb +104 -10
- data/lib/kanal/core/router/router_node.rb +21 -11
- data/lib/kanal/core/router/router_storage.rb +2 -0
- data/lib/kanal/core/services/service_container.rb +24 -7
- data/lib/kanal/plugins/batteries/attachments/attachment.rb +163 -0
- data/lib/kanal/plugins/batteries/batteries_plugin.rb +16 -0
- data/lib/kanal/version.rb +1 -1
- metadata +8 -3
- data/lib/kanal/core/logger/logger.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22224712e17434c60d3b437d44a35a5a997dd739cddcf53eb365d588fdf19fcd
|
4
|
+
data.tar.gz: f6677a4a8bc6eacf2e41469ba4a36da1b324d3b7d9092ae9271aa1b9f3fa8be6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e1416fb809513b53627cb120d8b09dfb76cae30f952de0212a1c2edde48e01d733afb122189230d0e6426dba812d143a8b2fc35b8561fba95954ea0b0bccbe2
|
7
|
+
data.tar.gz: 3dcc90d49fa16f4df0482d77a94f3f21a7ddc12742983a8925714199100284bfb4d84a96bef19aa289c3ad1ebcc21996d3a27fb9874837b24ca343ba53f700d6
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
-
## [0.
|
3
|
+
## [0.4.1] 2023-03-16
|
4
|
+
- Added Attachment that can be used inside new input parameters in Batteries plugin
|
5
|
+
- New input and output parameters in Batteries plugin: image, audio, file
|
6
|
+
|
7
|
+
## [0.4.0] 2023-03-10
|
8
|
+
- Added logging feature to Kanal, for a time being - just stdout
|
9
|
+
- Error response, now Kanal Router accepts error response block, when things go haywire inside constructing output
|
10
|
+
- Async response available, now developers can utilize respond_async(&block) which will be executed in separate thread
|
11
|
+
|
12
|
+
## [0.3.0] - 2023-01-10
|
4
13
|
- Kanal::Core::Core.get_plugin method added to get plugins for additional configuration by other plugins or developer code, if needed
|
5
14
|
- Kanal::Core::Plugins::Plugin.rake_tasks method introduced, for plugins to have their own rake tasks that can be merged inside
|
6
15
|
some kind of parent rake tasks, whether it's users Rakefile or kanal framework/interface or something Rakefile
|
data/Gemfile.lock
CHANGED
data/kanal.gemspec
CHANGED
@@ -1,15 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../logger/logging"
|
4
|
+
|
1
5
|
module Kanal
|
2
6
|
module Core
|
3
7
|
module Conditions
|
4
8
|
# Base class for conditions
|
5
9
|
# with this class you can
|
6
10
|
class Condition
|
11
|
+
include Logging
|
12
|
+
|
7
13
|
attr_reader :name
|
8
14
|
|
9
15
|
def initialize(name, with_argument: false, &met_block)
|
10
16
|
@name = name
|
11
17
|
|
12
|
-
|
18
|
+
unless met_block
|
19
|
+
logger.fatal "Attempted to create condition #{name} without block"
|
20
|
+
|
21
|
+
raise "Cannot create condition without block"
|
22
|
+
end
|
13
23
|
|
14
24
|
@with_argument = with_argument
|
15
25
|
|
@@ -1,9 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../logger/logging"
|
4
|
+
|
1
5
|
module Kanal
|
2
6
|
module Core
|
3
7
|
module Conditions
|
4
8
|
# This class helps creating conditions in dsl way,
|
5
9
|
# with using helper methods
|
6
10
|
class ConditionCreator
|
11
|
+
include Logging
|
12
|
+
|
7
13
|
def initialize(name)
|
8
14
|
@name = name
|
9
15
|
@met_block = nil
|
@@ -11,10 +17,23 @@ module Kanal
|
|
11
17
|
end
|
12
18
|
|
13
19
|
def create(&block)
|
20
|
+
logger.info "Attempting to create condition '#{@name}'"
|
21
|
+
|
14
22
|
instance_eval(&block)
|
15
23
|
|
16
|
-
|
17
|
-
|
24
|
+
unless @name
|
25
|
+
logger.fatal "Attempted to create condition without name"
|
26
|
+
|
27
|
+
raise "Please provide name for condition"
|
28
|
+
end
|
29
|
+
|
30
|
+
unless @met_block
|
31
|
+
logger.fatal "Attempted to create condition without met block"
|
32
|
+
|
33
|
+
raise "Please provide met? block for condition #{@name}"
|
34
|
+
end
|
35
|
+
|
36
|
+
logger.info "Creating condition '#{@name}'"
|
18
37
|
|
19
38
|
Condition.new @name, with_argument: @with_argument, &@met_block
|
20
39
|
end
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative "./condition"
|
2
4
|
require_relative "./condition_creator"
|
5
|
+
require_relative "../logger/logging"
|
3
6
|
|
4
7
|
module Kanal
|
5
8
|
module Core
|
@@ -8,6 +11,8 @@ module Kanal
|
|
8
11
|
# It is served as some kind of namespace for conditions, with specific
|
9
12
|
# name of pack and helper methods
|
10
13
|
class ConditionPack
|
14
|
+
include Logging
|
15
|
+
|
11
16
|
attr_reader :name
|
12
17
|
|
13
18
|
def initialize(name)
|
@@ -18,7 +23,11 @@ module Kanal
|
|
18
23
|
def get_condition_by_name!(name)
|
19
24
|
condition = get_condition_by_name name
|
20
25
|
|
21
|
-
|
26
|
+
unless condition
|
27
|
+
logger.fatal "Attempted to get condition #{name} in pack #{@name}"
|
28
|
+
|
29
|
+
raise "Condition #{name} was not found in pack #{@name}. Maybe it was not added?"
|
30
|
+
end
|
22
31
|
|
23
32
|
condition
|
24
33
|
end
|
@@ -28,9 +37,20 @@ module Kanal
|
|
28
37
|
end
|
29
38
|
|
30
39
|
def register_condition(condition)
|
31
|
-
|
40
|
+
logger.info "Attempting to register condition '#{condition.name}'"
|
41
|
+
|
42
|
+
unless condition.is_a? Condition
|
43
|
+
logger.fatal "Attempted to register condition which isn't of Condition class"
|
44
|
+
|
45
|
+
raise "Can register only conditions that inherit Condition class"
|
46
|
+
end
|
47
|
+
|
48
|
+
if condition_registered? condition
|
49
|
+
logger.warn "Condition '#{condition.name}' already registered"
|
50
|
+
return self
|
51
|
+
end
|
32
52
|
|
33
|
-
|
53
|
+
logger.info "Registering condition '#{condition.name}'"
|
34
54
|
|
35
55
|
@conditions.append condition
|
36
56
|
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative "./condition_pack"
|
2
4
|
require_relative "./condition_creator"
|
5
|
+
require_relative "../logger/logging"
|
3
6
|
|
4
7
|
module Kanal
|
5
8
|
module Core
|
@@ -7,6 +10,8 @@ module Kanal
|
|
7
10
|
# This class helps in condition pack creation
|
8
11
|
# with the help of dsl
|
9
12
|
class ConditionPackCreator
|
13
|
+
include Logging
|
14
|
+
|
10
15
|
TEMP_NAME = :temp_name
|
11
16
|
|
12
17
|
def initialize(name)
|
@@ -17,9 +22,19 @@ module Kanal
|
|
17
22
|
def create(&block)
|
18
23
|
instance_eval(&block)
|
19
24
|
|
20
|
-
|
25
|
+
unless @name
|
26
|
+
logger.fatal "Attempted to create condition pack without name"
|
27
|
+
|
28
|
+
raise "Please provide condition pack name"
|
29
|
+
end
|
30
|
+
|
31
|
+
if @conditions.empty?
|
32
|
+
logger.fatal "Attempted to create condition pack #{@name} without conditions provided"
|
33
|
+
|
34
|
+
raise "Please provide conditions for condition pack #{@name}"
|
35
|
+
end
|
21
36
|
|
22
|
-
|
37
|
+
logger.info "Creating condition pack '#{@name}'"
|
23
38
|
|
24
39
|
pack = ConditionPack.new(@name)
|
25
40
|
|
@@ -1,9 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../logger/logging"
|
4
|
+
|
1
5
|
module Kanal
|
2
6
|
module Core
|
3
7
|
module Conditions
|
4
8
|
# This class contains all needed functionality to store,
|
5
9
|
# search conditions
|
6
10
|
class ConditionStorage
|
11
|
+
include Logging
|
12
|
+
|
7
13
|
def initialize
|
8
14
|
@condition_packs = []
|
9
15
|
end
|
@@ -11,7 +17,11 @@ module Kanal
|
|
11
17
|
def get_condition_pack_by_name!(name)
|
12
18
|
pack = get_condition_pack_by_name name
|
13
19
|
|
14
|
-
|
20
|
+
unless pack
|
21
|
+
logger.fatal "Attempted to request unregistered condition pack #{name} from ConditionStorage"
|
22
|
+
|
23
|
+
raise "Condition pack #{name} is not registered, but was requested from ConditionStorage"
|
24
|
+
end
|
15
25
|
|
16
26
|
pack
|
17
27
|
end
|
@@ -44,7 +54,13 @@ module Kanal
|
|
44
54
|
def register_condition_pack(pack)
|
45
55
|
return if condition_pack_exists? pack
|
46
56
|
|
47
|
-
|
57
|
+
unless pack.is_a? ConditionPack
|
58
|
+
logger.fatal "Attempted to register condition pack which isn't of ConditionPack class"
|
59
|
+
|
60
|
+
raise "Condition pack should be descendant of ConditionPack class"
|
61
|
+
end
|
62
|
+
|
63
|
+
logger.info "Registering condition pack '#{pack.name}'"
|
48
64
|
|
49
65
|
@condition_packs.append pack
|
50
66
|
end
|
data/lib/kanal/core/core.rb
CHANGED
@@ -8,6 +8,8 @@ require_relative "./helpers/parameter_registrator"
|
|
8
8
|
require_relative "./plugins/plugin"
|
9
9
|
require_relative "./input/input"
|
10
10
|
require_relative "./services/service_container"
|
11
|
+
require_relative "./logger/logging"
|
12
|
+
|
11
13
|
|
12
14
|
module Kanal
|
13
15
|
module Core
|
@@ -34,6 +36,7 @@ module Kanal
|
|
34
36
|
include Plugins
|
35
37
|
include Hooks
|
36
38
|
include Services
|
39
|
+
include Logging
|
37
40
|
|
38
41
|
# @return [Kanal::Core::Conditions::ConditionStorage]
|
39
42
|
attr_reader :condition_storage
|
@@ -74,6 +77,8 @@ module Kanal
|
|
74
77
|
#
|
75
78
|
def register_plugin(plugin)
|
76
79
|
unless plugin.is_a? Plugin
|
80
|
+
logger.fatal "Attempted to register plugin that is not of type Kanal::Core::Plugin or a class that inherits base Plugin class"
|
81
|
+
|
77
82
|
raise "Plugin must be of type Kanal::Core::Plugin or be a class that inherits base Plugin class"
|
78
83
|
end
|
79
84
|
|
@@ -82,10 +87,16 @@ module Kanal
|
|
82
87
|
name = plugin.name
|
83
88
|
|
84
89
|
# TODO: _log that plugin already registered with such name
|
85
|
-
|
90
|
+
if !name.nil? && plugin_registered?(name)
|
91
|
+
logger.warn "Plugin '#{name}' already registered"
|
92
|
+
|
93
|
+
return
|
94
|
+
end
|
86
95
|
|
87
96
|
plugin.setup(self)
|
88
97
|
|
98
|
+
logger.info "Registering plugin '#{name}'"
|
99
|
+
|
89
100
|
@plugins.append plugin
|
90
101
|
# NOTE: Catching here Exception because metho.name can raise ScriptError (derived from Exception)
|
91
102
|
# and method .setup can raise ANY type of error.
|
@@ -101,6 +112,8 @@ module Kanal
|
|
101
112
|
end
|
102
113
|
|
103
114
|
# TODO: _log this info in critical error instead of raising exception
|
115
|
+
logger.fatal "There was a problem while registering plugin named: #{name}. Error: `#{e}`."
|
116
|
+
|
104
117
|
raise "There was a problem while registering plugin named: #{name}. Error: `#{e}`.
|
105
118
|
Remember, plugin errors are often due to .name method not overriden or
|
106
119
|
having faulty code inside .setup overriden method"
|
@@ -153,6 +166,8 @@ module Kanal
|
|
153
166
|
# @return [void] <description>
|
154
167
|
#
|
155
168
|
def register_input_parameter(name, readonly: false)
|
169
|
+
logger.info "Registering input parameter: '#{name}', readonly: '#{readonly}'"
|
170
|
+
|
156
171
|
@input_parameter_registrator.register_parameter name, readonly: readonly
|
157
172
|
end
|
158
173
|
|
@@ -165,6 +180,7 @@ module Kanal
|
|
165
180
|
# @return [void] <description>
|
166
181
|
#
|
167
182
|
def register_output_parameter(name, readonly: false)
|
183
|
+
logger.info "Registering output parameter: '#{name}', readonly: '#{readonly}'"
|
168
184
|
@output_parameter_registrator.register_parameter name, readonly: readonly
|
169
185
|
end
|
170
186
|
|
@@ -179,6 +195,8 @@ module Kanal
|
|
179
195
|
# @return [void] <description>
|
180
196
|
#
|
181
197
|
def add_condition_pack(name, &block)
|
198
|
+
logger.info "Starting to create condition pack '#{name}'"
|
199
|
+
|
182
200
|
creator = ConditionPackCreator.new name
|
183
201
|
|
184
202
|
pack = creator.create(&block)
|
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative "parameter_bag"
|
4
|
+
require_relative "../logger/logging"
|
2
5
|
|
3
6
|
module Kanal
|
4
7
|
module Core
|
@@ -7,6 +10,8 @@ module Kanal
|
|
7
10
|
# and if they are has needed by registrator allowances, types etc, whatever
|
8
11
|
# registrator rules are stored for property
|
9
12
|
class ParameterBagWithRegistrator < ParameterBag
|
13
|
+
include Logging
|
14
|
+
|
10
15
|
def initialize(registrator)
|
11
16
|
super()
|
12
17
|
@registrator = registrator
|
@@ -27,6 +32,9 @@ module Kanal
|
|
27
32
|
value_exists = !get(name).nil?
|
28
33
|
|
29
34
|
if value_exists
|
35
|
+
logger.fatal "Parameter #{name} is marked readonly! Attempted to set it's value, but
|
36
|
+
it already has value."
|
37
|
+
|
30
38
|
raise "Parameter #{name} is marked readonly! You tried to set it's value, but
|
31
39
|
it already has value."
|
32
40
|
end
|
@@ -37,6 +45,8 @@ module Kanal
|
|
37
45
|
|
38
46
|
def validate_parameter_registration(name)
|
39
47
|
unless @registrator.parameter_registered? name
|
48
|
+
logger.fatal "Parameter #{name} was not registered! Did you forget to register that parameter?"
|
49
|
+
|
40
50
|
raise "Parameter #{name} was not registered! Did you forget to register that parameter?"
|
41
51
|
end
|
42
52
|
end
|
@@ -18,18 +18,16 @@ module Kanal
|
|
18
18
|
# input.prop = 123
|
19
19
|
if symbol.to_s.include? "="
|
20
20
|
@parameter_bag.set parameter_name, args.first
|
21
|
-
|
21
|
+
elsif !args.empty?
|
22
22
|
# this approach can be used also in dsl
|
23
23
|
# like that
|
24
24
|
# setters: prop value
|
25
25
|
# getters: prop
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@parameter_bag.get parameter_name
|
32
|
-
end
|
26
|
+
@parameter_bag.set(parameter_name, *args)
|
27
|
+
# means it is used as setter in dsl,
|
28
|
+
# method call with argument
|
29
|
+
else
|
30
|
+
@parameter_bag.get parameter_name
|
33
31
|
end
|
34
32
|
end
|
35
33
|
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../logger/logging"
|
4
|
+
|
1
5
|
module Kanal
|
2
6
|
module Core
|
3
7
|
module Helpers
|
@@ -17,6 +21,8 @@ module Kanal
|
|
17
21
|
# Class holds parameter names that are allowed
|
18
22
|
# to be used.
|
19
23
|
class ParameterRegistrator
|
24
|
+
include Logging
|
25
|
+
|
20
26
|
def initialize
|
21
27
|
@parameters_by_name = {}
|
22
28
|
end
|
@@ -25,7 +31,13 @@ module Kanal
|
|
25
31
|
# be changed. handy for input parameters populated by interface or
|
26
32
|
# whatever
|
27
33
|
def register_parameter(name, readonly: false)
|
28
|
-
|
34
|
+
if @parameters_by_name.key? name
|
35
|
+
logger.fatal "Attempted to register already registered parameter '#{name}'"
|
36
|
+
|
37
|
+
raise "Parameter named #{name} already registered!"
|
38
|
+
end
|
39
|
+
|
40
|
+
logger.info "Registering parameter '#{name}'"
|
29
41
|
|
30
42
|
registration = ParameterRegistration.new readonly
|
31
43
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../hooks/hook_storage"
|
4
|
+
|
5
|
+
module Kanal
|
6
|
+
module Core
|
7
|
+
module Helpers
|
8
|
+
class Queue
|
9
|
+
include Hooks
|
10
|
+
|
11
|
+
attr_reader :hooks
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@items = []
|
15
|
+
@hooks = HookStorage.new
|
16
|
+
hooks.register(:item_queued) # args arguments: item
|
17
|
+
end
|
18
|
+
|
19
|
+
def enqueue(element)
|
20
|
+
@items.append element
|
21
|
+
@hooks.call :item_queued, element
|
22
|
+
end
|
23
|
+
|
24
|
+
def dequeue
|
25
|
+
@items.shift
|
26
|
+
end
|
27
|
+
|
28
|
+
def empty?
|
29
|
+
@items.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
def remove(element)
|
33
|
+
@items.delete(element)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kanal
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
class ResponseBlock
|
7
|
+
attr_reader :block
|
8
|
+
|
9
|
+
def initialize(block, async: false)
|
10
|
+
@block = block
|
11
|
+
@async = async
|
12
|
+
end
|
13
|
+
|
14
|
+
def async?
|
15
|
+
@async
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../output/output"
|
4
|
+
require_relative "../logger/logging"
|
5
|
+
|
6
|
+
module Kanal
|
7
|
+
module Core
|
8
|
+
module Helpers
|
9
|
+
class ResponseExecutionBlock
|
10
|
+
include Output
|
11
|
+
include Logging
|
12
|
+
|
13
|
+
attr_reader :response_block, :input
|
14
|
+
|
15
|
+
def initialize(response_block, input, default_error_node, error_node)
|
16
|
+
@response_block = response_block
|
17
|
+
@input = input
|
18
|
+
@default_error_node = default_error_node
|
19
|
+
@error_node = error_node
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute(core, output_queue)
|
23
|
+
if response_block.async?
|
24
|
+
# NOTE: Thread doesnt just die here - it's execution is continued in output_queue.enqueue in router
|
25
|
+
# then :item_queued hook is called inside and subsequently output_ready_block gets called in this thread
|
26
|
+
# TODO: be aware that this can cause unexpected behaviour. Maybe think how to rework it.
|
27
|
+
Thread.new do
|
28
|
+
output_queue.enqueue construct_output(core)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
output_queue.enqueue construct_output(core)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def construct_output(core)
|
38
|
+
logger.info "Constructing output for input ##{input.__id__}"
|
39
|
+
|
40
|
+
output = Output::Output.new core.output_parameter_registrator, input, core
|
41
|
+
|
42
|
+
begin
|
43
|
+
output.instance_eval(&@response_block.block)
|
44
|
+
|
45
|
+
core.hooks.call :output_before_returned, input, output
|
46
|
+
rescue => e
|
47
|
+
logger.error "Failed to construct output for input ##{input.__id__}. Error: '#{e}'"
|
48
|
+
|
49
|
+
output = Output::Output.new core.output_parameter_registrator, input, core
|
50
|
+
|
51
|
+
error_node = @error_node || @default_error_node
|
52
|
+
|
53
|
+
logger.info "Trying to construct error response for input ##{input.__id__}. Error response is default: #{@error_node.nil?}"
|
54
|
+
|
55
|
+
begin
|
56
|
+
output.instance_eval(&error_node.response_blocks.first.block)
|
57
|
+
|
58
|
+
core.hooks.call :output_before_returned, input, output
|
59
|
+
rescue => e
|
60
|
+
logger.error "Failed to construct error response for input ##{input.__id__}. Error: '#{e}'"
|
61
|
+
|
62
|
+
logger.info "Trying to construct default error response for input ##{input.__id__}"
|
63
|
+
|
64
|
+
output.instance_eval(&@default_error_node.response_blocks.first.block)
|
65
|
+
|
66
|
+
core.hooks.call :output_before_returned, input, output
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
logger.info "Output ##{output.__id__} for input ##{input.__id__} constructed"
|
71
|
+
|
72
|
+
output
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "../logger/logging"
|
4
|
+
|
3
5
|
module Kanal
|
4
6
|
module Core
|
5
7
|
module Hooks
|
@@ -8,6 +10,8 @@ module Kanal
|
|
8
10
|
# attaching to hooks, calling hooks with arguments
|
9
11
|
#
|
10
12
|
class HookStorage
|
13
|
+
include Logging
|
14
|
+
|
11
15
|
def initialize
|
12
16
|
@listeners = {}
|
13
17
|
end
|
@@ -29,7 +33,12 @@ module Kanal
|
|
29
33
|
# Wondrous world of dynamic languages 🌈🦄
|
30
34
|
#
|
31
35
|
def register(name)
|
32
|
-
|
36
|
+
if hook_exists? name
|
37
|
+
logger.warn "Hook '#{name}' already exists"
|
38
|
+
return
|
39
|
+
end
|
40
|
+
|
41
|
+
logger.info "Registering hook '#{name}'"
|
33
42
|
|
34
43
|
@listeners[name] = []
|
35
44
|
end
|
@@ -81,9 +90,7 @@ module Kanal
|
|
81
90
|
# @return [void] <description>
|
82
91
|
#
|
83
92
|
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
|
93
|
+
raise "You cannot listen to hook that does not exist! Hook in question: #{name}" unless hook_exists? name
|
87
94
|
|
88
95
|
proc_to_lambda_object = Object.new
|
89
96
|
proc_to_lambda_object.define_singleton_method(:hook_block, &block)
|
@@ -24,6 +24,11 @@ module Kanal
|
|
24
24
|
#
|
25
25
|
def initialize(core)
|
26
26
|
@core = core
|
27
|
+
|
28
|
+
_this = self
|
29
|
+
@core.router.output_ready do |output|
|
30
|
+
_this.consume_output output
|
31
|
+
end
|
27
32
|
end
|
28
33
|
|
29
34
|
#
|
@@ -59,6 +64,14 @@ module Kanal
|
|
59
64
|
def stop
|
60
65
|
raise NotImplementedError
|
61
66
|
end
|
67
|
+
|
68
|
+
def consume_input(input)
|
69
|
+
@core.router.consume_input input
|
70
|
+
end
|
71
|
+
|
72
|
+
def consume_output(output)
|
73
|
+
raise NotImplementedError
|
74
|
+
end
|
62
75
|
end
|
63
76
|
end
|
64
77
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
|
5
|
+
module Kanal
|
6
|
+
module Core
|
7
|
+
module Logging
|
8
|
+
# Logger instance will be saved inside class itself for future calls
|
9
|
+
@logger_instance
|
10
|
+
|
11
|
+
# Mixing in Logger in some class adds possibility to use logger instance method
|
12
|
+
def logger
|
13
|
+
@logger_instance ||= Logging.create_logger self.class.name
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def create_logger(class_name)
|
18
|
+
logger = Logger.new STDOUT
|
19
|
+
logger.progname = class_name.rpartition(':').last
|
20
|
+
logger.datetime_format = "%d-%m-%Y %H:%M:%S"
|
21
|
+
logger
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "./router_node"
|
4
|
+
require_relative "../helpers/queue"
|
5
|
+
require_relative "../helpers/response_execution_block"
|
6
|
+
require_relative "../logger/logging"
|
4
7
|
|
5
8
|
module Kanal
|
6
9
|
module Core
|
@@ -10,17 +13,50 @@ module Kanal
|
|
10
13
|
# namespace. Basically class router stores all the
|
11
14
|
# router nodes and have a name.
|
12
15
|
class Router
|
13
|
-
|
14
|
-
|
16
|
+
include Helpers
|
17
|
+
include Logging
|
18
|
+
|
19
|
+
attr_reader :name, :core, :output_ready_block
|
15
20
|
|
16
21
|
def initialize(name, core)
|
22
|
+
logger.info "Initializing"
|
23
|
+
|
17
24
|
@name = name
|
18
25
|
@core = core
|
19
26
|
@root_node = nil
|
20
27
|
@default_node = nil
|
28
|
+
@default_error_node = nil
|
29
|
+
default_error_response do
|
30
|
+
if core.plugin_registered? :batteries
|
31
|
+
body "Unfortunately, error happened. Please consider contacting the creator of this bot to provide information about the circumstances of this error."
|
32
|
+
else
|
33
|
+
raise "Error occurred and there is no way to inform end user about it. You can override error response with router.error_response method or register :batteries plugin so default response will populate the .body output parameter"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
@error_node = nil
|
37
|
+
@response_execution_queue = Queue.new
|
38
|
+
@output_queue = Queue.new
|
39
|
+
@output_ready_block = nil
|
40
|
+
@core.hooks.register(:output_ready) # arg
|
41
|
+
|
42
|
+
_this = self
|
43
|
+
_output_queue = @output_queue
|
44
|
+
@output_queue.hooks.attach :item_queued do |output|
|
45
|
+
_this.logger.info "Calling output_ready block for input ##{output.input.__id__} and output #{output.__id__}. Output body is: '#{output.body}'"
|
46
|
+
|
47
|
+
begin
|
48
|
+
_this.output_ready_block.call output
|
49
|
+
_output_queue.remove(output)
|
50
|
+
rescue
|
51
|
+
_output_queue.remove(output)
|
52
|
+
raise "Error in output_ready block!"
|
53
|
+
end
|
54
|
+
end
|
21
55
|
end
|
22
56
|
|
23
57
|
def configure(&block)
|
58
|
+
logger.info "Configuring"
|
59
|
+
|
24
60
|
# Root node does not have parent
|
25
61
|
@root_node ||= RouterNode.new router: self, parent: nil, root: true
|
26
62
|
|
@@ -28,24 +64,56 @@ module Kanal
|
|
28
64
|
end
|
29
65
|
|
30
66
|
def default_response(&block)
|
31
|
-
|
67
|
+
logger.info "Setting default response"
|
68
|
+
|
69
|
+
if @default_node
|
70
|
+
logger.fatal "Attempted to set default_response for a second time"
|
71
|
+
|
72
|
+
raise "default node for router #{@name} already defined"
|
73
|
+
end
|
32
74
|
|
33
75
|
@default_node = RouterNode.new parent: nil, router: self, default: true
|
34
76
|
|
35
77
|
@default_node.respond(&block)
|
36
78
|
end
|
37
79
|
|
38
|
-
|
39
|
-
|
80
|
+
def error_response(&block)
|
81
|
+
raise "error node for router #{@name} already defined" if @error_node
|
82
|
+
|
83
|
+
@error_node = RouterNode.new parent: nil, router: self, error: true
|
84
|
+
|
85
|
+
@error_node.respond(&block)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Main method for creating output(s) if it is found or going to default output
|
89
|
+
def consume_input(input)
|
90
|
+
logger.info "Consuming input #{input.__id__}."
|
91
|
+
|
40
92
|
# Checking if default node with output exists throw error if not
|
41
|
-
|
93
|
+
unless @default_node
|
94
|
+
logger.fatal "Attempted to consume input with no default response set"
|
95
|
+
|
96
|
+
raise "Please provide default response for router before you try and throw input against it ;)"
|
97
|
+
end
|
98
|
+
|
99
|
+
unless @root_node
|
100
|
+
logger.fatal "Attempted to consume input but router is not configured"
|
42
101
|
|
43
|
-
|
102
|
+
raise "You did not actually .configure router, didn't you? There is no even root node! Use .configure method"
|
103
|
+
end
|
44
104
|
|
45
105
|
unless @root_node.children?
|
106
|
+
logger.fatal "Attempted to consume input but router does not have any routes"
|
107
|
+
|
46
108
|
raise "Hey your router actually does not have ANY routes to work with. Did you even try adding them?"
|
47
109
|
end
|
48
110
|
|
111
|
+
unless @output_ready_block
|
112
|
+
logger.fatal "Attempted to consume input but output_ready block is not set"
|
113
|
+
|
114
|
+
raise "You must provide block via .output_ready for router to function properly"
|
115
|
+
end
|
116
|
+
|
49
117
|
@core.hooks.call :input_before_router, input
|
50
118
|
|
51
119
|
node = test_input_against_router_node input, @root_node
|
@@ -54,11 +122,31 @@ module Kanal
|
|
54
122
|
# using default response
|
55
123
|
node ||= @default_node
|
56
124
|
|
57
|
-
|
125
|
+
response_blocks = node.response_blocks
|
126
|
+
|
127
|
+
error_node = @error_node || @default_error_node
|
128
|
+
|
129
|
+
response_execution_blocks = response_blocks.map { |rb| ResponseExecutionBlock.new rb, input, @default_error_node, @error_node }
|
130
|
+
|
131
|
+
response_execution_blocks.each do |reb|
|
132
|
+
@response_execution_queue.enqueue reb
|
133
|
+
end
|
134
|
+
|
135
|
+
process_response_execution_queue
|
136
|
+
end
|
58
137
|
|
59
|
-
|
138
|
+
def process_response_execution_queue
|
139
|
+
until @response_execution_queue.empty?
|
140
|
+
response_execution = @response_execution_queue.dequeue
|
60
141
|
|
61
|
-
|
142
|
+
response_execution.execute core, @output_queue
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def output_ready(&block)
|
147
|
+
logger.info "Setting output_ready block"
|
148
|
+
|
149
|
+
@output_ready_block = block
|
62
150
|
end
|
63
151
|
|
64
152
|
# Recursive method for searching router nodes
|
@@ -91,6 +179,12 @@ module Kanal
|
|
91
179
|
end
|
92
180
|
|
93
181
|
private :test_input_against_router_node
|
182
|
+
|
183
|
+
def default_error_response(&block)
|
184
|
+
@default_error_node = RouterNode.new parent: nil, router: self, error: true
|
185
|
+
|
186
|
+
@default_error_node.respond(&block)
|
187
|
+
end
|
94
188
|
end
|
95
189
|
end
|
96
190
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "../output/output"
|
4
|
+
require_relative "../helpers/response_block"
|
5
|
+
require_relative "../logger/logging"
|
4
6
|
|
5
7
|
module Kanal
|
6
8
|
module Core
|
@@ -11,6 +13,8 @@ module Kanal
|
|
11
13
|
# tree, containing conditions and responses
|
12
14
|
class RouterNode
|
13
15
|
include Output
|
16
|
+
include Helpers
|
17
|
+
include Logging
|
14
18
|
|
15
19
|
attr_reader :parent,
|
16
20
|
:children
|
@@ -18,13 +22,13 @@ module Kanal
|
|
18
22
|
# parameter default: is for knowing that this node
|
19
23
|
# is for default response
|
20
24
|
# default response cannot have child nodes
|
21
|
-
def initialize(*args, router:, parent:, default: false, root: false)
|
25
|
+
def initialize(*args, router:, parent:, default: false, root: false, error: false)
|
22
26
|
@router = router
|
23
27
|
@parent = parent
|
24
28
|
|
25
29
|
@children = []
|
26
30
|
|
27
|
-
@
|
31
|
+
@response_blocks = []
|
28
32
|
|
29
33
|
@condition_pack_name = nil
|
30
34
|
@condition_name = nil
|
@@ -32,7 +36,7 @@ module Kanal
|
|
32
36
|
|
33
37
|
# We omit setting conditions because default router node does not need any conditions
|
34
38
|
# Also root node does not have conditions so we basically omit them if arguments are empty
|
35
|
-
return if default || root
|
39
|
+
return if default || root || error
|
36
40
|
|
37
41
|
# With this we attach names of condition pack and condition to this router
|
38
42
|
# node, so we will be able to find them later at runtime and use them
|
@@ -48,24 +52,28 @@ module Kanal
|
|
48
52
|
child.instance_eval(&block)
|
49
53
|
end
|
50
54
|
|
51
|
-
def
|
52
|
-
|
55
|
+
def response_blocks
|
56
|
+
if @response_blocks.empty?
|
57
|
+
raise "no response block configured for this node. router: #{@router.name}. debug: #{debug_info}"
|
58
|
+
end
|
53
59
|
|
54
|
-
|
60
|
+
@response_blocks
|
61
|
+
end
|
55
62
|
|
56
|
-
|
63
|
+
def respond(&block)
|
64
|
+
raise "Router node with children cannot have response" unless @children.empty?
|
57
65
|
|
58
|
-
|
66
|
+
@response_blocks.append ResponseBlock.new(block)
|
59
67
|
end
|
60
68
|
|
61
|
-
def
|
69
|
+
def respond_async(&block)
|
62
70
|
raise "Router node with children cannot have response" unless @children.empty?
|
63
71
|
|
64
|
-
@
|
72
|
+
@response_blocks.append ResponseBlock.new(block, async: true)
|
65
73
|
end
|
66
74
|
|
67
75
|
def response?
|
68
|
-
!@
|
76
|
+
!@response_blocks.empty?
|
69
77
|
end
|
70
78
|
|
71
79
|
# This method processes args to populate condition and condition pack
|
@@ -88,6 +96,8 @@ module Kanal
|
|
88
96
|
condition = pack.get_condition_by_name! condition_name
|
89
97
|
|
90
98
|
if condition.with_argument? && !@condition_argument
|
99
|
+
logger.fatal "Condition requires argument, but was provided as :symbol, not as positional_arg:"
|
100
|
+
|
91
101
|
raise "Condition requires argument, though you wrote it as :symbol, not as positional_arg:
|
92
102
|
Please check route with condition pack: #{condition_pack_name} and condition: #{condition_name}"
|
93
103
|
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../logger/logging"
|
4
|
+
|
1
5
|
module Kanal
|
2
6
|
module Core
|
3
7
|
module Services
|
@@ -24,6 +28,8 @@ module Kanal
|
|
24
28
|
# lifespan types.
|
25
29
|
#
|
26
30
|
class ServiceContainer
|
31
|
+
include Logging
|
32
|
+
|
27
33
|
TYPE_SINGLETON = :singleton
|
28
34
|
TYPE_TRANSIENT = :transient
|
29
35
|
|
@@ -45,12 +51,24 @@ module Kanal
|
|
45
51
|
# @return [void] <description>
|
46
52
|
#
|
47
53
|
def register_service(name, service_class, type: TYPE_SINGLETON, &block)
|
48
|
-
|
54
|
+
logger.info "Trying to register service '#{name}'"
|
49
55
|
|
50
|
-
|
56
|
+
if @registrations.key? name
|
57
|
+
logger.warn "Attempted to register service '#{name}', but it is already registered"
|
58
|
+
|
59
|
+
return
|
60
|
+
end
|
61
|
+
|
62
|
+
unless allowed_types.include? type
|
63
|
+
logger.fatal "Attempted to register service type '#{type}'."
|
64
|
+
|
65
|
+
raise "Unrecognized service type #{type}. Allowed types: #{allowed_types}"
|
66
|
+
end
|
51
67
|
|
52
68
|
registration = ServiceRegistration.new service_class, type, block
|
53
69
|
|
70
|
+
logger.info "Registering service '#{name}'"
|
71
|
+
|
54
72
|
@registrations[name] = registration
|
55
73
|
end
|
56
74
|
|
@@ -66,14 +84,13 @@ module Kanal
|
|
66
84
|
|
67
85
|
registration = @registrations[name]
|
68
86
|
|
69
|
-
|
87
|
+
case registration.type
|
88
|
+
when TYPE_SINGLETON
|
70
89
|
# Created once and reused after creation
|
71
|
-
if @services[name].nil?
|
72
|
-
@services[name] = create_service_from_registration registration
|
73
|
-
end
|
90
|
+
@services[name] = create_service_from_registration registration if @services[name].nil?
|
74
91
|
|
75
92
|
@services[name]
|
76
|
-
|
93
|
+
when TYPE_TRANSIENT
|
77
94
|
# Created every time
|
78
95
|
create_service_from_registration registration
|
79
96
|
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "uri"
|
5
|
+
require 'open-uri'
|
6
|
+
|
7
|
+
module Kanal
|
8
|
+
module Plugins
|
9
|
+
module Batteries
|
10
|
+
module Attachments
|
11
|
+
class Attachment
|
12
|
+
attr_reader :url
|
13
|
+
|
14
|
+
def initialize(url)
|
15
|
+
@url = url
|
16
|
+
end
|
17
|
+
|
18
|
+
def audio?
|
19
|
+
mp3? || wav? || ogg?
|
20
|
+
end
|
21
|
+
|
22
|
+
def mp3?
|
23
|
+
extension == "mp3"
|
24
|
+
end
|
25
|
+
|
26
|
+
def wav?
|
27
|
+
extension == "wav"
|
28
|
+
end
|
29
|
+
|
30
|
+
def ogg?
|
31
|
+
extension == "ogg"
|
32
|
+
end
|
33
|
+
|
34
|
+
def document?
|
35
|
+
doc? || docx? || odf?
|
36
|
+
end
|
37
|
+
|
38
|
+
def doc?
|
39
|
+
extension == "doc"
|
40
|
+
end
|
41
|
+
|
42
|
+
def docx?
|
43
|
+
extension == "docx"
|
44
|
+
end
|
45
|
+
|
46
|
+
def odf?
|
47
|
+
extension == "odf"
|
48
|
+
end
|
49
|
+
|
50
|
+
def image?
|
51
|
+
jpg? || jpeg? || png? || bpm? || gif?
|
52
|
+
end
|
53
|
+
|
54
|
+
def jpg?
|
55
|
+
extension == "jpg"
|
56
|
+
end
|
57
|
+
|
58
|
+
def jpeg?
|
59
|
+
extension == "jpeg"
|
60
|
+
end
|
61
|
+
|
62
|
+
def png?
|
63
|
+
extension == "png"
|
64
|
+
end
|
65
|
+
|
66
|
+
def bpm?
|
67
|
+
extension == "bpm"
|
68
|
+
end
|
69
|
+
|
70
|
+
def gif?
|
71
|
+
extension == "gif"
|
72
|
+
end
|
73
|
+
|
74
|
+
def video?
|
75
|
+
mp4? || mov? || mkv?
|
76
|
+
end
|
77
|
+
|
78
|
+
def mp4?
|
79
|
+
extension == "mp4"
|
80
|
+
end
|
81
|
+
|
82
|
+
def mov?
|
83
|
+
extension == "mov"
|
84
|
+
end
|
85
|
+
|
86
|
+
def mkv?
|
87
|
+
extension == "mkv"
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Method that returns extension of url file if possible
|
92
|
+
# For example calling extension https://123.txt?something=1 will return 'txt'
|
93
|
+
#
|
94
|
+
# @return [String, nil]
|
95
|
+
#
|
96
|
+
def extension
|
97
|
+
uri = URI.parse(@url)
|
98
|
+
return nil if uri.path.nil?
|
99
|
+
|
100
|
+
File.extname(uri.path).split(".").last if File.basename(uri.path).include? "."
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Saves file to specified path. End user provides full filepath.
|
105
|
+
#
|
106
|
+
# @param [String] <Full filepath>
|
107
|
+
# @param [Boolean] <Should directories be created or not>
|
108
|
+
#
|
109
|
+
# @return [void]
|
110
|
+
#
|
111
|
+
def save(filepath, create_dirs = false)
|
112
|
+
stream = URI.open(@url)
|
113
|
+
|
114
|
+
save_stream_to_file stream, filepath, create_dirs
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# Saves file. End user provides directory only. Filename gets generated, extension is read from url.
|
119
|
+
#
|
120
|
+
# @param [String] <Directory>
|
121
|
+
# @param [Boolean] <Should directories be created or not>
|
122
|
+
# @param [Integer] <Length of filename to generate>
|
123
|
+
#
|
124
|
+
# @return [String] Full filepath to saved file
|
125
|
+
#
|
126
|
+
def quick_save(directory, create_dir = false, filename_length = 32)
|
127
|
+
filename = generate_filename filename_length, extension
|
128
|
+
|
129
|
+
return quick_save directory, create_dir, filename_length if File.exist? filename
|
130
|
+
|
131
|
+
save directory + filename, create_dir
|
132
|
+
|
133
|
+
directory + filename
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def generate_filename(filename_length, extension = nil)
|
139
|
+
alphanumeric = "abcdefghijkmnopQRSTUVWNXYZW12345676789-".chars
|
140
|
+
|
141
|
+
name = ""
|
142
|
+
|
143
|
+
filename_length.times do
|
144
|
+
name += alphanumeric.sample
|
145
|
+
end
|
146
|
+
|
147
|
+
extension ? "#{name}.#{extension}" : name
|
148
|
+
end
|
149
|
+
|
150
|
+
def save_stream_to_file(stream, filepath, create_dirs)
|
151
|
+
raise "File with that name already exists!" if File.exist? filepath
|
152
|
+
|
153
|
+
if create_dirs == true
|
154
|
+
FileUtils.mkdir_p(File.dirname(filepath)) unless File.directory?(File.dirname(filepath))
|
155
|
+
end
|
156
|
+
|
157
|
+
IO.copy_stream stream, filepath
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -16,6 +16,8 @@ module Kanal
|
|
16
16
|
source_batteries core
|
17
17
|
body_batteries core
|
18
18
|
flow_batteries core
|
19
|
+
attachments_batteries core
|
20
|
+
reply_markup_batteries core
|
19
21
|
end
|
20
22
|
|
21
23
|
def flow_batteries(core)
|
@@ -110,6 +112,20 @@ module Kanal
|
|
110
112
|
end
|
111
113
|
end
|
112
114
|
end
|
115
|
+
|
116
|
+
def attachments_batteries(core)
|
117
|
+
core.register_input_parameter :image
|
118
|
+
core.register_input_parameter :audio
|
119
|
+
core.register_input_parameter :file
|
120
|
+
|
121
|
+
core.register_output_parameter :image
|
122
|
+
core.register_output_parameter :audio
|
123
|
+
core.register_output_parameter :file
|
124
|
+
end
|
125
|
+
|
126
|
+
def reply_markup_batteries(core)
|
127
|
+
core.register_output_parameter :reply_markup
|
128
|
+
end
|
113
129
|
end
|
114
130
|
end
|
115
131
|
end
|
data/lib/kanal/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kanal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- idchlife
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Thanks to the core library, ecosystem of Kanal tools can be extendted
|
14
14
|
to use with input-output bot-like behaviour, with routing
|
@@ -42,11 +42,14 @@ files:
|
|
42
42
|
- lib/kanal/core/helpers/parameter_bag_with_registrator.rb
|
43
43
|
- lib/kanal/core/helpers/parameter_finder_with_method_missing_mixin.rb
|
44
44
|
- lib/kanal/core/helpers/parameter_registrator.rb
|
45
|
+
- lib/kanal/core/helpers/queue.rb
|
46
|
+
- lib/kanal/core/helpers/response_block.rb
|
47
|
+
- lib/kanal/core/helpers/response_execution_block.rb
|
45
48
|
- lib/kanal/core/helpers/router_proc_parser.rb
|
46
49
|
- lib/kanal/core/hooks/hook_storage.rb
|
47
50
|
- lib/kanal/core/input/input.rb
|
48
51
|
- lib/kanal/core/interfaces/interface.rb
|
49
|
-
- lib/kanal/core/logger/
|
52
|
+
- lib/kanal/core/logger/logging.rb
|
50
53
|
- lib/kanal/core/output/output.rb
|
51
54
|
- lib/kanal/core/output/output_creator.rb
|
52
55
|
- lib/kanal/core/plugins/plugin.rb
|
@@ -55,6 +58,7 @@ files:
|
|
55
58
|
- lib/kanal/core/router/router_storage.rb
|
56
59
|
- lib/kanal/core/services/service_container.rb
|
57
60
|
- lib/kanal/interfaces/simple_cli/simple_cli_interface.rb
|
61
|
+
- lib/kanal/plugins/batteries/attachments/attachment.rb
|
58
62
|
- lib/kanal/plugins/batteries/batteries_plugin.rb
|
59
63
|
- lib/kanal/version.rb
|
60
64
|
- sig/kanal.rbs
|
@@ -68,6 +72,7 @@ metadata:
|
|
68
72
|
homepage_uri: https://idchlife.github.io/kanal-documentation/
|
69
73
|
source_code_uri: https://github.com/idchlife/kanal
|
70
74
|
changelog_uri: https://github.com/idchlife/kanal/CHANGELOG.md
|
75
|
+
rubygems_mfa_required: 'true'
|
71
76
|
post_install_message:
|
72
77
|
rdoc_options: []
|
73
78
|
require_paths:
|