skn_utils 5.4.1 → 5.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a38f9d3fd144a0b4d591b492fd5dd4ba2c9e448a
4
- data.tar.gz: 9d5bdceb553ee84f17d37c8f8b3c06d1126f831e
3
+ metadata.gz: 9a78df2df5ed11a61cea3d00b0f3543295e3ce5b
4
+ data.tar.gz: eb021f9d1f707e8e08411b542b05959061322d25
5
5
  SHA512:
6
- metadata.gz: c1d14426f03a6b19cb668f596ccf777352e78295c3c36c7dbe11b56939e90e0b79adb3676b09ab16a61f3167b8089c2f97a40ef846185ac17b527bac04edab04
7
- data.tar.gz: 296386960a3ee3d438334fb1a0ded9ae275632ce5bbf6f486cbdbdab18b5904d6f82c9b33373a0632e7c14e34156230ba2b6c1c9008c27305ba7541f15b0435b
6
+ metadata.gz: 0ee33852c9675b13b64a8beb22fdd26fa6afd09994ee4809e4d2fc84bbc46201f469f9b715f4a7ba2291abadd3f5de36f6d98d257c3cc1f085b29f4f7c316374
7
+ data.tar.gz: d7ff622a8c1dc13ab5a0438c08b5f9f0e5fc04f32c989bcc8ab32d8d08243cae69c1cd6ce985c0a7871b7784f56a181d6ed6ab8879282e9c139588cbd4112f95
data/.rspec CHANGED
@@ -1,2 +1,4 @@
1
1
  --color
2
2
  --require spec_helper
3
+ --format html
4
+ --out coverage/rspec_results.html
data/README.md CHANGED
@@ -2,68 +2,81 @@
2
2
  [![Gem Version](https://badge.fury.io/rb/skn_utils.svg)](http://badge.fury.io/rb/skn_utils)
3
3
 
4
4
  # SknUtils
5
- ## SknUtils::NestedResult class; dynamic key/value container
6
- A Ruby Gem containing a Ruby PORO (Plain Old Ruby Object) that can be instantiated at runtime with an input hash. This library creates
7
- an Object with Dot and Hash notational accessors to each key's value. Additional key/value pairs can be added post-create
8
- by simply assigning it; `obj.my_new_var = "some value"`
9
5
 
10
- * Transforms the initialing hash into accessible object instance values, with their keys as method names.
11
- * If the key's value is also a hash, it too will become an Object.
12
- * if the key's value is a Array of Hashes, or Array of Arrays of Hashes, each hash element of the Arrays will become an Object.
13
- * The current key/value (including nested) pairs are returned via #to_hash or #to_json when and if needed.
14
- * Best described as dot notation wrapper over a Ruby (Concurrent-Ruby) Hash.
15
-
16
- Ruby's Hash object is already extremely flexible, even more so with the addition of dot-notation. As I work more with Ruby outside of Rails, I'm finding more use cases for the capabilities of this gem. Here are a few examples:
17
-
18
- 1. Application settings containers, SknSettings. Loads Yaml file based on `ENV['RACK_ENV']` value, or specified file-key.
19
- - Replaces Config and/or RBConfig Gems for yaml based settings
20
- 1. Substitute for Rails.root, via a little ERB/YAML/Marshal statement in settings.yml file, and a helper class
21
- - settings.yml (YAML)
22
- - `root: <%= Dir.pwd %>`
23
- - enables `SknSettings.root`
24
- - `env: !ruby/string:SknUtils::EnvStringHandler <%= ENV.fetch('RACK_ENV', 'development') %>`
25
- - enables `SknSettings.env.production?` ...
26
- 1. Since SknSettings is by necessity a global constant, it can serve as Session Storage to keep system objects; like a ROM-RB instance.
27
- 1. In-Memory Key-Store, use it to cache active user objects, or active Integration passwords, and/or objects that are not serializable.
28
- 1. Command registries used to dispatch command requests to proper command handler. see example app [SknBase](https://github.com/skoona/skn_base/blob/master/strategy/services/content/command_handler.rb)
29
- ```ruby
30
- SknSettings.command_handler = {
31
- Commands::RetrieveAvailableResources => method(:resources_metadata_service),
32
- Commands::RetrieveResourceContent => method(:resource_content_service)
33
- }
34
- ...
35
- SknSettings.command_handler[ cmd.class ].call( cmd )
36
- -- or --
37
- SknSettings.command_handler.key?( cmd.class ) && cmd.valid? ?
38
- SknSettings.command_handler[ cmd.class ].call( cmd ) : command_not_found_action()
39
- ```
40
- There are many more use cases for Ruby's Hash that this gem just makes easier to implement.
41
-
42
-
43
- ## Available Classes
44
- * SknSuccess
45
- * SknFailure
6
+ `SknUtils` is a collection of pure-ruby utility classes and modules, with limited
7
+ dependencies, to augment the development of Ruby applications. Examples of these
8
+ utilities in action can be found in my related projects `SknServices`, `SknWebApp`,
9
+ and `SknBase`.
10
+
11
+ * The exchange or handoff of values between objects is addressed via the `NestedResults`
12
+ class which implements dot-notation and nesting over a concurrent hash: A ruby Hash can
13
+ use any valid ruby object as a key or value.
14
+ * `NestedResults` is later sub-classed as `Configuration` to provide application level
15
+ settings using YAML files with a API simular to the RbConfig gem.
16
+ * Object or method return values can be enclosed in `SknSuccess` or `SknFailure` classes
17
+ to prevent or minimize nil returns.
18
+ * Precise microsecond `duration`s, Number to `as_human_size`, and a `catch_exceptions`
19
+ retry-able feature are implemented on the SknUtils class directly for ease of use.
20
+ * `Configurable` module extends any class or module with configurable attribute method
21
+ as needed, with a set of defaults methods which emulate Rails.env, Rails.logger, and
22
+ Rails.root functionality.
23
+ * `CoreObjectExtensions` simular to ActiveSupport's, `#present?` and `#blank?` are
24
+ automatically applied, unless already present, when SknUtils gem is loaded.
25
+ * `SknRegistry` class is an advanced feature which allows you to manually register
26
+ classes, procs, or any value with a user-defined `label`. Initialization and dependency injection
27
+ requirements of service-like classes can be included in this registration process allowing
28
+ them to be centrally maintained. The `label` can be any valid ruby value; like a
29
+ classname, symbol, or string as needed
30
+ * `NullObject`, `NotifierBase`, and `Wrappable` are interesting classes which you might want to explore further.
31
+ * `ConcurrentJobs` is a feature implemented to allow concurrent/multi-threaded execution of jobs. The included
32
+ companion classes focus on HTTP GET,PUT,POST, and DELETE jobs as an example of how to use the `ConcurrentJobs` feature.
33
+
34
+ All classes and modules have RSpec test coverage (90+) of their originally intended use-cases.
35
+
36
+
37
+ ### Available Classes
46
38
  * SknSettings
47
39
  * SknUtils::Configuration
48
40
  * SknUtils::EnvStringHandler
49
- * SknRegistry
50
- * SknContainer
51
41
  * SknHash
52
42
  * SknUtils::ResultBean
53
43
  * SknUtils::PageControls
54
44
  * SknUtils::NestedResult
55
- * SknUtils::NullObject
56
- * SknUtils::CoreObjectExtensions
45
+ * SknRegistry
46
+ * SknContainer
47
+ * SknSuccess
48
+ * SknFailure
57
49
  * SknUtils::Configurable
58
-
59
- ## Available Class.Methods
50
+ * SknUtils::CoreObjectExtensions
51
+ * SknUtils::NullObject
52
+ * SknUtils::NotifierBase
53
+ * SknUtils::Wrappable
54
+ * SknUtils::ConcurrentJobs
55
+ * SknUtils::JobWrapper (via ConcurrentJobs)
56
+ * SknUtils::CommandJSONPost (via JobCommands)
57
+ * SknUtils::CommandFORMPost
58
+ * SknUtils::CommandJSONGet
59
+ * SknUtils::CommandJSONPut
60
+ * SknUtils::CommandFORMDelete
61
+ * SknUtils::HttpProcessor
62
+
63
+ ### Available Class.Methods
60
64
  * SknUtils.catch_exceptions()
61
65
  * SknUtils.as_human_size()
62
66
  * SknUtils.duration(start_time=nil)
63
67
 
64
68
 
65
-
66
69
  ## History
70
+ 2/24/2019 V5.5.0
71
+ Added
72
+ * ConcurrentJobs feature set
73
+ - Executes (HTTP/any) jobs in parallel
74
+
75
+ 1/2/2019 V5.4.1
76
+ Added
77
+ - Wrappable module for evaluation.
78
+ - Ruby comment # frozen_string_literal, everywhere
79
+
67
80
  12/16/2018 V5.4.0
68
81
  Added :duration() utils to SknUtils module:
69
82
  #duration() #=> returns start_time value
@@ -154,6 +167,43 @@ There are many more use cases for Ruby's Hash that this gem just makes easier to
154
167
  06/2015 V1.5.1 commit #67ef656
155
168
  Last Version to depend on Rails (ActiveModel) for #to_json and #to_xml serialization
156
169
 
170
+ ### NestedResult class; dynamic key/value container
171
+ A class implementing a Ruby PORO (Plain Old Ruby Object) that can be instantiated at runtime with a hash. Creates
172
+ a nested object with Dot and Hash notational accessors to each key's value. Additional key/value pairs can be added post-create
173
+ by simply assigning it; `obj.my_new_var = "some value"`
174
+
175
+ * Transforms the initialing hash into accessible object instance values, with their keys as method names.
176
+ * If the key's value is also a hash, it too will become an Object.
177
+ * if the key's value is a Array of Hashes, or Array of Arrays of Hashes, each hash element of the Arrays will become an Object.
178
+ * The current key/value (including nested) pairs are returned via #to_hash or #to_json when and if needed.
179
+ * Best described as dot notation wrapper over a Ruby (Concurrent-Ruby) Hash.
180
+
181
+ Ruby's Hash object is already extremely flexible, even more so with the addition of dot-notation. As I work more with Ruby outside of Rails, I'm finding more use cases for the capabilities of this gem. Here are a few examples:
182
+
183
+ 1. Application settings containers, SknSettings. Loads Yaml file based on `ENV['RACK_ENV']` value, or specified file-key.
184
+ - Replaces Config and/or RBConfig Gems for yaml based settings
185
+ 1. Substitute for Rails.root, via a little ERB/YAML/Marshal statement in settings.yml file, and a helper class
186
+ - settings.yml (YAML)
187
+ - `root: <%= Dir.pwd %>`
188
+ - enables `SknSettings.root`
189
+ - `env: !ruby/string:SknUtils::EnvStringHandler <%= ENV.fetch('RACK_ENV', 'development') %>`
190
+ - enables `SknSettings.env.production?` ...
191
+ 1. Since SknSettings is by necessity a global constant, it can serve as Session Storage to keep system objects; like a ROM-RB instance.
192
+ 1. In-Memory Key-Store, use it to cache active user objects, or active Integration passwords, and/or objects that are not serializable.
193
+ 1. Command registries used to dispatch command requests to proper command handler. see example app [SknBase](https://github.com/skoona/skn_base/blob/master/strategy/services/content/command_handler.rb)
194
+ ```ruby
195
+ SknSettings.command_handler = {
196
+ Commands::RetrieveAvailableResources => method(:resources_metadata_service),
197
+ Commands::RetrieveResourceContent => method(:resource_content_service)
198
+ }
199
+ ...
200
+ SknSettings.command_handler[ cmd.class ].call( cmd )
201
+ -- or --
202
+ SknSettings.command_handler.key?( cmd.class ) &&
203
+ cmd.valid? ? SknSettings.command_handler[ cmd.class ].call( cmd ) : command_not_found_action()
204
+ ```
205
+ There are many more use cases for Ruby's Hash that this gem just makes easier to implement.
206
+
157
207
 
158
208
  ## Public Components
159
209
  SknUtils::NestedResult # Primary Key/Value Container with Dot/Hash notiation support.
@@ -171,6 +221,14 @@ There are many more use cases for Ruby's Hash that this gem just makes easier to
171
221
  SknSuccess # Three attribute value containers for return codes -- #value, #message, #success
172
222
  - Extra #payload method returns value as NestResult if value is_a Hash
173
223
  SknFailure # Three attribute value containers for return codes -- #value, #message, #success
224
+ SknUtils::ConcurrentJobs # Async/Sync Job executor pool with HTTP support
225
+ SknUtils::CommandJSONGet # HTTP Get Command class expecting `json` return, located inside `job_commands`
226
+ SknUtils::CommandJSONPut # HTTP Put Command class expecting `json` return, located inside `job_commands`
227
+ SknUtils::CommandJSONPost # HTTP Post Command class expecting `json` return, located inside `job_commands`
228
+ SknUtils::CommandFORMPost # HTTP Post Command class expecting `form` data return, located inside `job_commands`
229
+ SknUtils::CommandFORMDelete # HTTP Delete Command class expecting `form` data return, located inside `job_commands`
230
+ SknUtils::HttpProcessor # Command driven HTTP processing object supporting GET,PUT,POST, and DELETE
231
+ SknUtils::JobWrapper # Outer wrapper class to capture catastrophic exceptions (syntax normally)
174
232
 
175
233
 
176
234
  ## Public Methods: SknUtils::Configurable module
@@ -239,8 +297,8 @@ There are many more use cases for Ruby's Hash that this gem just makes easier to
239
297
  ```
240
298
 
241
299
 
242
- ## Public Methods: SknContainer ONLY
243
- SknContainer is global constant assigned to an instantiated instance of SknRegistry.
300
+ ## Public Methods: SknRegistry ONLY
301
+ `SknContainer` is global constant assigned to an instantiated instance of `SknRegistry`.
244
302
  Returns the keyed value as the original instance/value or if provided a proc the result of calling that proc.
245
303
  To register a class or object for global retrieval, use the following API. Also review the RSpecs for additional useage info.
246
304
 
@@ -266,9 +324,69 @@ There are many more use cases for Ruby's Hash that this gem just makes easier to
266
324
  same_instance = SknContainer.resolve(:the_instance)
267
325
 
268
326
 
327
+ ## Public Methods: SknUtils::ConcurrentJobs ONLY
328
+ `ConcurrentJobs` behaves as a concurrent thread pool by using Concurrent::Promise from the `concurrent-ruby` gem.
329
+ Enables the definition of Procs, or any callable class, which will be executed in parrallel the available jobs
330
+ loaded into ConcurrentJobs. Meant to reduce user-sensitive response times when multiple APIs must be invoked.
331
+ Also review the RSpecs for additional useage info.
332
+
333
+ SknUtils::ConcurrentJobs
334
+ #call(async: true) - Instantiate ConcurrentJobs with Async Workers, false for Sync Workers
335
+ #register_jobs(cmds, callable) - Array of Command to be executed by single callable
336
+ #register_job(&block) - Adds callable block to internal worker queue
337
+ #render_jobs - Collect results from all jobs into a Result object
338
+ #elapsed_time_string - "0.012 seconds" string showing duration of last #render_jobs call
339
+
340
+ SknUtils::Result - Contains individual results from each job executed
341
+ #success? - Determines if any job failed
342
+ #messages - Retrieves messages from job results, assumed present when job fails
343
+ #values - Returns an array of individual results from job executions
344
+
345
+ Commands and HttpProcessors are included to demonstrate Job creating patterns. ConcurrentJobs is not restricted
346
+ to Http calls or the command to command handler pattern. Using the #register_job method you can pass callable BLOCK
347
+ and it will be executed when #render_jobs is invoked. HttpProcessor is what I needed and triggered me to add this feature.
348
+
349
+ Example here:
350
+ ```ruby
351
+ begin
352
+ # CommandJSONPost, CommandFORMGet, CommandJSONGet,
353
+ # CommandJSONPut, CommandFORMDelete
354
+ commands = [
355
+ SknUtils::CommandJSONGet.call(full_url: "http://jsonplaceholder.typicode.com/posts"),
356
+ SknUtils::CommandJSONGet.call(full_url: "https://jsonplaceholder.typicode.com/comments"),
357
+ SknUtils::CommandJSONGet.call(full_url: "https://jsonplaceholder.typicode.com/todos/1"),
358
+ SknUtils::CommandJSONGet.call(full_url: "http://jsonplaceholder.typicode.com/users")
359
+ ]
360
+
361
+ # Initialize the queue with Async Workers by default
362
+ provider = SknUtils::ConcurrentJobs.call
363
+
364
+ # Populate WorkQueue
365
+ provider.register_jobs(commands, SknUtils::HttpProcessor) # mis-spelling these params result in an immediate exception (line 43 below)
366
+
367
+ # Execute WorkQueue
368
+ result = provider.render_jobs
369
+
370
+ if result.success?
371
+ puts "Success: true"
372
+ puts "Values: #{result.values}"
373
+ puts "Messages: #{result.messages}"
374
+ else
375
+ puts "Success: false - errors: #{result.messages.join(', ')}"
376
+ puts "Values: #{result.values}"
377
+ end
378
+
379
+ # result.values
380
+ rescue => e
381
+ $stderr.puts e.message, e.backtrace
382
+ end
383
+
384
+ ```
385
+
386
+
269
387
 
270
388
  ## Public Methods: SknSettings ONLY
271
- SknSettings is global constant containing an initialized Object of SknUtils::Configuration using defaults
389
+ SknSettings is a global constant containing an initialized Object of SknUtils::Configuration using defaults
272
390
  To change the 'development'.yml default please use the following method early or in the case of Rails in 'application.rb
273
391
  #load_config_basename!(config_name) -- Where config_name is the name of yml files stored in the `./config/settings` directory
274
392
  #config_path!(path) -- Where path format is './<dirs>/', default is: './config/'
@@ -420,10 +538,11 @@ There are many more use cases for Ruby's Hash that this gem just makes easier to
420
538
  ## Installation
421
539
 
422
540
  runtime prereqs:
423
- * V4+ None
424
- * V3+ None
425
- * V2+ None
426
- * V1+ gem 'active_model', '~> 3.0'
541
+ * V5+ None
542
+ * V4+ None
543
+ * V3+ None
544
+ * V2+ None
545
+ * V1+ gem 'active_model', '~> 3.0'
427
546
 
428
547
 
429
548
  Add this line to your application's Gemfile:
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # file: concurrent_test_block
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
+ begin
31
+ SknUtils::HttpProcessor.call(command) # mis-spelling these params result in [SknFailure, SknFailure, ...] results
32
+ rescue => ex
33
+ $stderr.puts ex
34
+ SknFailure.(ex.class.name, "#{ex.message}; #{ex.backtrace[0]}")
35
+ end
36
+ end
37
+ end
38
+
39
+ # Execute WorkQueue
40
+ result = provider.render_jobs
41
+
42
+ if result.success?
43
+ puts "Success: true"
44
+ puts "Values: #{result.values}"
45
+ puts "Messages: #{result.messages}"
46
+ else
47
+ puts "Success: false - errors: #{result.messages.join(', ')}"
48
+ puts "Values: #{result.values}"
49
+ end
50
+
51
+ # result.values
52
+ rescue => e
53
+ $stderr.puts e.message, e.backtrace
54
+ end
@@ -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_utils.rb CHANGED
@@ -10,9 +10,12 @@ require 'time'
10
10
  require 'concurrent'
11
11
  unless defined?(Rails)
12
12
  begin
13
+ require "uri"
14
+ require "net/http"
15
+ require 'net/https'
13
16
  require 'deep_merge'
14
17
  rescue LoadError => e
15
- puts e.message
18
+ $stderr.puts e.message, e.backtrace
16
19
  end
17
20
  end
18
21
  require 'skn_utils/core_extensions'
@@ -29,6 +32,10 @@ require 'skn_utils/configuration'
29
32
  require 'skn_utils/configurable'
30
33
  require 'skn_utils/wrappable'
31
34
 
35
+ require "skn_utils/job_commands"
36
+ require "skn_utils/http_processor"
37
+ require "skn_utils/concurrent_jobs"
38
+
32
39
  require 'skn_hash'
33
40
  require 'skn_registry'
34
41
  require 'skn_container'
@@ -51,7 +58,7 @@ module SknUtils
51
58
  [SknFailure, SknFailure].any? {|o| res.kind_of?(o) } ? res : SknSuccess.( res )
52
59
 
53
60
  rescue StandardError, ScriptError => error
54
- puts "#{retry_count} - #{error.class.name}:#{error.message}"
61
+ $stderr.puts "#{retry_count} - #{error.class.name}:#{error.message}"
55
62
  if retry_count <= attempts
56
63
  retry_count+= 1
57
64
  sleep(pause_between)
@@ -89,3 +96,4 @@ module SknUtils
89
96
  end
90
97
 
91
98
  end
99
+
@@ -0,0 +1,96 @@
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
+ end
19
+ end
20
+
21
+ class AsyncWorker
22
+ def initialize(&blk)
23
+ @blk = Concurrent::Promise.execute(&blk)
24
+ end
25
+
26
+ def call
27
+ @blk.value
28
+ end
29
+ end
30
+
31
+ class Result
32
+ def initialize(merged)
33
+ @merged = merged
34
+ end
35
+
36
+ def success?
37
+ @merged.all?(&:success) rescue false
38
+ end
39
+
40
+ def messages
41
+ @merged.map(&:message)&.compact rescue []
42
+ end
43
+
44
+ def values
45
+ @merged
46
+ end
47
+ end
48
+
49
+ class JobWrapper
50
+ def self.call(command, callable)
51
+ callable.call(command)
52
+ rescue => ex
53
+ SknFailure.(ex.class.name, "#{ex.message}; #{ex.backtrace[0]}")
54
+ end
55
+ end
56
+
57
+ class ConcurrentJobs
58
+ attr_reader :elapsed_time_string
59
+
60
+ def self.call(async: true)
61
+ worker = async ? AsyncWorker : SyncWorker
62
+ new(worker: worker)
63
+ end
64
+
65
+ def initialize(worker:)
66
+ @worker = worker
67
+ @workers = []
68
+ end
69
+
70
+ # commands: array of command objects related to callable
71
+ # callable: callable class or proc, ex:SknUtils::HttpProcessor
72
+ # callable must return SknSuccess || SknFailure
73
+ def register_jobs(commands, callable)
74
+ commands.each do |command|
75
+ register_job do
76
+ JobWrapper.call(command,callable)
77
+ end
78
+ end
79
+ end
80
+
81
+ def register_job(&blk)
82
+ @workers << @worker.new(&blk)
83
+ end
84
+
85
+ def render_jobs
86
+ stime = SknUtils.duration
87
+ merged = @workers.each_with_object([]) do |worker, acc|
88
+ acc.push( worker.call )
89
+ end
90
+ @elapsed_time_string = SknUtils.duration(stime)
91
+ Result.new(merged)
92
+ rescue => e
93
+ Result.new(merged || [])
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,34 @@
1
+ # ##
2
+ #
3
+ #
4
+ module SknUtils
5
+
6
+ class HttpProcessor
7
+
8
+ def self.call(command)
9
+ completion = false
10
+
11
+ response = Net::HTTP.start( command.uri.host,command.uri.port,
12
+ use_ssl: command.uri.scheme.eql?("https")
13
+ ) do |http|
14
+ http.open_timeout = 5 # in seconds, for internal http timeouts
15
+ http.read_timeout = 15 # in seconds
16
+ if command.uri.scheme.eql?("https")
17
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
18
+ end
19
+ http.request(command.request)
20
+ end
21
+
22
+ if ( response.kind_of?(Net::HTTPClientError) or response.kind_of?(Net::HTTPServerError) )
23
+ completion = SknFailure.call(response.code, response.message)
24
+ else
25
+ payload = command.json? ? JSON.load(response.body) : response.body
26
+ completion = SknSuccess.call(payload, response.class.name)
27
+ end
28
+
29
+ completion
30
+ rescue => exception
31
+ SknFailure.call(command.uri.request_uri, "#{exception.message}; #{exception.backtrace[0]}")
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,201 @@
1
+ # ##
2
+ #
3
+ # Ref: https://yukimotopress.github.io/http
4
+
5
+ module SknUtils
6
+
7
+ # #################################################
8
+ #
9
+ class CommandJSONPost
10
+ def self.call(options) # {full_url:,username:,userpass:,payload:}
11
+ new(options)
12
+ end
13
+
14
+ def json?
15
+ true
16
+ end
17
+
18
+ def uri
19
+ @_uri
20
+ end
21
+
22
+ def request
23
+ req = Net::HTTP::Post.new(uri.path) # Generate HTTPRequest object
24
+ req.basic_auth(@_username, @_userpass) if credentials?
25
+ req.content_type = 'application/json'
26
+ req.body = formatted_data
27
+ req
28
+ end
29
+
30
+ private
31
+
32
+ def initialize(opts={})
33
+ @_username = opts[:username]
34
+ @_userpass = opts[:userpass]
35
+ @_uri = URI.parse( opts[:full_url])
36
+ @_data = opts[:payload]
37
+ end
38
+
39
+ def formatted_data
40
+ @_data.respond_to?(:to_json) ? @_data.to_json : @_data
41
+ end
42
+
43
+ def credentials?
44
+ !(@_username.nil? || @_userpass.nil?)
45
+ end
46
+ end
47
+
48
+
49
+ # #################################################
50
+ #
51
+ class CommandFORMPost
52
+ def self.call(options) # {full_url:,username:,userpass:,payload:}
53
+ new(options)
54
+ end
55
+
56
+ def json?
57
+ false
58
+ end
59
+
60
+ def uri
61
+ @_uri
62
+ end
63
+
64
+ def request
65
+ req = Net::HTTP::Post.new(uri.path) # Generate HTTPRequest object
66
+ req.basic_auth(@_username, @_userpass) if credentials?
67
+ req.content_type = 'application/x-www-form-urlencoded'
68
+ req.set_form_data(formatted_data)
69
+ req
70
+ end
71
+
72
+ private
73
+
74
+ def initialize(opts={})
75
+ @_username = opts[:username]
76
+ @_userpass = opts[:userpass]
77
+ @_uri = URI.parse( opts[:full_url])
78
+ @_data = opts[:payload]
79
+ end
80
+
81
+ def formatted_data
82
+ @_data
83
+ end
84
+
85
+ def credentials?
86
+ !(@_username.nil? || @_userpass.nil?)
87
+ end
88
+ end
89
+
90
+
91
+ # #################################################
92
+ #
93
+ class CommandJSONGet
94
+ def self.call(options) # {full_url:,username:,userpass:}
95
+ new(options)
96
+ end
97
+
98
+ def json?
99
+ true
100
+ end
101
+
102
+ def uri
103
+ @_uri
104
+ end
105
+
106
+ def request
107
+ req = Net::HTTP::Get.new(uri.request_uri)
108
+ req.basic_auth(@_username, @_userpass) if credentials?
109
+ req
110
+ end
111
+
112
+ private
113
+
114
+ def initialize(opts={})
115
+ @_username = opts[:username]
116
+ @_userpass = opts[:userpass]
117
+ @_uri = URI.parse( opts[:full_url])
118
+ end
119
+
120
+ def credentials?
121
+ !(@_username.nil? || @_userpass.nil?)
122
+ end
123
+ end
124
+
125
+
126
+ # #################################################
127
+ #
128
+ class CommandJSONPut
129
+ def self.call(options) # {full_url:,username:,userpass:,payload:}
130
+ new(options)
131
+ end
132
+
133
+ def json?
134
+ true
135
+ end
136
+
137
+ def uri
138
+ @_uri
139
+ end
140
+
141
+ def request
142
+ req = Net::HTTP::Put.new(uri.path) # Generate HTTPRequest object
143
+ req.basic_auth(@_username, @_userpass) if credentials?
144
+ req.content_type = 'application/json'
145
+ req.body = formatted_data
146
+ req
147
+ end
148
+
149
+ private
150
+
151
+ def initialize(opts={})
152
+ @_username = opts[:username]
153
+ @_userpass = opts[:userpass]
154
+ @_uri = URI.parse( opts[:full_url])
155
+ @_data = opts[:payload]
156
+ end
157
+
158
+ def formatted_data
159
+ @_data.respond_to?(:to_json) ? @_data.to_json : @_data
160
+ end
161
+
162
+ def credentials?
163
+ !(@_username.nil? || @_userpass.nil?)
164
+ end
165
+ end
166
+
167
+
168
+ # #################################################
169
+ #
170
+ class CommandFORMDelete
171
+ def self.call(options) # {full_url:,username:,userpass:}
172
+ new(options)
173
+ end
174
+
175
+ def json?
176
+ false
177
+ end
178
+
179
+ def uri
180
+ @_uri
181
+ end
182
+
183
+ def request
184
+ req = Net::HTTP::Delete.new(uri.request_uri)
185
+ req.basic_auth(@_username, @_userpass) if credentials?
186
+ req
187
+ end
188
+
189
+ private
190
+
191
+ def initialize(opts={})
192
+ @_username = opts[:username]
193
+ @_userpass = opts[:userpass]
194
+ @_uri = URI.parse( opts[:full_url])
195
+ end
196
+
197
+ def credentials?
198
+ !(@_username.nil? || @_userpass.nil?)
199
+ end
200
+ end
201
+ end
@@ -4,8 +4,8 @@
4
4
  module SknUtils
5
5
  class Version
6
6
  MAJOR = 5
7
- MINOR = 4
8
- PATCH = 1
7
+ MINOR = 5
8
+ PATCH = 0
9
9
 
10
10
  def self.to_s
11
11
  [MAJOR, MINOR, PATCH].join('.')
data/skn_utils.gemspec CHANGED
@@ -39,7 +39,10 @@ EOF
39
39
  spec.add_development_dependency "rake", "~> 10"
40
40
  spec.add_development_dependency "rspec", '~> 3'
41
41
  spec.add_development_dependency "pry", "~> 0"
42
+ spec.add_development_dependency "pry-coolline"
42
43
  spec.add_development_dependency "simplecov", "~> 0"
43
44
  spec.add_development_dependency 'benchmark-ips', '~> 2'
45
+ spec.add_development_dependency 'webmock'
46
+
44
47
 
45
48
  end
@@ -0,0 +1,277 @@
1
+ ##
2
+ # spec/lib/skn_utils/as_human_size_spec.rb
3
+ #
4
+
5
+
6
+ describe SknUtils::ConcurrentJobs, 'Run Multiple Jobs' do
7
+
8
+ let(:commands) {
9
+ [
10
+ SknUtils::CommandJSONPost.call(full_url: "http://example.com/posts", payload: {one: 1}),
11
+ SknUtils::CommandFORMPost.call(full_url: "http://example.com/posts", payload: {one: 1}),
12
+ SknUtils::CommandJSONGet.call(full_url: "http://example.com/posts/1"),
13
+ SknUtils::CommandJSONPut.call(full_url: "http://example.com/posts", payload: {one: 1}),
14
+ SknUtils::CommandFORMDelete.call(full_url: "http://example.com/posts/1")
15
+ ]
16
+ }
17
+
18
+ let(:test_proc) {
19
+ ->(cmd) { SknSuccess.(cmd.uri.request_uri, "Ok") }
20
+ }
21
+
22
+ let(:inline_failure_proc) {
23
+ ->(cmd) { SknFailure.(cmd.uri.request_uri, "Failure") }
24
+ }
25
+
26
+ let(:catastrophic_proc) {
27
+ ->(cmd) { SomeUnkownThing.(cmd.uri.request_uri, "Catastrophic") }
28
+ }
29
+
30
+ context "HTTP Requests " do
31
+ it "Job Commands will provide a valid http request object" do
32
+ expect(commands.any?(&:request)).to be true
33
+ end
34
+
35
+ it "Performs Http Post Requests" do
36
+ test_url = "http://jsonplaceholder.typicode.com/users"
37
+ stub_request(:post, test_url).
38
+ to_return(status: 200, body: "{\"message\":\"me\"}", headers: {})
39
+
40
+ cmd = SknUtils::CommandJSONPost.call(full_url: test_url, payload: {"one" => 1})
41
+
42
+ provider = SknUtils::ConcurrentJobs.call
43
+ provider.register_job do
44
+ SknUtils::JobWrapper.call(cmd, SknUtils::HttpProcessor)
45
+ end
46
+ result = provider.render_jobs
47
+
48
+ expect(result).to be_a(SknUtils::Result)
49
+ expect(result.success?).to be true
50
+ expect(result.values.size).to eq(1)
51
+ expect(result.values[0]).to be_a(SknSuccess)
52
+ expect(result.values[0].value).to be_a(Hash)
53
+ expect(result.values[0].value["message"]).to eq("me")
54
+ end
55
+
56
+ it "Performs Http Form Post Requests" do
57
+ test_url = "http://jsonplaceholder.typicode.com/users"
58
+ stub_request(:post, test_url).
59
+ to_return(status: 200, body: "message=me", headers: {})
60
+
61
+ cmd = SknUtils::CommandFORMPost.call(full_url: test_url, payload: {"one" => 1})
62
+
63
+ provider = SknUtils::ConcurrentJobs.call
64
+ provider.register_job do
65
+ SknUtils::JobWrapper.call(cmd, SknUtils::HttpProcessor)
66
+ end
67
+ result = provider.render_jobs
68
+
69
+ expect(result).to be_a(SknUtils::Result)
70
+ expect(result.success?).to be true
71
+ expect(result.values.size).to eq(1)
72
+ expect(result.values[0]).to be_a(SknSuccess)
73
+ expect(result.values[0].value).to be_a(String)
74
+ expect(result.values[0].value).to eq("message=me")
75
+ end
76
+
77
+ it "Performs Http Get Requests" do
78
+ test_url = "http://jsonplaceholder.typicode.com/users"
79
+ stub_request(:get, test_url).
80
+ to_return(status: 200, body: "{\"message\":\"me\"}", headers: {})
81
+
82
+ cmd = SknUtils::CommandJSONGet.call(full_url: test_url)
83
+
84
+ provider = SknUtils::ConcurrentJobs.call
85
+ provider.register_job do
86
+ SknUtils::JobWrapper.call(cmd, SknUtils::HttpProcessor)
87
+ end
88
+ result = provider.render_jobs
89
+
90
+ expect(result).to be_a(SknUtils::Result)
91
+ expect(result.success?).to be true
92
+ expect(result.values.size).to eq(1)
93
+ expect(result.values[0]).to be_a(SknSuccess)
94
+ expect(result.values[0].value).to be_a(Hash)
95
+ expect(result.values[0].value["message"]).to eq("me")
96
+ end
97
+
98
+ it "Performs Http Put Requests" do
99
+ test_url = "http://jsonplaceholder.typicode.com/users"
100
+ stub_request(:put, test_url).
101
+ to_return(status: 200, body: "{\"message\":\"me\"}", headers: {})
102
+
103
+ cmd = SknUtils::CommandJSONPut.call(full_url: test_url, payload: {"one" => 1})
104
+
105
+ provider = SknUtils::ConcurrentJobs.call
106
+ provider.register_job do
107
+ SknUtils::JobWrapper.call(cmd, SknUtils::HttpProcessor)
108
+ end
109
+ result = provider.render_jobs
110
+
111
+ expect(result).to be_a(SknUtils::Result)
112
+ expect(result.success?).to be true
113
+ expect(result.values.size).to eq(1)
114
+ expect(result.values[0]).to be_a(SknSuccess)
115
+ expect(result.values[0].value).to be_a(Hash)
116
+ expect(result.values[0].value["message"]).to eq("me")
117
+ end
118
+
119
+ it "Performs Http Form Delete Requests" do
120
+ test_url = "http://jsonplaceholder.typicode.com/users"
121
+ stub_request(:delete, test_url).
122
+ to_return(status: 200, body: "message=me", headers: {})
123
+
124
+ cmd = SknUtils::CommandFORMDelete.call(full_url: test_url, payload: {"one" => 1})
125
+
126
+ provider = SknUtils::ConcurrentJobs.call
127
+ provider.register_job do
128
+ SknUtils::JobWrapper.call(cmd, SknUtils::HttpProcessor)
129
+ end
130
+ result = provider.render_jobs
131
+
132
+ expect(result).to be_a(SknUtils::Result)
133
+ expect(result.success?).to be true
134
+ expect(result.values.size).to eq(1)
135
+ expect(result.values[0]).to be_a(SknSuccess)
136
+ expect(result.values[0].value).to be_a(String)
137
+ expect(result.values[0].value).to eq("message=me")
138
+ end
139
+ end
140
+
141
+ context "Asynchronous" do
142
+ it "Runs Jobs" do
143
+ provider = SknUtils::ConcurrentJobs.call
144
+ provider.register_jobs(commands, test_proc)
145
+ result = provider.render_jobs
146
+
147
+ expect(result).to be_a(SknUtils::Result)
148
+ expect(result.success?).to be true
149
+ expect(result.values.size).to eq(commands.size)
150
+ expect(result.values[0]).to be_a(SknSuccess)
151
+ end
152
+ it "Runs Jobs and handles inline failures" do
153
+ provider = SknUtils::ConcurrentJobs.call
154
+
155
+ provider.register_job do
156
+ SknUtils::JobWrapper.call(commands[0], test_proc)
157
+ end
158
+ provider.register_job do
159
+ SknUtils::JobWrapper.call(commands[1], inline_failure_proc)
160
+ end
161
+ provider.register_job do
162
+ SknUtils::JobWrapper.call(commands[2], inline_failure_proc)
163
+ end
164
+ provider.register_job do
165
+ SknUtils::JobWrapper.call(commands[3], inline_failure_proc)
166
+ end
167
+ provider.register_job do
168
+ SknUtils::JobWrapper.call(commands[4], test_proc)
169
+ end
170
+
171
+ result = provider.render_jobs
172
+
173
+ expect(result).to be_a(SknUtils::Result)
174
+ expect(result.success?).to be false
175
+ expect(result.values.size).to eq(commands.size)
176
+ expect(result.values[1]).to be_a(SknFailure)
177
+ expect(result.values[1].message).to eq("Failure")
178
+ end
179
+ it "Runs Jobs and handles catastrophic failures" do
180
+ provider = SknUtils::ConcurrentJobs.call
181
+ provider.register_job do
182
+ SknUtils::JobWrapper.call(command[0], test_proc)
183
+ end
184
+ provider.register_job do # notice `command` vs `commands`
185
+ SknUtils::JobWrapper.call(command[1], inline_failure_proc)
186
+ end
187
+ provider.register_job do
188
+ SknUtils::JobWrapper.call(command[2], catastrophic_proc)
189
+ end
190
+ provider.register_job do
191
+ SknUtils::JobWrapper.call(commands[3], catastrophic_proc)
192
+ end
193
+ provider.register_job do
194
+ SknUtils::JobWrapper.call(commands[4], test_proc)
195
+ end
196
+
197
+ result = provider.render_jobs
198
+
199
+ expect(result).to be_a(SknUtils::Result)
200
+ expect(result.success?).to be false
201
+ expect(result.values.size).to eq(commands.size)
202
+ expect(result.values[3].value).to eq("NameError")
203
+ expect(result.values[3]).to be_a(SknFailure)
204
+ expect(result.values[3]).to be_a(SknFailure)
205
+ expect(result.values[0]).to be nil
206
+ end
207
+ end
208
+
209
+ context "Synchronous" do
210
+ it "Runs Jobs" do
211
+ provider = SknUtils::ConcurrentJobs.call(async: false)
212
+ provider.register_jobs(commands, test_proc)
213
+ result = provider.render_jobs
214
+
215
+ expect(result).to be_a(SknUtils::Result)
216
+ expect(result.success?).to be true
217
+ expect(result.values.size).to eq(commands.size)
218
+ expect(result.values[0]).to be_a(SknSuccess)
219
+ end
220
+ it "Runs Jobs and handles inline failures" do
221
+ provider = SknUtils::ConcurrentJobs.call
222
+
223
+ provider.register_job do
224
+ SknUtils::JobWrapper.call(commands[0], test_proc)
225
+ end
226
+ provider.register_job do
227
+ SknUtils::JobWrapper.call(commands[1], inline_failure_proc)
228
+ end
229
+ provider.register_job do
230
+ SknUtils::JobWrapper.call(commands[2], inline_failure_proc)
231
+ end
232
+ provider.register_job do
233
+ SknUtils::JobWrapper.call(commands[3], inline_failure_proc)
234
+ end
235
+ provider.register_job do
236
+ SknUtils::JobWrapper.call(commands[4], test_proc)
237
+ end
238
+
239
+ result = provider.render_jobs
240
+
241
+ expect(result).to be_a(SknUtils::Result)
242
+ expect(result.success?).to be false
243
+ expect(result.values.size).to eq(commands.size)
244
+ expect(result.values[1]).to be_a(SknFailure)
245
+ expect(result.values[1].message).to eq("Failure")
246
+ end
247
+ it "Runs Jobs and handles catastrophic failures" do
248
+ provider = SknUtils::ConcurrentJobs.call
249
+ provider.register_job do
250
+ SknUtils::JobWrapper.call(commands[0], test_proc)
251
+ end
252
+ provider.register_job do
253
+ SknUtils::JobWrapper.call(commands[1], inline_failure_proc)
254
+ end
255
+ provider.register_job do
256
+ SknUtils::JobWrapper.call(commands[2], catastrophic_proc)
257
+ end
258
+ provider.register_job do
259
+ SknUtils::JobWrapper.call(commands[3], catastrophic_proc)
260
+ end
261
+ provider.register_job do
262
+ SknUtils::JobWrapper.call(commands[4], test_proc)
263
+ end
264
+
265
+ result = provider.render_jobs
266
+
267
+ expect(result).to be_a(SknUtils::Result)
268
+ expect(result.success?).to be false
269
+ expect(result.values.size).to eq(commands.size)
270
+ expect(result.values[3].value).to eq("NameError")
271
+ expect(result.values[2]).to be_a(SknFailure)
272
+ expect(result.values[1]).to be_a(SknFailure)
273
+ expect(result.values[0]).to be_a(SknSuccess)
274
+ end
275
+ end
276
+
277
+ end
data/spec/spec_helper.rb CHANGED
@@ -15,6 +15,8 @@ end
15
15
  require 'skn_utils'
16
16
  require 'rspec'
17
17
 
18
+ require 'webmock/rspec'
19
+
18
20
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
21
  RSpec.configure do |config|
20
22
  Kernel.srand config.seed
@@ -30,6 +32,8 @@ RSpec.configure do |config|
30
32
  # config.disable_monkey_patching! # -- breaks rspec runtime
31
33
  config.warnings = true
32
34
 
35
+ config.include WebMock::API
36
+
33
37
  if config.files_to_run.one?
34
38
  config.formatter = :documentation
35
39
  else
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skn_utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.4.1
4
+ version: 5.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Scott Jr
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-02 00:00:00.000000000 Z
11
+ date: 2019-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deep_merge
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry-coolline
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: simplecov
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +150,20 @@ dependencies:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
152
  version: '2'
153
+ - !ruby/object:Gem::Dependency
154
+ name: webmock
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
139
167
  description: "The intent of the NestedResult class is to be a container for data values
140
168
  composed of key/value pairs, \nwith easy access to its contents, and on-demand transformation
141
169
  back to the hash (#to_hash).\n\nReview the RSpec tests, and or review the README
@@ -156,6 +184,10 @@ files:
156
184
  - Rakefile
157
185
  - _config.yml
158
186
  - bin/bench_nested_result.rb
187
+ - bin/concurrent_test_block
188
+ - bin/concurrent_test_grouped
189
+ - bin/concurrent_test_procs
190
+ - bin/concurrent_test_wrapped
159
191
  - bin/configs/settings.yml
160
192
  - bin/configs/settings/development.yml
161
193
  - bin/configs/settings/production.yml
@@ -172,11 +204,14 @@ files:
172
204
  - lib/skn_settings.rb
173
205
  - lib/skn_success.rb
174
206
  - lib/skn_utils.rb
207
+ - lib/skn_utils/concurrent_jobs.rb
175
208
  - lib/skn_utils/configurable.rb
176
209
  - lib/skn_utils/configuration.rb
177
210
  - lib/skn_utils/core_extensions.rb
178
211
  - lib/skn_utils/dotted_hash.rb
179
212
  - lib/skn_utils/env_string_handler.rb
213
+ - lib/skn_utils/http_processor.rb
214
+ - lib/skn_utils/job_commands.rb
180
215
  - lib/skn_utils/nested_result.rb
181
216
  - lib/skn_utils/notifier_base.rb
182
217
  - lib/skn_utils/null_object.rb
@@ -196,6 +231,7 @@ files:
196
231
  - spec/lib/skn_settings_spec.rb
197
232
  - spec/lib/skn_utils/as_human_size_spec.rb
198
233
  - spec/lib/skn_utils/catch_exceptions_spec.rb
234
+ - spec/lib/skn_utils/concurrent_jobs_spec.rb
199
235
  - spec/lib/skn_utils/configurable_spec.rb
200
236
  - spec/lib/skn_utils/container_spec.rb
201
237
  - spec/lib/skn_utils/nested_result_spec.rb
@@ -246,6 +282,7 @@ test_files:
246
282
  - spec/lib/skn_settings_spec.rb
247
283
  - spec/lib/skn_utils/as_human_size_spec.rb
248
284
  - spec/lib/skn_utils/catch_exceptions_spec.rb
285
+ - spec/lib/skn_utils/concurrent_jobs_spec.rb
249
286
  - spec/lib/skn_utils/configurable_spec.rb
250
287
  - spec/lib/skn_utils/container_spec.rb
251
288
  - spec/lib/skn_utils/nested_result_spec.rb