service_skeleton 1.0.5 → 2.1.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 +4 -4
- data/.github/workflows/ci.yml +6 -2
- data/README.md +2 -54
- data/lib/service_skeleton/generator.rb +1 -1
- data/lib/service_skeleton/hurriable_timer.rb +61 -0
- data/lib/service_skeleton/hurriable_timer_sequence.rb +35 -0
- data/lib/service_skeleton/signal_manager.rb +1 -1
- data/lib/service_skeleton.rb +2 -0
- data/service_skeleton.gemspec +4 -12
- data/ultravisor/lib/ultravisor/child.rb +4 -0
- data/ultravisor/spec/spec_helper.rb +4 -2
- data/ultravisor/spec/ultravisor/child/spawn_spec.rb +112 -3
- metadata +14 -46
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 2eb043ee79208c4b1b457b1b9deb2b5901119339c9789d8a6d511870bc038e46
         | 
| 4 | 
            +
              data.tar.gz: 6848edcb6fe369e5a6ce0dcf823780ee8047f8953b40ea261e03aa0f47314d27
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 43fe6a4e0ad9adb50cd78c09c41063df614a0116c194283710660b24f2877994038bb818e35ad78eb7b72da62c3ec7e75646fc2bfae5c6f85ef5fdd7a5289dba
         | 
| 7 | 
            +
              data.tar.gz: 9aacd383f2d867a51b19adc8c1d501ad383497d13a72de0b8fa9992301b029cd23e60aaa3c72511e98f240ed060dd1dab6b8b38a89a9de3cce19a40c8c696f39
         | 
    
        data/.github/workflows/ci.yml
    CHANGED
    
    | @@ -4,7 +4,8 @@ on: | |
| 4 4 | 
             
              pull_request:
         | 
| 5 5 | 
             
              push:
         | 
| 6 6 | 
             
                branches:
         | 
| 7 | 
            -
                  -  | 
| 7 | 
            +
                  - master
         | 
| 8 | 
            +
                  - main
         | 
| 8 9 |  | 
| 9 10 | 
             
            jobs:
         | 
| 10 11 | 
             
              build:
         | 
| @@ -14,6 +15,9 @@ jobs: | |
| 14 15 | 
             
                  matrix:
         | 
| 15 16 | 
             
                    ruby:
         | 
| 16 17 | 
             
                      - 2.5
         | 
| 18 | 
            +
                      - 2.6
         | 
| 19 | 
            +
                      - 2.7
         | 
| 20 | 
            +
                      - 3.0
         | 
| 17 21 |  | 
| 18 22 | 
             
                steps:
         | 
| 19 23 | 
             
                  - uses: actions/checkout@v2
         | 
| @@ -31,7 +35,7 @@ jobs: | |
| 31 35 | 
             
                    run: bundle exec rake test
         | 
| 32 36 |  | 
| 33 37 | 
             
              publish:
         | 
| 34 | 
            -
                if: github.event_name == 'push' && (github.ref == 'refs/heads/ | 
| 38 | 
            +
                if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
         | 
| 35 39 | 
             
                needs: build
         | 
| 36 40 | 
             
                runs-on: ubuntu-latest
         | 
| 37 41 |  | 
    
        data/README.md
    CHANGED
    
    | @@ -268,7 +268,7 @@ default for a config variable, like so: | |
| 268 268 | 
             
                class GenericHelloService
         | 
| 269 269 | 
             
                  include ServiceSkeleton
         | 
| 270 270 |  | 
| 271 | 
            -
                  string :RECIPIENT, match: /\ | 
| 271 | 
            +
                  string :RECIPIENT, match: /\A\w+\z/, default: "Anonymous Coward"
         | 
| 272 272 |  | 
| 273 273 | 
             
                  # ...
         | 
| 274 274 |  | 
| @@ -448,8 +448,7 @@ portion is the all-uppercase [service name](#the-service-name). | |
| 448 448 |  | 
| 449 449 | 
             
                    INFO,buggy=DEBUG,/noisy/i=ERROR
         | 
| 450 450 |  | 
| 451 | 
            -
              Logging levels can be changed at runtime | 
| 452 | 
            -
              [the HTTP admin interface](#http-admin-interface).
         | 
| 451 | 
            +
              Logging levels can be changed at runtime via [signals](#default-signals).
         | 
| 453 452 |  | 
| 454 453 | 
             
            * **`<SERVICENAME>_LOGSTASH_SERVER`** (string; default `""`) -- if set to a
         | 
| 455 454 | 
             
              non-empty string, we will engage the services of the [loggerstash
         | 
| @@ -686,57 +685,6 @@ When the service is shutdown, all signal handlers will be automatically | |
| 686 685 | 
             
            unhooked, which saves you having to do it yourself.
         | 
| 687 686 |  | 
| 688 687 |  | 
| 689 | 
            -
            ## HTTP Admin Interface
         | 
| 690 | 
            -
             | 
| 691 | 
            -
            In these modern times we live in, it seems everything from nuclear reactors to
         | 
| 692 | 
            -
            toasters can be controlled from a browser.  Why should your services be any
         | 
| 693 | 
            -
            different?
         | 
| 694 | 
            -
             | 
| 695 | 
            -
             | 
| 696 | 
            -
            ### HTTP Admin Configuration
         | 
| 697 | 
            -
             | 
| 698 | 
            -
            In the spirit of "secure by default", you must explicitly enable the HTTP admin
         | 
| 699 | 
            -
            interface, and configure an authentication method.  To do that, use the
         | 
| 700 | 
            -
            following environment variables, where `<SERVICENAME>_` is the all-uppercase
         | 
| 701 | 
            -
            version of [the service name](#the-service-name).
         | 
| 702 | 
            -
             | 
| 703 | 
            -
            * **`<SERVICENAME>_HTTP_ADMIN_PORT`** (integer; range 1..65535; default: `""`)
         | 
| 704 | 
            -
              -- if set to a valid port number (`1` to `65535` inclusive), the HTTP admin
         | 
| 705 | 
            -
              interface will listen on that port, if also enabled by configuring
         | 
| 706 | 
            -
              authentication.
         | 
| 707 | 
            -
             | 
| 708 | 
            -
            * **`<SERVICENAME>_HTTP_ADMIN_BASIC_AUTH`** (string; default: `""`) -- if set
         | 
| 709 | 
            -
              to a string containing a username and password separated by a colon, then
         | 
| 710 | 
            -
              authentication via [HTTP Basic auth](https://tools.ietf.org/html/rfc7617)
         | 
| 711 | 
            -
              will be supported.  Note that in addition to this setting, an admin port must
         | 
| 712 | 
            -
              also be specified in order for the admin interface to be enabled.
         | 
| 713 | 
            -
             | 
| 714 | 
            -
            * **`<SERVICENAME>_HTTP_ADMIN_PROXY_USERNAME_HEADER`** (string; default: `""`)
         | 
| 715 | 
            -
              -- if set to a non-empty string, then incoming requests will be examined for
         | 
| 716 | 
            -
              a HTTP header with the specified name.  If such a header exists and has a
         | 
| 717 | 
            -
              non-empty value, then the request will be deemed to have been authenticated
         | 
| 718 | 
            -
              by an upstream authenticating proxy (such as
         | 
| 719 | 
            -
              [`discourse-auth-proxy`](https://github.com/discourse/discourse-auth-proxy))
         | 
| 720 | 
            -
              as the user given in the header value.  Note that in addition to this
         | 
| 721 | 
            -
              setting, an admin port must also be specified in order for the admin
         | 
| 722 | 
            -
              interface to be enabled.
         | 
| 723 | 
            -
             | 
| 724 | 
            -
             | 
| 725 | 
            -
            ### HTTP Admin Usage
         | 
| 726 | 
            -
             | 
| 727 | 
            -
            The HTTP admin interface provides both an interactive, browser-based mode,
         | 
| 728 | 
            -
            as well as a RESTful interface, which should, in general, provide equivalent
         | 
| 729 | 
            -
            functionality.
         | 
| 730 | 
            -
             | 
| 731 | 
            -
            * Visiting the service's `IP address:port` in a web browser will bring up an HTML
         | 
| 732 | 
            -
              interface showing all the features that are available.  Usage should
         | 
| 733 | 
            -
              (hopefully) be self-explanatory.
         | 
| 734 | 
            -
             | 
| 735 | 
            -
            * Visiting the service's `IP address:port` whilst accepting `application/json`
         | 
| 736 | 
            -
              responses will provide a directory of links to available endpoints which you
         | 
| 737 | 
            -
              can use to interact with the HTTP admin interface programmatically.
         | 
| 738 | 
            -
             | 
| 739 | 
            -
             | 
| 740 688 | 
             
            # Contributing
         | 
| 741 689 |  | 
| 742 690 | 
             
            Patches can be sent as [a Github pull
         | 
| @@ -16,8 +16,8 @@ module ServiceSkeleton | |
| 16 16 | 
             
              module Generator
         | 
| 17 17 | 
             
                def generate(config:, metrics_registry:, service_metrics:, service_signal_handlers:)
         | 
| 18 18 | 
             
                  Ultravisor.new(logger: config.logger).tap do |ultravisor|
         | 
| 19 | 
            -
                    initialize_metrics(ultravisor, config, metrics_registry, service_metrics)
         | 
| 20 19 | 
             
                    initialize_loggerstash(ultravisor, config, metrics_registry)
         | 
| 20 | 
            +
                    initialize_metrics(ultravisor, config, metrics_registry, service_metrics)
         | 
| 21 21 | 
             
                    initialize_signals(ultravisor, config, service_signal_handlers, metrics_registry)
         | 
| 22 22 | 
             
                  end
         | 
| 23 23 | 
             
                end
         | 
| @@ -0,0 +1,61 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # A mechanism for waiting until a timer expires or until another thread signals
         | 
| 4 | 
            +
            # readiness.
         | 
| 5 | 
            +
            class ServiceSkeleton::HurriableTimer
         | 
| 6 | 
            +
              def initialize(timeout)
         | 
| 7 | 
            +
                @mutex = Mutex.new
         | 
| 8 | 
            +
                @condition = ConditionVariable.new
         | 
| 9 | 
            +
                @end_time = now + timeout
         | 
| 10 | 
            +
                @hurried = false
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              # Wait for the timer to elapse
         | 
| 14 | 
            +
              #
         | 
| 15 | 
            +
              # Any number of threads can wait on the same HurriableTimer
         | 
| 16 | 
            +
              def wait(t = nil)
         | 
| 17 | 
            +
                end_time =
         | 
| 18 | 
            +
                  if t
         | 
| 19 | 
            +
                    [@end_time, now + t].min
         | 
| 20 | 
            +
                  else
         | 
| 21 | 
            +
                    @end_time
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                @mutex.synchronize {
         | 
| 25 | 
            +
                  while true
         | 
| 26 | 
            +
                    remaining = end_time - now
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    if remaining < 0 || @hurried
         | 
| 29 | 
            +
                      break
         | 
| 30 | 
            +
                    else
         | 
| 31 | 
            +
                      @condition.wait(@mutex, remaining)
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                nil
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              # Cause the timer to trigger early if it hasn't already expired
         | 
| 40 | 
            +
              #
         | 
| 41 | 
            +
              # This method is idempotent
         | 
| 42 | 
            +
              def hurry!
         | 
| 43 | 
            +
                @mutex.synchronize {
         | 
| 44 | 
            +
                  @hurried = true
         | 
| 45 | 
            +
                  @condition.broadcast
         | 
| 46 | 
            +
                }
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                nil
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              def expired?
         | 
| 52 | 
            +
                @hurried || @end_time - now < 0
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              private
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              def now
         | 
| 58 | 
            +
                # Using this instead of Time.now, because it isn't affected by NTP updates
         | 
| 59 | 
            +
                Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW)
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # HurribleTimerSequence is a resettable version of HurriableTimer, designed for
         | 
| 4 | 
            +
            # cases where some action needs to happen at at least some frequency, but may
         | 
| 5 | 
            +
            # happen more often when other threads trigger the process early.
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            # It would have been possible to implement this without requiring allocation on
         | 
| 8 | 
            +
            # reset, by reusing the mutex and condition variable in the normal timer, but
         | 
| 9 | 
            +
            # this version is more obviously correct.
         | 
| 10 | 
            +
            class ServiceSkeleton::HurriableTimerSequence
         | 
| 11 | 
            +
              def initialize(timeout)
         | 
| 12 | 
            +
                @mutex = Mutex.new
         | 
| 13 | 
            +
                @timeout = timeout
         | 
| 14 | 
            +
                @latest = ServiceSkeleton::HurriableTimer.new(@timeout)
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              def reset!
         | 
| 18 | 
            +
                @mutex.synchronize {
         | 
| 19 | 
            +
                  @latest.hurry!
         | 
| 20 | 
            +
                  @latest = ServiceSkeleton::HurriableTimer.new(@timeout)
         | 
| 21 | 
            +
                }
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              def wait(t = nil)
         | 
| 25 | 
            +
                @mutex.synchronize { @latest }.wait(t)
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              def hurry!
         | 
| 29 | 
            +
                @mutex.synchronize { @latest }.hurry!
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def expired?
         | 
| 33 | 
            +
                @mutex.synchronize { @latest }.expired?
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
    
        data/lib/service_skeleton.rb
    CHANGED
    
    | @@ -3,6 +3,8 @@ | |
| 3 3 | 
             
            require_relative "service_skeleton/config_class"
         | 
| 4 4 | 
             
            require_relative "service_skeleton/config_variables"
         | 
| 5 5 | 
             
            require_relative "service_skeleton/generator"
         | 
| 6 | 
            +
            require_relative "service_skeleton/hurriable_timer"
         | 
| 7 | 
            +
            require_relative "service_skeleton/hurriable_timer_sequence"
         | 
| 6 8 | 
             
            require_relative "service_skeleton/logging_helpers"
         | 
| 7 9 | 
             
            require_relative "service_skeleton/metrics_methods"
         | 
| 8 10 | 
             
            require_relative "service_skeleton/service_name"
         | 
    
        data/service_skeleton.gemspec
    CHANGED
    
    | @@ -1,15 +1,9 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            begin
         | 
| 4 | 
            -
              require 'git-version-bump'
         | 
| 5 | 
            -
            rescue LoadError
         | 
| 6 | 
            -
              nil
         | 
| 7 | 
            -
            end
         | 
| 8 | 
            -
             | 
| 9 3 | 
             
            Gem::Specification.new do |s|
         | 
| 10 4 | 
             
              s.name = "service_skeleton"
         | 
| 11 5 |  | 
| 12 | 
            -
              s.version = '1.0 | 
| 6 | 
            +
              s.version = '2.1.0'
         | 
| 13 7 |  | 
| 14 8 | 
             
              s.platform = Gem::Platform::RUBY
         | 
| 15 9 |  | 
| @@ -32,22 +26,20 @@ Gem::Specification.new do |s| | |
| 32 26 | 
             
              s.required_ruby_version = ">= 2.5.0"
         | 
| 33 27 |  | 
| 34 28 | 
             
              s.add_runtime_dependency "frankenstein", "~> 2.0"
         | 
| 35 | 
            -
              s.add_runtime_dependency "loggerstash", " | 
| 29 | 
            +
              s.add_runtime_dependency "loggerstash", "~> 1"
         | 
| 36 30 | 
             
              s.add_runtime_dependency "prometheus-client", "~> 2.0"
         | 
| 37 31 | 
             
              s.add_runtime_dependency "sigdump", "~> 0.2"
         | 
| 38 32 | 
             
              s.add_runtime_dependency "to_regexp", "~> 0.2"
         | 
| 33 | 
            +
              s.add_runtime_dependency "webrick"
         | 
| 39 34 |  | 
| 40 35 | 
             
              s.add_development_dependency 'bundler'
         | 
| 41 | 
            -
              s.add_development_dependency 'github-release'
         | 
| 42 | 
            -
              s.add_development_dependency 'git-version-bump'
         | 
| 43 36 | 
             
              s.add_development_dependency 'guard-rspec'
         | 
| 44 37 | 
             
              s.add_development_dependency 'guard-rubocop'
         | 
| 45 38 | 
             
              s.add_development_dependency 'rack-test'
         | 
| 46 39 | 
             
              s.add_development_dependency 'rake'
         | 
| 47 40 | 
             
              s.add_development_dependency 'redcarpet'
         | 
| 48 41 | 
             
              s.add_development_dependency 'rspec'
         | 
| 49 | 
            -
              s.add_development_dependency 'rubocop'
         | 
| 50 | 
            -
              s.add_development_dependency 'rubocop-discourse'
         | 
| 42 | 
            +
              s.add_development_dependency 'rubocop-discourse', '~> 2.4.1'
         | 
| 51 43 | 
             
              s.add_development_dependency 'simplecov'
         | 
| 52 44 | 
             
              s.add_development_dependency 'yard'
         | 
| 53 45 | 
             
            end
         | 
| @@ -381,6 +381,10 @@ class Ultravisor | |
| 381 381 | 
             
                  # urgh.
         | 
| 382 382 | 
             
                  if @klass.instance_method(:initialize).arity == 0
         | 
| 383 383 | 
             
                    @klass.new()
         | 
| 384 | 
            +
                  elsif @args.is_a?(Hash)
         | 
| 385 | 
            +
                    @klass.new(**@args)
         | 
| 386 | 
            +
                  elsif @args.last.is_a?(Hash)
         | 
| 387 | 
            +
                    @klass.new(*@args[0...-1], **@args.last)
         | 
| 384 388 | 
             
                  else
         | 
| 385 389 | 
             
                    @klass.new(*@args)
         | 
| 386 390 | 
             
                  end.tap do |i|
         | 
| @@ -93,14 +93,123 @@ describe Ultravisor::Child do | |
| 93 93 | 
             
                  end
         | 
| 94 94 | 
             
                end
         | 
| 95 95 |  | 
| 96 | 
            -
                context "with a worker class that takes args" do
         | 
| 96 | 
            +
                context "with a worker class that takes only non-keyword args" do
         | 
| 97 | 
            +
                  let(:args) { { id: :testy, klass: mock_class, args: ["foo", "bar"], method: :run } }
         | 
| 98 | 
            +
                  let(:mock_class) do
         | 
| 99 | 
            +
                    Class.new do
         | 
| 100 | 
            +
                      def initialize(foo, bar)
         | 
| 101 | 
            +
                      end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                      def run
         | 
| 104 | 
            +
                      end
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                  it "creates the class instance with args" do
         | 
| 109 | 
            +
                    allow(mock_class).to receive(:new).and_call_original
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    child.spawn(term_queue).wait
         | 
| 112 | 
            +
                    expect(mock_class).to have_received(:new).with("foo", "bar")
         | 
| 113 | 
            +
                  end
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                context "with a worker class that takes non-keyword and optional args" do
         | 
| 97 117 | 
             
                  let(:args) { { id: :testy, klass: mock_class, args: ["foo", "bar", baz: "wombat"], method: :run } }
         | 
| 98 | 
            -
                  let(:mock_class)  | 
| 118 | 
            +
                  let(:mock_class) do
         | 
| 119 | 
            +
                    Class.new do
         | 
| 120 | 
            +
                      def initialize(foo, bar, baz = {})
         | 
| 121 | 
            +
                      end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                      def run
         | 
| 124 | 
            +
                      end
         | 
| 125 | 
            +
                    end
         | 
| 126 | 
            +
                  end
         | 
| 99 127 |  | 
| 100 128 | 
             
                  it "creates the class instance with args" do
         | 
| 101 | 
            -
                     | 
| 129 | 
            +
                    allow(mock_class).to receive(:new).and_call_original
         | 
| 102 130 |  | 
| 103 131 | 
             
                    child.spawn(term_queue).wait
         | 
| 132 | 
            +
                    expect(mock_class).to have_received(:new).with("foo", "bar", { baz: "wombat" })
         | 
| 133 | 
            +
                  end
         | 
| 134 | 
            +
                end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                context "with a worker class that takes mixed args" do
         | 
| 137 | 
            +
                  let(:args) { { id: :testy, klass: mock_class, args: ["foo", "bar", baz: "wombat"], method: :run } }
         | 
| 138 | 
            +
                  let(:mock_class) do
         | 
| 139 | 
            +
                    Class.new do
         | 
| 140 | 
            +
                      def initialize(foo, bar, baz:)
         | 
| 141 | 
            +
                      end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                      def run
         | 
| 144 | 
            +
                      end
         | 
| 145 | 
            +
                    end
         | 
| 146 | 
            +
                  end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  it "creates the class instance with args" do
         | 
| 149 | 
            +
                    allow(mock_class).to receive(:new).and_call_original
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                    child.spawn(term_queue).wait
         | 
| 152 | 
            +
                    expect(mock_class).to have_received(:new).with("foo", "bar", baz: "wombat")
         | 
| 153 | 
            +
                  end
         | 
| 154 | 
            +
                end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                context "with a worker class that takes keyword args" do
         | 
| 157 | 
            +
                  let(:args) { { id: :testy, klass: mock_class, args: [foo: "bar", baz: "wombat"], method: :run } }
         | 
| 158 | 
            +
                  let(:mock_class) do
         | 
| 159 | 
            +
                    Class.new do
         | 
| 160 | 
            +
                      def initialize(foo:, baz:)
         | 
| 161 | 
            +
                      end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                      def run
         | 
| 164 | 
            +
                      end
         | 
| 165 | 
            +
                    end
         | 
| 166 | 
            +
                  end
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                  it "creates the class instance with args" do
         | 
| 169 | 
            +
                    allow(mock_class).to receive(:new).and_call_original
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                    child.spawn(term_queue).wait
         | 
| 172 | 
            +
                    expect(mock_class).to have_received(:new).with(foo: "bar", baz: "wombat")
         | 
| 173 | 
            +
                  end
         | 
| 174 | 
            +
                end
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                context "with a worker class that takes optional args" do
         | 
| 177 | 
            +
                  let(:args) { { id: :testy, klass: mock_class, args: [foo: "bar", baz: "wombat", fib: "wib", woop: "woob"], method: :run } }
         | 
| 178 | 
            +
                  let(:mock_class) do
         | 
| 179 | 
            +
                    Class.new do
         | 
| 180 | 
            +
                      def initialize(foo:, baz:, fib: "none", woop: nil)
         | 
| 181 | 
            +
                      end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                      def run
         | 
| 184 | 
            +
                      end
         | 
| 185 | 
            +
                    end
         | 
| 186 | 
            +
                  end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                  it "creates the class instance with args" do
         | 
| 189 | 
            +
                    allow(mock_class).to receive(:new).and_call_original
         | 
| 190 | 
            +
                    child.spawn(term_queue).wait
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                    expect(mock_class).to have_received(:new).with(foo: "bar", baz: "wombat", fib: "wib", woop: "woob")
         | 
| 193 | 
            +
                  end
         | 
| 194 | 
            +
                end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                context "with a worker class that takes keyword args in form of a hash" do
         | 
| 197 | 
            +
                  let(:args) { { id: :testy, klass: mock_class, args: { foo: "bar", baz: "wombat" }, method: :run } }
         | 
| 198 | 
            +
                  let(:mock_class) do
         | 
| 199 | 
            +
                    Class.new do
         | 
| 200 | 
            +
                      def initialize(foo:, baz:)
         | 
| 201 | 
            +
                      end
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                      def run
         | 
| 204 | 
            +
                      end
         | 
| 205 | 
            +
                    end
         | 
| 206 | 
            +
                  end
         | 
| 207 | 
            +
             | 
| 208 | 
            +
                  it "creates the class instance with args" do
         | 
| 209 | 
            +
                    allow(mock_class).to receive(:new).and_call_original
         | 
| 210 | 
            +
                    child.spawn(term_queue).wait
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                    expect(mock_class).to have_received(:new).with(foo: "bar", baz: "wombat")
         | 
| 104 213 | 
             
                  end
         | 
| 105 214 | 
             
                end
         | 
| 106 215 | 
             
              end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: service_skeleton
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.0 | 
| 4 | 
            +
              version: 2.1.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Matt Palmer
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date:  | 
| 12 | 
            +
            date: 2022-01-25 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: frankenstein
         | 
| @@ -29,20 +29,14 @@ dependencies: | |
| 29 29 | 
             
              name: loggerstash
         | 
| 30 30 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 31 31 | 
             
                requirements:
         | 
| 32 | 
            -
                - - " | 
| 33 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 34 | 
            -
                    version: 0.0.9
         | 
| 35 | 
            -
                - - "<"
         | 
| 32 | 
            +
                - - "~>"
         | 
| 36 33 | 
             
                  - !ruby/object:Gem::Version
         | 
| 37 34 | 
             
                    version: '1'
         | 
| 38 35 | 
             
              type: :runtime
         | 
| 39 36 | 
             
              prerelease: false
         | 
| 40 37 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 41 38 | 
             
                requirements:
         | 
| 42 | 
            -
                - - " | 
| 43 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 44 | 
            -
                    version: 0.0.9
         | 
| 45 | 
            -
                - - "<"
         | 
| 39 | 
            +
                - - "~>"
         | 
| 46 40 | 
             
                  - !ruby/object:Gem::Version
         | 
| 47 41 | 
             
                    version: '1'
         | 
| 48 42 | 
             
            - !ruby/object:Gem::Dependency
         | 
| @@ -88,27 +82,13 @@ dependencies: | |
| 88 82 | 
             
                  - !ruby/object:Gem::Version
         | 
| 89 83 | 
             
                    version: '0.2'
         | 
| 90 84 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 91 | 
            -
              name:  | 
| 85 | 
            +
              name: webrick
         | 
| 92 86 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 93 87 | 
             
                requirements:
         | 
| 94 88 | 
             
                - - ">="
         | 
| 95 89 | 
             
                  - !ruby/object:Gem::Version
         | 
| 96 90 | 
             
                    version: '0'
         | 
| 97 | 
            -
              type: : | 
| 98 | 
            -
              prerelease: false
         | 
| 99 | 
            -
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 100 | 
            -
                requirements:
         | 
| 101 | 
            -
                - - ">="
         | 
| 102 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 103 | 
            -
                    version: '0'
         | 
| 104 | 
            -
            - !ruby/object:Gem::Dependency
         | 
| 105 | 
            -
              name: github-release
         | 
| 106 | 
            -
              requirement: !ruby/object:Gem::Requirement
         | 
| 107 | 
            -
                requirements:
         | 
| 108 | 
            -
                - - ">="
         | 
| 109 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 110 | 
            -
                    version: '0'
         | 
| 111 | 
            -
              type: :development
         | 
| 91 | 
            +
              type: :runtime
         | 
| 112 92 | 
             
              prerelease: false
         | 
| 113 93 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 114 94 | 
             
                requirements:
         | 
| @@ -116,7 +96,7 @@ dependencies: | |
| 116 96 | 
             
                  - !ruby/object:Gem::Version
         | 
| 117 97 | 
             
                    version: '0'
         | 
| 118 98 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 119 | 
            -
              name:  | 
| 99 | 
            +
              name: bundler
         | 
| 120 100 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 121 101 | 
             
                requirements:
         | 
| 122 102 | 
             
                - - ">="
         | 
| @@ -213,34 +193,20 @@ dependencies: | |
| 213 193 | 
             
                - - ">="
         | 
| 214 194 | 
             
                  - !ruby/object:Gem::Version
         | 
| 215 195 | 
             
                    version: '0'
         | 
| 216 | 
            -
            - !ruby/object:Gem::Dependency
         | 
| 217 | 
            -
              name: rubocop
         | 
| 218 | 
            -
              requirement: !ruby/object:Gem::Requirement
         | 
| 219 | 
            -
                requirements:
         | 
| 220 | 
            -
                - - ">="
         | 
| 221 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 222 | 
            -
                    version: '0'
         | 
| 223 | 
            -
              type: :development
         | 
| 224 | 
            -
              prerelease: false
         | 
| 225 | 
            -
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 226 | 
            -
                requirements:
         | 
| 227 | 
            -
                - - ">="
         | 
| 228 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 229 | 
            -
                    version: '0'
         | 
| 230 196 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 231 197 | 
             
              name: rubocop-discourse
         | 
| 232 198 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 233 199 | 
             
                requirements:
         | 
| 234 | 
            -
                - - " | 
| 200 | 
            +
                - - "~>"
         | 
| 235 201 | 
             
                  - !ruby/object:Gem::Version
         | 
| 236 | 
            -
                    version:  | 
| 202 | 
            +
                    version: 2.4.1
         | 
| 237 203 | 
             
              type: :development
         | 
| 238 204 | 
             
              prerelease: false
         | 
| 239 205 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 240 206 | 
             
                requirements:
         | 
| 241 | 
            -
                - - " | 
| 207 | 
            +
                - - "~>"
         | 
| 242 208 | 
             
                  - !ruby/object:Gem::Version
         | 
| 243 | 
            -
                    version:  | 
| 209 | 
            +
                    version: 2.4.1
         | 
| 244 210 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 245 211 | 
             
              name: simplecov
         | 
| 246 212 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -309,6 +275,8 @@ files: | |
| 309 275 | 
             
            - lib/service_skeleton/error.rb
         | 
| 310 276 | 
             
            - lib/service_skeleton/filtering_logger.rb
         | 
| 311 277 | 
             
            - lib/service_skeleton/generator.rb
         | 
| 278 | 
            +
            - lib/service_skeleton/hurriable_timer.rb
         | 
| 279 | 
            +
            - lib/service_skeleton/hurriable_timer_sequence.rb
         | 
| 312 280 | 
             
            - lib/service_skeleton/logging_helpers.rb
         | 
| 313 281 | 
             
            - lib/service_skeleton/metric_method_name.rb
         | 
| 314 282 | 
             
            - lib/service_skeleton/metrics_methods.rb
         | 
| @@ -368,7 +336,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 368 336 | 
             
                - !ruby/object:Gem::Version
         | 
| 369 337 | 
             
                  version: '0'
         | 
| 370 338 | 
             
            requirements: []
         | 
| 371 | 
            -
            rubygems_version: 3.1. | 
| 339 | 
            +
            rubygems_version: 3.1.6
         | 
| 372 340 | 
             
            signing_key: 
         | 
| 373 341 | 
             
            specification_version: 4
         | 
| 374 342 | 
             
            summary: The bare bones of a service
         |