iprog_worker_heartbeat 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 535a2156f09c87a51fa7f4492795bfa1e3844b847d53972f72920e0e851395c9
4
+ data.tar.gz: a2124c7edaa42932a50a7cd74421aa80dd1b7d07c7bc18bd09fe80d14d2b9a52
5
+ SHA512:
6
+ metadata.gz: 54fddb98ba7ce36ecdd97629f941b48469ad314dbe204541b68eb3e164832c811802a63f168cf6fb41a400f8e6d3bf45f210709f7b04b14c1bdefc702614adde
7
+ data.tar.gz: 1c5d11906a0e6cadc575f5dfb65e55b2146a3e1e6bee067325d455d1c7b20cd39ccc3d2d82c552196cff95689e5059caf04b474f435ded5e0c488e1dbb5be086
data/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## [0.1.1] - 2026-03-07
4
+ - Improve generated initializer: prefer `ENV` then Rails credentials for `auth_token` with local manual fallback.
5
+ - Keep only `auth_token` and `callback_url` active by default; comment optional settings.
6
+ - Align README configuration example with generated initializer defaults.
7
+
8
+ ## [0.1.0] - 2026-03-07
9
+ - Initial release with Rails engine, endpoint, logging model, delayed callback job, generator, and failure email alerts.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 IPROG TECH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # IprogWorkerHeartbeat
2
+
3
+ `iprog_worker_heartbeat` provides a Rails engine for worker heartbeat client apps.
4
+
5
+ ## Features
6
+
7
+ - `POST /worker-heartbeat` endpoint
8
+ - Bearer token + `worker_token` validation
9
+ - Persistence in `worker_heartbeats` table
10
+ - Delayed callback job (default 5 minutes)
11
+ - Failure-only email alerts
12
+
13
+ ## Installation
14
+
15
+ Add gem to your app:
16
+
17
+ ```ruby
18
+ gem "iprog_worker_heartbeat"
19
+ ```
20
+
21
+ Then run:
22
+
23
+ ```bash
24
+ bin/rails generate iprog_worker_heartbeat:install
25
+ bin/rails db:migrate
26
+ ```
27
+
28
+ ## Configuration
29
+
30
+ Edit `config/initializers/iprog_worker_heartbeat.rb`:
31
+
32
+ ```ruby
33
+ IprogWorkerHeartbeat.configure do |config|
34
+ config.auth_token = ENV["IPROG_WORKER_HEARTBEAT_AUTH_TOKEN"].presence ||
35
+ Rails.application.credentials.dig(:iprog_worker_heartbeat, :auth_token).presence ||
36
+ "local-heartbeat-token"
37
+ config.callback_url = "https://www.iprogtech.com/worker-heartbeat"
38
+ # config.failure_email_recipients = ["admin@iprogtech.com"]
39
+ # config.mailer_from = "IPROG Worker Heartbeat <admin@iprogtech.com>"
40
+ # config.mail_subject_prefix = "[IPROGSMS Worker Heartbeat]"
41
+ # config.notify_on_auth_failure = true
42
+ # config.callback_delay_seconds = 300
43
+ # config.max_retries = 3
44
+ # config.retry_wait_seconds = 60
45
+ end
46
+ ```
47
+
48
+ ## API Contract
49
+
50
+ - `POST /worker-heartbeat`
51
+ - Header: `Authorization: Bearer <auth_token>`
52
+ - Body: `{ "worker_token": "..." }`
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IprogWorkerHeartbeat
4
+ class HeartbeatsController < ActionController::API
5
+ def create
6
+ return render json: { ok: false, error: "Missing bearer token." }, status: :unauthorized if bearer_token.blank?
7
+ return invalid_auth! unless valid_bearer_token?
8
+
9
+ worker_token = heartbeat_params[:worker_token].to_s.strip
10
+ if worker_token.blank?
11
+ notify_failure("invalid_payload", "Missing worker_token.")
12
+ return render json: { ok: false, error: "Missing worker_token." }, status: :unprocessable_entity
13
+ end
14
+
15
+ heartbeat = IprogWorkerHeartbeat::Heartbeat.find_or_initialize_by(worker_token: worker_token)
16
+ if heartbeat.new_record?
17
+ heartbeat.assign_attributes(status: :pending, received_at: Time.current)
18
+ heartbeat.save!
19
+ IprogWorkerHeartbeat::CallbackJob.set(wait: IprogWorkerHeartbeat.config.callback_delay_seconds.to_i.seconds).perform_later(heartbeat.id)
20
+ return render json: { ok: true, queued: true }, status: :accepted
21
+ end
22
+
23
+ render json: { ok: true, queued: heartbeat.pending?, status: heartbeat.status }
24
+ rescue StandardError => e
25
+ notify_failure("controller_error", e.message)
26
+ render json: { ok: false, error: "Unable to process worker heartbeat." }, status: :internal_server_error
27
+ end
28
+
29
+ private
30
+
31
+ def heartbeat_params
32
+ params.permit(:worker_token)
33
+ end
34
+
35
+ def bearer_token
36
+ header = request.authorization.to_s
37
+ return "" unless header.start_with?("Bearer ")
38
+
39
+ header.delete_prefix("Bearer ").strip
40
+ end
41
+
42
+ def valid_bearer_token?
43
+ expected = IprogWorkerHeartbeat.config.auth_token.to_s
44
+ return false if expected.blank?
45
+ return false unless bearer_token.bytesize == expected.bytesize
46
+
47
+ ActiveSupport::SecurityUtils.secure_compare(bearer_token, expected)
48
+ end
49
+
50
+ def invalid_auth!
51
+ notify_failure("auth_failure", "Invalid bearer token") if IprogWorkerHeartbeat.config.notify_on_auth_failure
52
+ render json: { ok: false, error: "Invalid bearer token." }, status: :unauthorized
53
+ end
54
+
55
+ def notify_failure(event, details)
56
+ return if IprogWorkerHeartbeat.config.failure_email_recipients_array.empty?
57
+
58
+ IprogWorkerHeartbeat::FailureMailer.notify(heartbeat: nil, event: event, details: details).deliver_now
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IprogWorkerHeartbeat
4
+ class CallbackJob < ActiveJob::Base
5
+ queue_as :default
6
+
7
+ def perform(heartbeat_id)
8
+ heartbeat = IprogWorkerHeartbeat::Heartbeat.find_by(id: heartbeat_id)
9
+ return if heartbeat.nil? || heartbeat.callback_sent?
10
+
11
+ heartbeat.with_lock do
12
+ return unless heartbeat.pending? || heartbeat.failed?
13
+
14
+ attempts = heartbeat.attempts_count + 1
15
+ heartbeat.update!(attempts_count: attempts, failure_reason: nil)
16
+
17
+ IprogWorkerHeartbeat::CallbackClient.deliver!(
18
+ url: IprogWorkerHeartbeat.config.callback_url,
19
+ bearer_token: IprogWorkerHeartbeat.config.auth_token,
20
+ worker_token: heartbeat.worker_token
21
+ )
22
+
23
+ heartbeat.update!(status: :callback_sent, callback_sent_at: Time.current)
24
+ end
25
+ rescue StandardError => e
26
+ handle_failure(heartbeat, e)
27
+ end
28
+
29
+ private
30
+
31
+ def handle_failure(heartbeat, error)
32
+ return if heartbeat.nil?
33
+
34
+ heartbeat.update(status: :failed, failure_reason: error.message.to_s.truncate(255))
35
+ IprogWorkerHeartbeat::FailureMailer.notify(
36
+ heartbeat: heartbeat,
37
+ event: "callback_failed",
38
+ details: error.message.to_s
39
+ ).deliver_now
40
+
41
+ if heartbeat.attempts_count < IprogWorkerHeartbeat.config.max_retries.to_i
42
+ self.class.set(wait: IprogWorkerHeartbeat.config.retry_wait_seconds.to_i.seconds).perform_later(heartbeat.id)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IprogWorkerHeartbeat
4
+ class FailureMailer < ActionMailer::Base
5
+ def notify(heartbeat:, event:, details:)
6
+ @heartbeat = heartbeat
7
+ @event = event
8
+ @details = details
9
+
10
+ recipients = IprogWorkerHeartbeat.config.failure_email_recipients_array
11
+ return if recipients.empty?
12
+
13
+ mail(
14
+ to: recipients,
15
+ from: mailer_from,
16
+ subject: "#{IprogWorkerHeartbeat.config.mail_subject_prefix} #{event}"
17
+ )
18
+ end
19
+
20
+ private
21
+
22
+ def mailer_from
23
+ IprogWorkerHeartbeat.config.mailer_from.to_s.presence || "IPROG Worker Heartbeat <admin@iprogtech.com>"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IprogWorkerHeartbeat
4
+ class Heartbeat < ApplicationRecord
5
+ self.table_name = "worker_heartbeats"
6
+
7
+ enum :status, {
8
+ pending: "pending",
9
+ callback_sent: "callback_sent",
10
+ failed: "failed"
11
+ }, default: :pending, validate: true
12
+
13
+ validates :worker_token, presence: true, uniqueness: true
14
+ validates :received_at, presence: true
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+ require "uri"
6
+
7
+ module IprogWorkerHeartbeat
8
+ class CallbackClient
9
+ def self.deliver!(url:, bearer_token:, worker_token:)
10
+ uri = URI.parse(url)
11
+ request = Net::HTTP::Post.new(uri.request_uri.presence || "/", "Content-Type" => "application/json")
12
+ request["Authorization"] = "Bearer #{bearer_token}"
13
+ request.body = JSON.generate(worker_token:)
14
+
15
+ response = Net::HTTP.start(
16
+ uri.host,
17
+ uri.port,
18
+ use_ssl: uri.scheme == "https",
19
+ open_timeout: 5,
20
+ read_timeout: 10
21
+ ) { |http| http.request(request) }
22
+
23
+ return response if response.is_a?(Net::HTTPSuccess)
24
+
25
+ raise "worker heartbeat callback failed with status=#{response.code}"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,8 @@
1
+ <p>Worker heartbeat failure event detected.</p>
2
+ <ul>
3
+ <li><strong>Event:</strong> <%= @event %></li>
4
+ <li><strong>Worker Token:</strong> <%= @heartbeat&.worker_token || "N/A" %></li>
5
+ <li><strong>Status:</strong> <%= @heartbeat&.status || "N/A" %></li>
6
+ <li><strong>Attempt Count:</strong> <%= @heartbeat&.attempts_count || 0 %></li>
7
+ <li><strong>Details:</strong> <%= @details %></li>
8
+ </ul>
@@ -0,0 +1,7 @@
1
+ Worker heartbeat failure event detected.
2
+
3
+ Event: <%= @event %>
4
+ Worker Token: <%= @heartbeat&.worker_token || "N/A" %>
5
+ Status: <%= @heartbeat&.status || "N/A" %>
6
+ Attempt Count: <%= @heartbeat&.attempts_count || 0 %>
7
+ Details: <%= @details %>
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ IprogWorkerHeartbeat::Engine.routes.draw do
4
+ post "/worker-heartbeat", to: "heartbeats#create"
5
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/migration"
5
+
6
+ module IprogWorkerHeartbeat
7
+ module Generators
8
+ class InstallGenerator < Rails::Generators::Base
9
+ include Rails::Generators::Migration
10
+
11
+ source_root File.expand_path("templates", __dir__)
12
+ desc "Installs iprog_worker_heartbeat initializer, route mount, and migration"
13
+
14
+ def copy_initializer
15
+ template "iprog_worker_heartbeat_initializer.rb", "config/initializers/iprog_worker_heartbeat.rb"
16
+ end
17
+
18
+ def add_mount_route
19
+ route_line = 'mount IprogWorkerHeartbeat::Engine => "/"'
20
+ routes_file = "config/routes.rb"
21
+ return if File.read(routes_file).include?(route_line)
22
+
23
+ inject_into_file routes_file, " #{route_line}\n", after: "Rails.application.routes.draw do\n"
24
+ end
25
+
26
+ def create_migration_file
27
+ migration_template "create_worker_heartbeats.rb", "db/migrate/create_worker_heartbeats.rb"
28
+ end
29
+
30
+ def self.next_migration_number(dirname)
31
+ if ActiveRecord::Base.timestamped_migrations
32
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
33
+ else
34
+ format("%03d", current_migration_number(dirname) + 1)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateWorkerHeartbeats < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :worker_heartbeats do |t|
6
+ t.string :worker_token, null: false
7
+ t.string :status, null: false, default: "pending"
8
+ t.datetime :received_at, null: false
9
+ t.datetime :callback_sent_at
10
+ t.integer :attempts_count, null: false, default: 0
11
+ t.string :failure_reason
12
+
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :worker_heartbeats, :worker_token, unique: true
17
+ add_index :worker_heartbeats, :status
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ IprogWorkerHeartbeat.configure do |config|
4
+ config.auth_token = ENV["IPROG_WORKER_HEARTBEAT_AUTH_TOKEN"].presence ||
5
+ Rails.application.credentials.dig(:iprog_worker_heartbeat, :auth_token).presence ||
6
+ "local-heartbeat-token"
7
+ config.callback_url = "https://www.iprogtech.com/worker-heartbeat"
8
+ # config.failure_email_recipients = ["admin@iprogtech.com"]
9
+ # config.mailer_from = "IPROG Worker Heartbeat <admin@iprogtech.com>"
10
+ # config.mail_subject_prefix = "[IPROGSMS Worker Heartbeat]"
11
+ # config.notify_on_auth_failure = true
12
+ # config.callback_delay_seconds = 300
13
+ # config.max_retries = 3
14
+ # config.retry_wait_seconds = 60
15
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IprogWorkerHeartbeat
4
+ class Configuration
5
+ attr_accessor :auth_token,
6
+ :callback_url,
7
+ :failure_email_recipients,
8
+ :mailer_from,
9
+ :mail_subject_prefix,
10
+ :notify_on_auth_failure,
11
+ :callback_delay_seconds,
12
+ :max_retries,
13
+ :retry_wait_seconds
14
+
15
+ def initialize
16
+ @auth_token = nil
17
+ @callback_url = "https://www.iprogtech.com/worker-heartbeat"
18
+ @failure_email_recipients = []
19
+ @mailer_from = nil
20
+ @mail_subject_prefix = "[Worker Heartbeat]"
21
+ @notify_on_auth_failure = false
22
+ @callback_delay_seconds = 300
23
+ @max_retries = 3
24
+ @retry_wait_seconds = 60
25
+ end
26
+
27
+ def failure_email_recipients_array
28
+ Array(failure_email_recipients).map(&:to_s).map(&:strip).reject(&:empty?).uniq
29
+ end
30
+
31
+ def validate!
32
+ raise ArgumentError, "IprogWorkerHeartbeat auth_token is required" if auth_token.to_s.strip.empty?
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IprogWorkerHeartbeat
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace IprogWorkerHeartbeat
6
+
7
+ initializer "iprog_worker_heartbeat.validate_config" do
8
+ config.after_initialize do
9
+ next if Rails.env.test?
10
+
11
+ IprogWorkerHeartbeat.config.validate!
12
+ rescue ArgumentError => e
13
+ Rails.logger.error("iprog_worker_heartbeat config error: #{e.message}")
14
+ raise if Rails.env.production?
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IprogWorkerHeartbeat
4
+ VERSION = "0.1.1"
5
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails"
4
+ require_relative "iprog_worker_heartbeat/version"
5
+ require_relative "iprog_worker_heartbeat/configuration"
6
+ require_relative "iprog_worker_heartbeat/engine"
7
+
8
+ module IprogWorkerHeartbeat
9
+ class << self
10
+ attr_writer :config
11
+
12
+ def config
13
+ @config ||= Configuration.new
14
+ end
15
+
16
+ def configure
17
+ yield(config)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require_relative "../app/services/iprog_worker_heartbeat/callback_client"
5
+
6
+ RSpec.describe IprogWorkerHeartbeat::CallbackClient do
7
+ it "raises on non-success response" do
8
+ response = instance_double(Net::HTTPUnauthorized, code: "401")
9
+ allow(response).to receive(:is_a?).with(Net::HTTPSuccess).and_return(false)
10
+ allow(Net::HTTP).to receive(:start).and_return(response)
11
+
12
+ expect do
13
+ described_class.deliver!(url: "https://example.com/worker-heartbeat", bearer_token: "x", worker_token: "y")
14
+ end.to raise_error(RuntimeError, /status=401/)
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe IprogWorkerHeartbeat::Configuration do
6
+ it "requires auth_token in validate!" do
7
+ config = described_class.new
8
+ expect { config.validate! }.to raise_error(ArgumentError)
9
+ end
10
+
11
+ it "normalizes failure recipients" do
12
+ config = described_class.new
13
+ config.failure_email_recipients = [" admin@iprogtech.com ", "", "admin@iprogtech.com"]
14
+ expect(config.failure_email_recipients_array).to eq(["admin@iprogtech.com"])
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "iprog_worker_heartbeat"
4
+
5
+ RSpec.configure do |config|
6
+ config.example_status_persistence_file_path = ".rspec_status"
7
+ config.disable_monkey_patching!
8
+ config.expect_with(:rspec) { |c| c.syntax = :expect }
9
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iprog_worker_heartbeat
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Jayson Presto
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-03-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.13'
41
+ description: Provides /worker-heartbeat endpoint handling, persistence, delayed callback
42
+ job, and failure-only email alerts.
43
+ email:
44
+ - jaysonpresto.iprog21@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - CHANGELOG.md
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - app/controllers/iprog_worker_heartbeat/heartbeats_controller.rb
54
+ - app/jobs/iprog_worker_heartbeat/callback_job.rb
55
+ - app/mailers/iprog_worker_heartbeat/failure_mailer.rb
56
+ - app/models/iprog_worker_heartbeat/heartbeat.rb
57
+ - app/services/iprog_worker_heartbeat/callback_client.rb
58
+ - app/views/iprog_worker_heartbeat/failure_mailer/notify.html.erb
59
+ - app/views/iprog_worker_heartbeat/failure_mailer/notify.text.erb
60
+ - config/routes.rb
61
+ - lib/generators/iprog_worker_heartbeat/install/install_generator.rb
62
+ - lib/generators/iprog_worker_heartbeat/install/templates/create_worker_heartbeats.rb
63
+ - lib/generators/iprog_worker_heartbeat/install/templates/iprog_worker_heartbeat_initializer.rb
64
+ - lib/iprog_worker_heartbeat.rb
65
+ - lib/iprog_worker_heartbeat/configuration.rb
66
+ - lib/iprog_worker_heartbeat/engine.rb
67
+ - lib/iprog_worker_heartbeat/version.rb
68
+ - spec/callback_client_spec.rb
69
+ - spec/configuration_spec.rb
70
+ - spec/spec_helper.rb
71
+ homepage: https://rubygems.org/gems/iprog_worker_heartbeat
72
+ licenses:
73
+ - MIT
74
+ metadata:
75
+ homepage_uri: https://rubygems.org/gems/iprog_worker_heartbeat
76
+ source_code_uri: https://github.com/iprog21/iprog_worker_heartbeat
77
+ changelog_uri: https://github.com/iprog21/iprog_worker_heartbeat/blob/main/CHANGELOG.md
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 3.1.0
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubygems_version: 3.5.21
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Rails worker-heartbeat client workflow for monitor callbacks
97
+ test_files: []