Class: Smplkit::Jobs::JobEnvironment

Inherits:
Struct
  • Object
show all
Defined in:
lib/smplkit/jobs/models.rb

Overview

One environment’s *sparse override* for a job (ADR-056).

A job’s Smplkit::Jobs::Job#environments map holds one of these per environment. Only the leaves you set are sent on save; everything you leave unset is inherited from the job’s base definition, and the server resolves base ⊕ overrides when the job fires. The base definition is disabled everywhere, so a job runs in an environment only when that environment’s override sets enabled: true.

Reach one through Smplkit::Jobs::Job#environment, e.g. job.environment(“production”).url = “prod.example.com/warm.

*Reading a leaf returns this environment’s override, or nil when it does not override that leaf* — the SDK does not merge in the base value (jobs resolve server-side). To see a base value, read the job’s base definition (Smplkit::Jobs::Job#configuration, Smplkit::Jobs::Job#schedule, …).

rubocop:disable Lint/StructNewOverride – “:method“ matches the API attribute and shadowing Struct#method is the expected ergonomics.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil, url: nil, method: nil, timeout: nil, body: nil, success_status: nil, tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil) ⇒ JobEnvironment

Returns a new instance of JobEnvironment.



363
364
365
366
367
368
369
370
371
372
373
# File 'lib/smplkit/jobs/models.rb', line 363

def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
               url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
               tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
  super(
    enabled: enabled, schedule: schedule, timezone: timezone,
    retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
    url: url, method: method, timeout: timeout, body: body,
    success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
    headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
  )
end

Instance Attribute Details

#bodyString?

Returns Per-environment body override. nil inherits the base.

Returns:

  • (String, nil)

    Per-environment body override. nil inherits the base.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

#ca_certString?

Returns Per-environment CA-cert override. nil inherits the base.

Returns:

  • (String, nil)

    Per-environment CA-cert override. nil inherits the base.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

#enabledBoolean

Returns Whether the job runs in this environment. Defaults to false.

Returns:

  • (Boolean)

    Whether the job runs in this environment. Defaults to false.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

#headersHash{String => String}

Returns Per-environment header overrides, as a name→value object. Each entry overrides (or adds) that one header by name on top of the base headers, leaving the rest inherited. Use #set_header / #get_header.

Returns:

  • (Hash{String => String})

    Per-environment header overrides, as a name→value object. Each entry overrides (or adds) that one header by name on top of the base headers, leaving the rest inherited. Use #set_header / #get_header.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

#methodString?

Returns Per-environment HTTP-method override. nil inherits the base.

Returns:

  • (String, nil)

    Per-environment HTTP-method override. nil inherits the base.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

#next_run_atString?

Returns Read-only next scheduled fire time in this environment, or nil when not enabled / once a one-off has fired. Never sent on save.

Returns:

  • (String, nil)

    Read-only next scheduled fire time in this environment, or nil when not enabled / once a one-off has fired. Never sent on save.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

#retry_policyString?

Returns Per-environment retry-policy override — a policy id, a RetryPolicy (coerced to its id), or “Default”. nil inherits the base Smplkit::Jobs::Job#retry_policy.

Returns:



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

#scheduleString?

Returns Per-environment cron override (recurring jobs only). nil inherits the base Smplkit::Jobs::Job#schedule.

Returns:



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

#success_statusString?

Returns Per-environment success-status override. nil inherits the base.

Returns:

  • (String, nil)

    Per-environment success-status override. nil inherits the base.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

#timeoutInteger?

Returns Per-environment timeout override. nil inherits the base.

Returns:

  • (Integer, nil)

    Per-environment timeout override. nil inherits the base.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

#timezoneString?

Returns Per-environment IANA timezone override (recurring jobs only). nil inherits the base Smplkit::Jobs::Job#timezone, else UTC.

Returns:

  • (String, nil)

    Per-environment IANA timezone override (recurring jobs only). nil inherits the base Smplkit::Jobs::Job#timezone, else UTC.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

#tls_verifyBoolean?

Returns Per-environment TLS-verify override. nil inherits the base.

Returns:

  • (Boolean, nil)

    Per-environment TLS-verify override. nil inherits the base.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

#urlString?

Returns Per-environment URL override. nil inherits the base.

Returns:

  • (String, nil)

    Per-environment URL override. nil inherits the base.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/smplkit/jobs/models.rb', line 358

JobEnvironment = Struct.new(
  :enabled, :schedule, :timezone, :retry_policy, :url, :method, :timeout,
  :body, :success_status, :tls_verify, :ca_cert, :headers, :next_run_at,
  keyword_init: true
) do
  def initialize(enabled: false, schedule: nil, timezone: nil, retry_policy: nil,
                 url: nil, method: nil, timeout: nil, body: nil, success_status: nil,
                 tls_verify: nil, ca_cert: nil, headers: nil, next_run_at: nil)
    super(
      enabled: enabled, schedule: schedule, timezone: timezone,
      retry_policy: (retry_policy.is_a?(RetryPolicy) ? retry_policy.id : retry_policy),
      url: url, method: method, timeout: timeout, body: body,
      success_status: success_status, tls_verify: tls_verify, ca_cert: ca_cert,
      headers: (headers || {}).transform_keys(&:to_s), next_run_at: next_run_at
    )
  end

  # Coerce a retry-policy reference to its id on assignment, so both
  # +env.retry_policy = policy+ and +env.retry_policy = "retry-on-5xx"+ work.
  def retry_policy=(value)
    self[:retry_policy] = value.is_a?(RetryPolicy) ? value.id : value
  end

  # Override (or add) a single header by name in this environment.
  #
  # @param name [String] Header name.
  # @param value [String] Header value.
  def set_header(name, value)
    self.headers ||= {}
    headers[name.to_s] = value
  end

  # This environment's override for header +name+, or +nil+ when it does not
  # override that header.
  #
  # @param name [String] Header name.
  # @return [String, nil]
  def get_header(name)
    (headers || {})[name.to_s]
  end

  # @api private — Emit the flat sparse leaf-path overlay (ADR-056): +enabled+
  #   plus only the leaves this environment overrides, with each header as a
  #   +headers.<name>+ leaf. +next_run_at+ is read-only and never emitted.
  #
  # @return [Hash{String => Object}]
  def to_payload
    payload = { "enabled" => enabled }
    JOB_ENV_SCALAR_LEAVES.each do |leaf|
      value = self[leaf]
      payload[leaf.to_s] = value unless value.nil?
    end
    (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
    payload
  end

  # @api private — Parse the flat leaf-path overlay the server returns
  #   (ADR-056). Header leaves arrive as +headers.<name>+ (split on the first
  #   dot, so a dotted header name like +X-Foo.Bar+ is preserved); every other
  #   leaf is a single top-level key. Unknown leaves are ignored for forward
  #   compatibility. Keys may be symbols or strings.
  #
  # @param raw [Hash, nil] The flat overlay hash, or +nil+ for an empty
  #   override.
  # @return [JobEnvironment]
  def self.from_flat(raw)
    return new if raw.nil?

    headers = {}
    scalars = {}
    next_run_at = nil
    raw.each do |key, value|
      key = key.to_s
      if key == "next_run_at"
        next_run_at = value
        next
      end
      group, _dot, name = key.partition(".")
      if group == "headers" && !name.empty?
        headers[name] = value
      elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
        scalars[key] = value
      end
    end
    new(
      enabled: scalars["enabled"] ? true : false,
      schedule: scalars["schedule"], timezone: scalars["timezone"],
      retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
      timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
      tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
      headers: headers, next_run_at: next_run_at
    )
  end
end

Class Method Details

.from_flat(raw) ⇒ JobEnvironment

Parameters:

  • raw (Hash, nil)

    The flat overlay hash, or nil for an empty override.

Returns:



423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/smplkit/jobs/models.rb', line 423

def self.from_flat(raw)
  return new if raw.nil?

  headers = {}
  scalars = {}
  next_run_at = nil
  raw.each do |key, value|
    key = key.to_s
    if key == "next_run_at"
      next_run_at = value
      next
    end
    group, _dot, name = key.partition(".")
    if group == "headers" && !name.empty?
      headers[name] = value
    elsif JOB_ENV_SCALAR_LEAF_NAMES.include?(key) || key == "enabled"
      scalars[key] = value
    end
  end
  new(
    enabled: scalars["enabled"] ? true : false,
    schedule: scalars["schedule"], timezone: scalars["timezone"],
    retry_policy: scalars["retry_policy"], url: scalars["url"], method: scalars["method"],
    timeout: scalars["timeout"], body: scalars["body"], success_status: scalars["success_status"],
    tls_verify: scalars["tls_verify"], ca_cert: scalars["ca_cert"],
    headers: headers, next_run_at: next_run_at
  )
end

Instance Method Details

#get_header(name) ⇒ String?

This environment’s override for header name, or nil when it does not override that header.

Parameters:

  • name (String)

    Header name.

Returns:

  • (String, nil)


395
396
397
# File 'lib/smplkit/jobs/models.rb', line 395

def get_header(name)
  (headers || {})[name.to_s]
end

#set_header(name, value) ⇒ Object

Override (or add) a single header by name in this environment.

Parameters:

  • name (String)

    Header name.

  • value (String)

    Header value.



385
386
387
388
# File 'lib/smplkit/jobs/models.rb', line 385

def set_header(name, value)
  self.headers ||= {}
  headers[name.to_s] = value
end

#to_payloadHash{String => Object}

Returns:

  • (Hash{String => Object})


404
405
406
407
408
409
410
411
412
# File 'lib/smplkit/jobs/models.rb', line 404

def to_payload
  payload = { "enabled" => enabled }
  JOB_ENV_SCALAR_LEAVES.each do |leaf|
    value = self[leaf]
    payload[leaf.to_s] = value unless value.nil?
  end
  (headers || {}).each { |name, value| payload["headers.#{name}"] = value }
  payload
end