skn_utils 5.4.0 → 5.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.rspec +2 -0
- data/README.md +223 -72
- data/_config.yml +4 -4
- data/bin/concurrent_test_block +54 -0
- data/bin/concurrent_test_grouped +45 -0
- data/bin/concurrent_test_procs +45 -0
- data/bin/concurrent_test_wrapped +49 -0
- data/lib/skn_container.rb +2 -1
- data/lib/skn_failure.rb +2 -0
- data/lib/skn_hash.rb +2 -0
- data/lib/skn_registry.rb +25 -5
- data/lib/skn_settings.rb +2 -0
- data/lib/skn_success.rb +2 -0
- data/lib/skn_utils.rb +13 -2
- data/lib/skn_utils/concurrent_jobs.rb +117 -0
- data/lib/skn_utils/configurable.rb +55 -6
- data/lib/skn_utils/configuration.rb +2 -0
- data/lib/skn_utils/core_extensions.rb +29 -0
- data/lib/skn_utils/dotted_hash.rb +1 -0
- data/lib/skn_utils/env_string_handler.rb +2 -0
- data/lib/skn_utils/http_processor.rb +34 -0
- data/lib/skn_utils/job_commands.rb +247 -0
- data/lib/skn_utils/nested_result.rb +2 -0
- data/lib/skn_utils/notifier_base.rb +2 -0
- data/lib/skn_utils/null_object.rb +2 -0
- data/lib/skn_utils/page_controls.rb +2 -0
- data/lib/skn_utils/result_bean.rb +2 -0
- data/lib/skn_utils/version.rb +3 -1
- data/lib/skn_utils/wrappable.rb +32 -0
- data/skn_utils.gemspec +27 -22
- data/spec/lib/skn_utils/concurrent_jobs_spec.rb +279 -0
- data/spec/lib/skn_utils/configurable_spec.rb +23 -36
- data/spec/lib/skn_utils/registry_spec.rb +22 -0
- data/spec/lib/skn_utils/wrappers_spec.rb +80 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/configurables.rb +36 -0
- data/spec/support/xml_matchers.rb +121 -0
- metadata +71 -24
- data/README.rdoc +0 -379
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# file: concurrent_test_grouped
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'bundler/setup'
|
7
|
+
require 'skn_utils'
|
8
|
+
|
9
|
+
|
10
|
+
# ##
|
11
|
+
# MainLine
|
12
|
+
# ##
|
13
|
+
#
|
14
|
+
begin
|
15
|
+
# CommandJSONPost, CommandFORMGet, CommandJSONGet,
|
16
|
+
# CommandJSONPut, CommandFORMDelete
|
17
|
+
commands = [
|
18
|
+
SknUtils::CommandJSONGet.call(full_url: "http://jsonplaceholder.typicode.com/posts"),
|
19
|
+
SknUtils::CommandJSONGet.call(full_url: "https://jsonplaceholder.typicode.com/comments"),
|
20
|
+
SknUtils::CommandJSONGet.call(full_url: "https://jsonplaceholder.typicode.com/todos/1"),
|
21
|
+
SknUtils::CommandJSONGet.call(full_url: "http://jsonplaceholder.typicode.com/users")
|
22
|
+
]
|
23
|
+
|
24
|
+
# Initialize the queue with Async Workers by default
|
25
|
+
provider = SknUtils::ConcurrentJobs.call
|
26
|
+
|
27
|
+
# Populate WorkQueue
|
28
|
+
provider.register_jobs(commands, SknUtils::HttpProcessor) # mis-spelling these params result in an immediate exception (line 43 below)
|
29
|
+
|
30
|
+
# Execute WorkQueue
|
31
|
+
result = provider.render_jobs
|
32
|
+
|
33
|
+
if result.success?
|
34
|
+
puts "Success: true"
|
35
|
+
puts "Values: #{result.values}"
|
36
|
+
puts "Messages: #{result.messages}"
|
37
|
+
else
|
38
|
+
puts "Success: false - errors: #{result.messages.join(', ')}"
|
39
|
+
puts "Values: #{result.values}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# result.values
|
43
|
+
rescue => e
|
44
|
+
$stderr.puts e.message, e.backtrace
|
45
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# file: concurrent_test_procs
|
4
|
+
#
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'skn_utils'
|
7
|
+
|
8
|
+
|
9
|
+
# ##
|
10
|
+
# MainLine
|
11
|
+
# ##
|
12
|
+
#
|
13
|
+
begin
|
14
|
+
# CommandJSONPost, CommandFORMGet, CommandJSONGet,
|
15
|
+
# CommandJSONPut, CommandFORMDelete
|
16
|
+
commands = [
|
17
|
+
SknUtils::CommandJSONGet.call(full_url: "http://jsonplaceholder.typicode.com/posts"),
|
18
|
+
SknUtils::CommandJSONGet.call(full_url: "https://jsonplaceholder.typicode.com/comments"),
|
19
|
+
SknUtils::CommandJSONGet.call(full_url: "https://jsonplaceholder.typicode.com/todos/1"),
|
20
|
+
SknUtils::CommandJSONGet.call(full_url: "http://jsonplaceholder.typicode.com/users")
|
21
|
+
]
|
22
|
+
|
23
|
+
# Initialize the queue with Async Workers by default
|
24
|
+
provider = SknUtils::ConcurrentJobs.call
|
25
|
+
|
26
|
+
# Populate WorkQueue
|
27
|
+
work_proc = ->(cmd) { SknSuccess.(cmd.uri.request_uri, "Ok") } # mis-spelling these params result in [SknFailure, SknFailure, ...] results
|
28
|
+
provider.register_jobs(commands, work_proc)
|
29
|
+
|
30
|
+
# Execute WorkQueue
|
31
|
+
result = provider.render_jobs
|
32
|
+
|
33
|
+
if result.success?
|
34
|
+
puts "Success: true"
|
35
|
+
puts "Values: #{result.values}"
|
36
|
+
puts "Messages: #{result.messages}"
|
37
|
+
else
|
38
|
+
puts "Success: false - errors: #{result.messages.join(', ')}"
|
39
|
+
puts "Values: #{result.values}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# result.values
|
43
|
+
rescue => e
|
44
|
+
$stderr.puts e.message, e.backtrace
|
45
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# file: concurrent_test_wrapped
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'bundler/setup'
|
7
|
+
require 'skn_utils'
|
8
|
+
|
9
|
+
|
10
|
+
# ##
|
11
|
+
# MainLine
|
12
|
+
# ##
|
13
|
+
#
|
14
|
+
begin
|
15
|
+
# CommandJSONPost, CommandFORMGet, CommandJSONGet,
|
16
|
+
# CommandJSONPut, CommandFORMDelete
|
17
|
+
commands = [
|
18
|
+
SknUtils::CommandJSONGet.call(full_url: "http://jsonplaceholder.typicode.com/posts"),
|
19
|
+
SknUtils::CommandJSONGet.call(full_url: "https://jsonplaceholder.typicode.com/comments"),
|
20
|
+
SknUtils::CommandJSONGet.call(full_url: "https://jsonplaceholder.typicode.com/todos/1"),
|
21
|
+
SknUtils::CommandJSONGet.call(full_url: "http://jsonplaceholder.typicode.com/users")
|
22
|
+
]
|
23
|
+
|
24
|
+
# Initialize the queue with Async Workers by default
|
25
|
+
provider = SknUtils::ConcurrentJobs.call
|
26
|
+
|
27
|
+
# Populate WorkQueue
|
28
|
+
commands.each do |command|
|
29
|
+
provider.register_job do
|
30
|
+
SknUtils::JobWrapper.call(command, SknUtils::HttpProcessor) # mis-spelling these params result in [nil, nil, ...] results
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Execute WorkQueue
|
35
|
+
result = provider.render_jobs
|
36
|
+
|
37
|
+
if result.success?
|
38
|
+
puts "Success: true"
|
39
|
+
puts "Values: #{result.values}"
|
40
|
+
puts "Messages: #{result.messages}"
|
41
|
+
else
|
42
|
+
puts "Success: false - errors: #{result.messages.join(', ')}"
|
43
|
+
puts "Values: #{result.values}"
|
44
|
+
end
|
45
|
+
|
46
|
+
# result.values
|
47
|
+
rescue => e
|
48
|
+
$stderr.puts e.message, e.backtrace
|
49
|
+
end
|
data/lib/skn_container.rb
CHANGED
data/lib/skn_failure.rb
CHANGED
data/lib/skn_registry.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# ##
|
2
4
|
# SknRegistry
|
3
5
|
# - Key/Value Container where keys and/or values can be any valid Ruby Class, Proc, Instance, or object.
|
@@ -71,9 +73,8 @@ class SknRegistry < Concurrent::Hash
|
|
71
73
|
attr_reader :item, :options
|
72
74
|
|
73
75
|
def initialize(item, options = {})
|
74
|
-
@item
|
75
|
-
|
76
|
-
}.merge(options)
|
76
|
+
@item = item
|
77
|
+
@options = { call: item.is_a?(::Proc) }.merge(options)
|
77
78
|
end
|
78
79
|
|
79
80
|
# Determine if call is required, without changing original values
|
@@ -94,18 +95,19 @@ class SknRegistry < Concurrent::Hash
|
|
94
95
|
def initialize(&block)
|
95
96
|
super
|
96
97
|
block.call(self) if block_given?
|
98
|
+
@__substitutes = {}
|
97
99
|
end
|
98
100
|
|
99
101
|
# public instance methods
|
100
102
|
def register(key, contents = nil, options = {}, &block)
|
101
103
|
if block_given?
|
102
104
|
item = block
|
103
|
-
options
|
105
|
+
options.merge!(contents) if contents.is_a?(::Hash)
|
104
106
|
else
|
105
107
|
item = contents
|
106
108
|
end
|
107
109
|
|
108
|
-
self
|
110
|
+
self.store( key, Content.new(item, options) )
|
109
111
|
|
110
112
|
self # enable chaining
|
111
113
|
end
|
@@ -114,4 +116,22 @@ class SknRegistry < Concurrent::Hash
|
|
114
116
|
self[key]&.call(render_proc)
|
115
117
|
end
|
116
118
|
|
119
|
+
def register_mock(key, contents = nil, options = {}, &block)
|
120
|
+
if member?(key)
|
121
|
+
@__substitutes.store(key, self.delete(key) )
|
122
|
+
end
|
123
|
+
|
124
|
+
register(key, contents, options, &block)
|
125
|
+
end
|
126
|
+
alias_method :substitute, :register_mock
|
127
|
+
|
128
|
+
def unregister_mocks!
|
129
|
+
@__substitutes.keys.each do |k|
|
130
|
+
self[k] = @__substitutes.delete(k)
|
131
|
+
end
|
132
|
+
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
alias_method :restore!, :unregister_mocks!
|
136
|
+
|
117
137
|
end
|
data/lib/skn_settings.rb
CHANGED
data/lib/skn_success.rb
CHANGED
data/lib/skn_utils.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
|
2
4
|
require "skn_utils/version"
|
3
5
|
require 'psych'
|
@@ -8,9 +10,12 @@ require 'time'
|
|
8
10
|
require 'concurrent'
|
9
11
|
unless defined?(Rails)
|
10
12
|
begin
|
13
|
+
require "uri"
|
14
|
+
require "net/http"
|
15
|
+
require 'net/https'
|
11
16
|
require 'deep_merge'
|
12
17
|
rescue LoadError => e
|
13
|
-
puts e.message
|
18
|
+
$stderr.puts e.message, e.backtrace
|
14
19
|
end
|
15
20
|
end
|
16
21
|
require 'skn_utils/core_extensions'
|
@@ -25,6 +30,11 @@ require 'skn_utils/null_object'
|
|
25
30
|
require 'skn_utils/notifier_base'
|
26
31
|
require 'skn_utils/configuration'
|
27
32
|
require 'skn_utils/configurable'
|
33
|
+
require 'skn_utils/wrappable'
|
34
|
+
|
35
|
+
require "skn_utils/job_commands"
|
36
|
+
require "skn_utils/http_processor"
|
37
|
+
require "skn_utils/concurrent_jobs"
|
28
38
|
|
29
39
|
require 'skn_hash'
|
30
40
|
require 'skn_registry'
|
@@ -48,7 +58,7 @@ module SknUtils
|
|
48
58
|
[SknFailure, SknFailure].any? {|o| res.kind_of?(o) } ? res : SknSuccess.( res )
|
49
59
|
|
50
60
|
rescue StandardError, ScriptError => error
|
51
|
-
puts "#{retry_count} - #{error.class.name}:#{error.message}"
|
61
|
+
$stderr.puts "#{retry_count} - #{error.class.name}:#{error.message}"
|
52
62
|
if retry_count <= attempts
|
53
63
|
retry_count+= 1
|
54
64
|
sleep(pause_between)
|
@@ -86,3 +96,4 @@ module SknUtils
|
|
86
96
|
end
|
87
97
|
|
88
98
|
end
|
99
|
+
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# ##
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# See JobCommands, HttpProcessor, ...
|
6
|
+
# See ./bin/par_test_[block|grouped|wrapped] examples
|
7
|
+
#
|
8
|
+
|
9
|
+
module SknUtils
|
10
|
+
|
11
|
+
class SyncWorker
|
12
|
+
def initialize(&blk)
|
13
|
+
@blk = blk
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
@blk.call
|
18
|
+
rescue => ex
|
19
|
+
failures = ex.backtrace.map {|x| x.split("/").last }.join(",")
|
20
|
+
SknFailure.(ex.class.name, { cause: ex.message, backtrace: failures})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class AsyncWorker
|
25
|
+
def initialize(&blk)
|
26
|
+
@blk = Concurrent::Promise.execute(&blk)
|
27
|
+
end
|
28
|
+
|
29
|
+
def call
|
30
|
+
@blk.value
|
31
|
+
rescue => ex
|
32
|
+
failures = ex.backtrace.map {|x| x.split("/").last }.join(",")
|
33
|
+
SknFailure.(ex.class.name, { cause: ex.message, backtrace: failures})
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Result
|
38
|
+
def initialize(merged)
|
39
|
+
@merged = merged
|
40
|
+
end
|
41
|
+
|
42
|
+
def success?
|
43
|
+
@merged.all?(&:success) rescue false
|
44
|
+
end
|
45
|
+
|
46
|
+
def messages
|
47
|
+
@merged.map(&:message)&.compact rescue []
|
48
|
+
end
|
49
|
+
|
50
|
+
def values
|
51
|
+
@merged
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class CallableWrapperJob
|
56
|
+
def self.call(callable, command)
|
57
|
+
callable.call(command)
|
58
|
+
rescue => ex
|
59
|
+
failures = ex.backtrace.map {|x| x.split("/").last }.join(",")
|
60
|
+
SknFailure.(ex.class.name, { cause: ex.message, backtrace: failures})
|
61
|
+
end
|
62
|
+
end
|
63
|
+
class JobWrapper
|
64
|
+
def self.call(command, callable)
|
65
|
+
callable.call(command)
|
66
|
+
rescue => ex
|
67
|
+
failures = ex.backtrace.map {|x| x.split("/").last }.join(",")
|
68
|
+
SknFailure.(ex.class.name, { cause: ex.message, backtrace: failures})
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class ConcurrentJobs
|
73
|
+
attr_reader :elapsed_time_string
|
74
|
+
|
75
|
+
def self.call(async: true)
|
76
|
+
worker = async ? AsyncWorker : SyncWorker
|
77
|
+
new(worker: worker)
|
78
|
+
end
|
79
|
+
|
80
|
+
def initialize(worker:)
|
81
|
+
@worker = worker
|
82
|
+
@workers = []
|
83
|
+
end
|
84
|
+
|
85
|
+
# commands: array of command objects related to callable
|
86
|
+
# callable: callable class or proc, ex:SknUtils::HttpProcessor
|
87
|
+
# callable must return SknSuccess || SknFailure
|
88
|
+
def register_jobs(commands, callable)
|
89
|
+
commands.each do |command|
|
90
|
+
register_job do
|
91
|
+
JobWrapper.call(command,callable)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def register_job(&blk)
|
97
|
+
@workers << @worker.new(&blk)
|
98
|
+
end
|
99
|
+
|
100
|
+
def render_jobs
|
101
|
+
stime = SknUtils.duration
|
102
|
+
merged = @workers.each_with_object([]) do |worker, acc|
|
103
|
+
begin
|
104
|
+
res = worker.call
|
105
|
+
acc.push( res.nil? ? SknFailure.("Unknown", {cause: "Nil Return Value to render Jobs", backtrace: []}) : res )
|
106
|
+
rescue => ex
|
107
|
+
failures = ex.backtrace.map {|x| x.split("/").last }.join(",")
|
108
|
+
acc.push SknFailure.(ex.class.name, { cause: ex.message, backtrace: failures})
|
109
|
+
end
|
110
|
+
end
|
111
|
+
@elapsed_time_string = SknUtils.duration(stime)
|
112
|
+
Result.new(merged)
|
113
|
+
rescue => e
|
114
|
+
Result.new(merged || [])
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
##
|
2
4
|
# File: <gem-root>/lib/skn_utils/configurable.rb
|
3
5
|
# Ref: https://www.toptal.com/ruby/ruby-dsl-metaprogramming-guide
|
@@ -25,7 +27,7 @@ module SknUtils
|
|
25
27
|
# end
|
26
28
|
#
|
27
29
|
#################
|
28
|
-
# During Definition
|
30
|
+
# -or- During Definition
|
29
31
|
#################
|
30
32
|
# class MyApp
|
31
33
|
# include SknUtils::Configurable.with(:app_id, :title, :cookie_name, {root_enable: true})
|
@@ -36,6 +38,7 @@ module SknUtils
|
|
36
38
|
# cookie_name { "#{app_id}_session" }
|
37
39
|
# end
|
38
40
|
#
|
41
|
+
# # these are the root_enable default settings
|
39
42
|
# self.logger = Logger.new
|
40
43
|
# self.env = ENV.fetch('RACK_ENV', 'development')
|
41
44
|
# self.root = Dir.pwd
|
@@ -56,6 +59,11 @@ module SknUtils
|
|
56
59
|
# - env = string value from RACK_ENV
|
57
60
|
# - registry = SknRegistry instance
|
58
61
|
# - logger = Assigned Logger instance
|
62
|
+
# - romDB = var for Rom-DB if used or Any Platform Database
|
63
|
+
# - metadata = platform metadata container
|
64
|
+
# - userdata = user area
|
65
|
+
# - metrics = platform metrics container
|
66
|
+
# - ...
|
59
67
|
# #with(*user_attrs, enable_root: true|false) - defaults to enable of Main Class Attrs
|
60
68
|
# ##
|
61
69
|
# User-Defined Attrs
|
@@ -65,9 +73,9 @@ module SknUtils
|
|
65
73
|
|
66
74
|
module Configurable
|
67
75
|
|
68
|
-
def self.with(*
|
76
|
+
def self.with(*config_attrs, **root_options)
|
69
77
|
_not_provided = Object.new
|
70
|
-
|
78
|
+
_root_options = root_options.empty? || root_options.values.any?{|v| v == true}
|
71
79
|
|
72
80
|
# Define the config class/module methods
|
73
81
|
config_class = Class.new do
|
@@ -79,7 +87,7 @@ module SknUtils
|
|
79
87
|
instance_variable_set("@#{attr}", val)
|
80
88
|
end
|
81
89
|
|
82
|
-
|
90
|
+
config_attrs.each do |attr|
|
83
91
|
define_method attr do |value = _not_provided, &block|
|
84
92
|
if value === _not_provided && block.nil?
|
85
93
|
result = instance_variable_get("@#{attr}")
|
@@ -90,7 +98,7 @@ module SknUtils
|
|
90
98
|
end
|
91
99
|
end
|
92
100
|
|
93
|
-
attr_writer *
|
101
|
+
attr_writer *config_attrs
|
94
102
|
end
|
95
103
|
|
96
104
|
# Define the runtime access methods
|
@@ -101,7 +109,7 @@ module SknUtils
|
|
101
109
|
def configure(&block)
|
102
110
|
config.instance_eval(&block)
|
103
111
|
end
|
104
|
-
if
|
112
|
+
if _root_options
|
105
113
|
# Enable Rails<Like>.env and Rails.logger like feature:
|
106
114
|
# - MyClass.env.production? or MyClass.logger or MyClass.root
|
107
115
|
def registry
|
@@ -128,6 +136,38 @@ module SknUtils
|
|
128
136
|
def logger=(obj)
|
129
137
|
@__logger = obj
|
130
138
|
end
|
139
|
+
|
140
|
+
# Any Platform Database
|
141
|
+
def romDB
|
142
|
+
@__db ||= nil
|
143
|
+
end
|
144
|
+
def romDB(obj)
|
145
|
+
@__db = obj
|
146
|
+
end
|
147
|
+
|
148
|
+
# Maybe Platform Metadata
|
149
|
+
def metadata
|
150
|
+
@__metadata ||= nil
|
151
|
+
end
|
152
|
+
def metadata=(obj)
|
153
|
+
@__metadata = obj
|
154
|
+
end
|
155
|
+
|
156
|
+
# Userdata container for any use
|
157
|
+
def userdata
|
158
|
+
@__userdata ||= nil
|
159
|
+
end
|
160
|
+
def userdata=(obj)
|
161
|
+
@__userdata = obj
|
162
|
+
end
|
163
|
+
|
164
|
+
# Metrics container for any use
|
165
|
+
def metrics
|
166
|
+
@__metrics ||= nil
|
167
|
+
end
|
168
|
+
def metrics=(obj)
|
169
|
+
@__metrics = obj
|
170
|
+
end
|
131
171
|
end
|
132
172
|
end
|
133
173
|
|
@@ -142,3 +182,12 @@ module SknUtils
|
|
142
182
|
|
143
183
|
end # end module
|
144
184
|
end # End module
|
185
|
+
|
186
|
+
#
|
187
|
+
# def [](key)
|
188
|
+
# @internal_var[key]
|
189
|
+
# end
|
190
|
+
#
|
191
|
+
# def []=(key, value)
|
192
|
+
# @internal_var[key] = value
|
193
|
+
# end
|