class Cutoff

Constants

CURRENT_STACK_KEY

Attributes

allowed_seconds[R]

@return [Float] The total number of seconds for this cutoff

Public Class Methods

checkpoint!(name = nil) click to toggle source

Raise an exception if there is an active expired cutoff

Does nothing if no active cutoff is set

@raise CutoffExceededError If there is an active expired cutoff @return [void]

# File lib/cutoff.rb, line 91
def checkpoint!(name = nil)
  cutoff = current
  return unless cutoff

  cutoff.checkpoint!(name)
end
clear_all() click to toggle source

Clear the entire stack for this thread

@return [void]

# File lib/cutoff.rb, line 64
def clear_all
  Thread.current[CURRENT_STACK_KEY] = nil
end
current() click to toggle source

Get the current {Cutoff} if one is set

# File lib/cutoff.rb, line 21
def current
  Thread.current[CURRENT_STACK_KEY]&.last
end
disable!() click to toggle source

Disable Cutoff globally. Useful for testing and debugging

Should not be used in production

@return [void]

# File lib/cutoff.rb, line 103
def disable!
  @disabled = true
end
disabled?() click to toggle source

True if cutoff was disabled with {.disable!}

@return [Boolean] True if disabled

# File lib/cutoff.rb, line 119
def disabled?
  @disabled == true
end
enable!() click to toggle source

Enable Cutoff globally if it has been disabled

Should not be used in production

@return [void]

# File lib/cutoff.rb, line 112
def enable!
  @disabled = false
end
new(allowed_seconds, exclude: nil, only: nil) click to toggle source

Create a new cutoff

The timer starts immediately upon creation

@param allowed_seconds [Float, Integer] The total number of seconds to allow @param exclude [Enumberable<Symbol>, Symbol, nil] If given a name or

list of checkpoint names to skip

@param only [Enumberable<Symbol>, Symbol, nil] If given a name or

list of checkpoint names to allow
# File lib/cutoff.rb, line 136
def initialize(allowed_seconds, exclude: nil, only: nil)
  @allowed_seconds = allowed_seconds.to_f
  @start_time = Cutoff.now

  if exclude
    @exclude = Set.new(exclude.is_a?(Enumerable) ? exclude : [exclude])
  end

  if only
    @only = Set.new(only.is_a?(Enumerable) ? only : [only])
  end
end
start(allowed_seconds, **options) click to toggle source

Add a new {Cutoff} to the stack

This {Cutoff} will be specific to this thread

If a cutoff is already started for this thread, then `start` uses the minimum of the current remaining time and the given time

@param (see initialize) @return [Cutoff] The {Cutoff} instance

# File lib/cutoff.rb, line 34
def start(allowed_seconds, **options)
  if current
    allowed_seconds = [allowed_seconds, current.seconds_remaining].min
  end

  cutoff = new(allowed_seconds, **options)
  Thread.current[CURRENT_STACK_KEY] ||= []
  Thread.current[CURRENT_STACK_KEY] << cutoff
  cutoff
end
stop(cutoff = nil) click to toggle source

Remove the top {Cutoff} from the stack

@param cutoff [Cutoff] If given, the top instance will only be removed

if it matches the given cutoff instance

@return [Cutoff, nil] If a cutoff was removed it is returned

# File lib/cutoff.rb, line 50
def stop(cutoff = nil)
  stack = Thread.current[CURRENT_STACK_KEY]
  return unless stack

  top = stack.last
  stack.pop if cutoff.nil? || top == cutoff
  clear_all if stack.empty?

  cutoff
end
version() click to toggle source

@return [Gem::Version] The current version of the cutoff gem

# File lib/cutoff/version.rb, line 5
def self.version
  Gem::Version.new('0.4.2')
end
wrap(allowed_seconds, **options) { |cutoff| ... } click to toggle source

Wrap a block in a cutoff

Same as calling {.start} and {.stop} manually, but safer since you can't forget to stop a cutoff and it handles exceptions raised inside the block

@see .start @see .stop @param (see initialize) @return The value that returned from the block

# File lib/cutoff.rb, line 78
def wrap(allowed_seconds, **options)
  cutoff = start(allowed_seconds, **options)
  yield cutoff
ensure
  stop(cutoff)
end

Public Instance Methods

checkpoint!(name = nil) click to toggle source

Raises an error if this Cutoff has been exceeded

@raise CutoffExceededError If there is an active expired cutoff @return [void]

# File lib/cutoff.rb, line 183
def checkpoint!(name = nil)
  unless name.nil? || name.is_a?(Symbol)
    raise ArgumentError, 'name must be a symbol'
  end

  return unless selected?(name)
  raise CutoffExceededError, self if exceeded?

  nil
end
elapsed_seconds() click to toggle source

The number of seconds elapsed since this {Cutoff} was created

@return [Float] The number of seconds

# File lib/cutoff.rb, line 166
def elapsed_seconds
  return 0 if Cutoff.disabled?

  Cutoff.now - @start_time
end
exceeded?() click to toggle source

Has the Cutoff been exceeded?

@return [Boolean] True if the timer expired

# File lib/cutoff.rb, line 175
def exceeded?
  seconds_remaining.negative?
end
ms_remaining() click to toggle source

The number of milliseconds left on the clock

@return [Float] The number of milliseconds

# File lib/cutoff.rb, line 159
def ms_remaining
  seconds_remaining * 1000
end
seconds_remaining() click to toggle source

The number of seconds left on the clock

@return [Float] The number of seconds

# File lib/cutoff.rb, line 152
def seconds_remaining
  @allowed_seconds - elapsed_seconds
end
selected?(name) click to toggle source

True if the named checkpoint is selected by the `exclude` and `only` options. If these options are not given, a checkpoint is considered to be selected. If the checkpoint is not named, it is also considered to be selected.

@param name [Symbol, nil] The name of the checkpoint @return [Boolean] True if the checkpoint is selected

# File lib/cutoff.rb, line 201
def selected?(name)
  return true if name.nil? && @exclude
  return false if @exclude&.include?(name)
  return false if @only && !@only.include?(name)

  true
end