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