skn_utils 5.4.0 → 5.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|