module EnsureIt

@private

Constants

BIN_REGEXP
ENSURES
FLOAT_REGEXP
HEX_REGEXP
INT_REGEXP
OCT_REGEXP
OPERATIONS
TRUE_NAMES
VERSION

Public Class Methods

config() click to toggle source
# File lib/ensure_it/config.rb, line 24
def self.config
  Config
end
configure() { |Config| ... } click to toggle source
# File lib/ensure_it/config.rb, line 28
def self.configure
  yield(Config) if block_given?
  Config
end
ensure_array(arr, *args, values: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_array.rb, line 46
def self.ensure_array(arr, *args, values: nil, **opts)
  args.each do |arg|
    if arg.is_a?(Proc)
      arr = arr.map(arg)
      next
    end
    arg = arg.ensure_symbol || next
    case arg
    when *ENSURES
      arr =
        case arg
        when :ensure_symbol then arr.map { |x| x.ensure_symbol }
        when :ensure_symbol! then arr.map { |x| x.ensure_symbol! }
        when :ensure_string then arr.map { |x| x.ensure_string }
        when :ensure_string! then arr.map { |x| x.ensure_string! }
        when :ensure_integer then arr.map { |x| x.ensure_integer }
        when :ensure_integer! then arr.map { |x| x.ensure_integer! }
        when :ensure_float then arr.map { |x| x.ensure_float }
        when :ensure_float! then arr.map { |x| x.ensure_float! }
        when :ensure_array then arr.map { |x| x.ensure_array }
        when :ensure_array! then arr.map { |x| x.ensure_array! }
        when :ensure_class then arr.map { |x| x.ensure_class }
        when :ensure_class! then arr.map { |x| x.ensure_class! }
        end
    when *OPERATIONS
      op = arg == :sort_desc ? :sort : arg
      arr = arr.send(op)
      arr = arr.reverse if arg == :sort_desc
    else
      arr = arr.map { |x| x.respond_to?(arg) ? x.send(arg) : nil }
    end
  end
  values.is_a?(Array) ? arr & values : arr
end
ensure_array_error(**opts) click to toggle source
# File lib/ensure_it/ensure_array.rb, line 102
def self.ensure_array_error(**opts)
  opts[:message] ||= '#{subject} should be an Array'
  opts
end
ensure_boolean_error(**opts) click to toggle source
# File lib/ensure_it/ensure_boolean.rb, line 68
def self.ensure_boolean_error(**opts)
  unless opts.key?(:message)
    opts[:message] = '#{subject} should be a boolean or be able' \
                     ' to convert to it'
  end
  opts
end
ensure_class(klass, *args, values: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_class.rb, line 67
def self.ensure_class(klass, *args, values: nil, **opts)
  args.select! { |x| x.is_a?(Module) }
  throw :wrong unless args.all? { |x| klass <= x }
  throw :wrong if values.is_a?(Array) && !values.include?(klass)
  klass
end
ensure_class_string(str, *args, **opts) click to toggle source
# File lib/ensure_it/ensure_class.rb, line 57
def self.ensure_class_string(str, *args, **opts)
  opts.delete(:name_of)
  opts.delete(:exist)
  name = EnsureIt::StringUtils.ensure_name(
    str, name_of: :class, exist: true, **opts
  )
  throw :wrong if name.nil?
  EnsureIt.ensure_class(Object.const_get(name), *args, **opts)
end
ensure_float(float, values: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_float.rb, line 76
def self.ensure_float(float, values: nil, **opts)
  throw :wrong if values.is_a?(Array) && !values.include?(float)
  float
end
ensure_float_error(**opts) click to toggle source
# File lib/ensure_it/ensure_float.rb, line 81
def self.ensure_float_error(**opts)
  unless opts.key?(:message)
    opts[:message] = '#{subject} should be a float or be able' \
                     ' to convert to it'
  end
  opts
end
ensure_integer(int, values: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_integer.rb, line 116
def self.ensure_integer(int, values: nil, **opts)
  throw :wrong if values.is_a?(Array) && !values.include?(int)
  int
end
ensure_integer_error(**opts) click to toggle source
# File lib/ensure_it/ensure_integer.rb, line 133
def self.ensure_integer_error(**opts)
  unless opts.key?(:message)
    opts[:message] = '#{subject} should be an integer or be able' \
                     ' to convert to it'
  end
  opts
end
ensure_integer_string(str, **opts) click to toggle source
# File lib/ensure_it/ensure_integer.rb, line 121
def self.ensure_integer_string(str, **opts)
  value = case str
    when OCT_REGEXP then opts[:octal] == true ? str.to_i(8) : str.to_i
    when INT_REGEXP then str.to_i
    when HEX_REGEXP then str[2..-1].to_i(16)
    when BIN_REGEXP then str[2..-1].to_i(2)
    else throw :wrong
  end
  return value if opts.empty?
  return EnsureIt.ensure_integer(value, **opts)
end
ensure_string(str, values: nil, downcase: nil, name_of: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_string.rb, line 129
def self.ensure_string(str, values: nil, downcase: nil, name_of: nil, **opts)
  if name_of.nil?
    value = downcase == true ? str.downcase : str
  else
    value = EnsureIt::StringUtils.ensure_name(
      str, downcase: downcase, name_of: name_of, **opts
    )
    throw :wrong if value.nil?
  end
  throw :wrong if values.is_a?(Array) && !values.include?(value)
  value
end
ensure_string_error(**opts) click to toggle source
# File lib/ensure_it/ensure_string.rb, line 142
def self.ensure_string_error(**opts)
  unless opts.key?(:message)
    opts[:message] = '#{subject} should be a String or a Symbol'
    if opts[:numbers] == true
      opts[:message] << ' or a Numeric'
    end
    if opts.key?(:name_of)
      opts[:message] << " and should be a name of #{opts[:name_of]}"
    end
    if opts[:values].is_a?(Array)
      opts[:message] << " and should contained in #{opts[:values]}"
    end
  end
  opts
end
ensure_symbol(sym, values: nil, downcase: nil, name_of: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_symbol.rb, line 51
def self.ensure_symbol(sym, values: nil, downcase: nil, name_of: nil, **opts)
  if name_of.nil?
    value = downcase == true ? sym.to_s.downcase.to_sym : sym
  else
    value = EnsureIt::StringUtils.ensure_name(
      sym.to_s, downcase: downcase, name_of: name_of, **opts
    )
    throw :wrong if value.nil?
    value = value.to_sym
  end
  throw :wrong if values.is_a?(Array) && !values.include?(value)
  value
end
ensure_symbol_error(**opts) click to toggle source
# File lib/ensure_it/ensure_symbol.rb, line 65
def self.ensure_symbol_error(**opts)
  unless opts.key?(:message)
    opts[:message] = '#{subject} should be a Symbol or a String'
    if opts.key?(:name_of)
      opts[:message] << " and should be a name of #{opts[:name_of]}"
    end
    if opts[:values].is_a?(Array)
      opts[:message] << " and should contained in #{opts[:values]}"
    end
  end
  opts
end
raise_error(method_name, message: nil, error: nil, **opts) click to toggle source
# File lib/ensure_it/errors.rb, line 62
def self.raise_error(method_name, message: nil, error: nil, **opts)
  error = EnsureIt.config.error_class if error.nil? || !(error <= Exception)
  error_msg = ErrorMessage.new(method_name, message, caller[1..-1])
  # save message in backtrace in variables to not call getter
  # methods of error_msg instance in raise call
  error_message = error_msg.message
  error_backtrace = error_msg.backtrace
  if opts[:smart] == true || EnsureIt.config.errors == :smart
    inspect_source(error_msg, **opts)
    activate_smart_errors(error_msg, **opts)
  end
  raise error, error_message, error_backtrace
end
refined?() click to toggle source
# File lib/ensure_it.rb, line 14
def self.refined?
  ENSURE_IT_REFINED == true
end

Private Class Methods

activate_smart_errors(error, **opts) click to toggle source
# File lib/ensure_it/errors.rb, line 76
def self.activate_smart_errors(error, **opts)
  tp_count = 0
  error_obj = nil
  #
  # first trace point is to capture raise object before exitting
  # from :ensure_* method
  #
  # after that with second trace point we try to return from :ensure_* method
  # to caller and inspect code there for method name and arguments
  TracePoint.trace(:return, :raise) do |first_tp|
    if first_tp.event == :raise
      # save error object for patching
      error_obj = first_tp.raised_exception
    else
      # skip returns from :raise_smart_error and :raise_error
      tp_count += 1
      if tp_count > 2
        first_tp.disable
        # at this moment we are at the end of 'ensure_' method
        # skip last code line in :ensure_* method
        TracePoint.trace(:return, :line) do |second_tp|
          # now we are in caller context
          second_tp.disable
          unless error_obj.nil?
            # inspect caller code
            inspect_code(second_tp, error, **opts)
            # patch error message
            msg = error.message
            error_obj.define_singleton_method(:message) { msg }
          end
        end
      end
    end
  end
end
inspect_code(tp, error, **opts) click to toggle source
# File lib/ensure_it/errors.rb, line 140
def self.inspect_code(tp, error, **opts)
  return if tp.method_id.nil?
  error.inside = tp.method_id
  begin
    method = eval("method(:#{tp.method_id})", tp.binding)
  rescue NameError
    return
  end
  param = method.parameters.find { |_, name| name.to_s == error.subject }
  unless param.nil?
    error.subject_type = "#{param[0]}_argument".to_sym
  end
end
inspect_source(error, **opts) click to toggle source
# File lib/ensure_it/errors.rb, line 112
def self.inspect_source(error, **opts)
  file_name, line_no = error.backtrace.first.split(':', 2)
  return unless File.exist?(file_name)
  line_no = line_no.to_i
  line = read_line_number(file_name, line_no)
  return if line.nil?
  m_name = error.method_name
  m = /
    (?:(?<method_access>\.)|(?<class_access>@{1,2}))?
    (?<name>(?:[a-z_][a-zA-Z_0-9]*(?<modifier>[?!])?)|\))
    (?:
      (?<send>\.send\(\s*(?::#{m_name}|'#{m_name}'|"#{m_name}")\s*\))|
      (?:\.#{m_name}(?:[^a-zA-Z_0-9]|\z))
    )
  /x.match(line)
  return if m.nil? || m[:method_access].nil? && !m[:modifier].nil?
  error.subject = m[:name]
  error.subject_type = case
    when m[:class_access] then
      m[:class_access] == '@' ? :instance_variable : :class_variable
    when m[:name] == ')' then
      error.subject = nil
      :unknown_method_result
    when m[:method_access] then :method_result
    else :local_variable
    end
end
patch(target, &block) click to toggle source
# File lib/ensure_it/patch.rb, line 3
def self.patch(target, &block)
  module_eval do
    refine target do
      class_eval(&block)
    end
  end
end
read_line_number(file_name, number) click to toggle source
# File lib/ensure_it/errors.rb, line 154
def self.read_line_number(file_name, number)
  counter, line = 0, nil
  File.foreach(file_name) do |l|
    counter += 1
    if counter == number
      line = l.chomp!
      break
    end
  end
  line
end

Public Instance Methods

ensure_array(*args, default: [], make: false, **opts) click to toggle source
# File lib/ensure_it/ensure_array.rb, line 4
def ensure_array(*args, default: [], make: false, **opts)
  return default if make != true
  EnsureIt.ensure_array([self], *args, **opts)
end
ensure_array!(*args, make: false, **opts) click to toggle source
# File lib/ensure_it/ensure_array.rb, line 9
def ensure_array!(*args, make: false, **opts)
  return EnsureIt.ensure_array([self], *args, **opts) if make == true
  EnsureIt.raise_error(:ensure_array!,
                       **EnsureIt.ensure_array_error(**opts))
end
ensure_boolean(default: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_boolean.rb, line 5
def ensure_boolean(default: nil, **opts)
  default
end
ensure_boolean!(**opts) click to toggle source
# File lib/ensure_it/ensure_boolean.rb, line 9
def ensure_boolean!(**opts)
  EnsureIt.raise_error(:ensure_boolean!,
                       **EnsureIt::ensure_boolean_error(**opts))
end
ensure_class(*args, default: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_class.rb, line 3
def ensure_class(*args, default: nil, **opts)
  default
end
ensure_class!(*args, **opts) click to toggle source
# File lib/ensure_it/ensure_class.rb, line 7
def ensure_class!(*args, **opts)
  opts[:message] ||= '#{subject} should be a class'
  EnsureIt.raise_error(:ensure_class!, **opts)
end
ensure_float(default: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_float.rb, line 5
def ensure_float(default: nil, **opts)
  default
end
ensure_float!(**opts) click to toggle source
# File lib/ensure_it/ensure_float.rb, line 9
def ensure_float!(**opts)
  EnsureIt.raise_error(:ensure_float!,
                       **EnsureIt::ensure_float_error(**opts))
end
ensure_hash(*args, default: {}, **opts) click to toggle source
# File lib/ensure_it/ensure_hash.rb, line 3
def ensure_hash(*args, default: {}, **opts)
  default
end
ensure_hash!(*args, **opts) click to toggle source
# File lib/ensure_it/ensure_hash.rb, line 7
def ensure_hash!(*args, **opts)
  opts[:message] ||= '#{subject} should be a Hash'
  EnsureIt.raise_error(:ensure_hash!, **opts)
end
ensure_instance_of(klass, default: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_instance_of.rb, line 3
def ensure_instance_of(klass, default: nil, **opts)
  unless klass.is_a?(Class)
    fail(
      ArgumentError,
      'Wrong class argument for #ensure_instance_of specified'
    )
  end
  is_a?(klass) ? self : default
end
ensure_instance_of!(klass, **opts) click to toggle source
# File lib/ensure_it/ensure_instance_of.rb, line 13
def ensure_instance_of!(klass, **opts)
  unless klass.is_a?(Class)
    fail(
      ArgumentError,
      'Wrong class argument for #ensure_instance_of specified'
    )
  end
  return self if is_a?(klass)
  opts[:message] ||=
    "\#{subject} should be an instance of '#{klass.name}' class"
  EnsureIt.raise_error(:ensure_instance_of!, **opts)
end
ensure_integer(default: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_integer.rb, line 3
def ensure_integer(default: nil, **opts)
  default
end
ensure_integer!(**opts) click to toggle source
# File lib/ensure_it/ensure_integer.rb, line 7
def ensure_integer!(**opts)
  EnsureIt.raise_error(:ensure_integer!,
                       **EnsureIt::ensure_integer_error(**opts))
end
ensure_string(default: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_string.rb, line 57
def ensure_string(default: nil, **opts)
  default
end
ensure_string!(**opts) click to toggle source
# File lib/ensure_it/ensure_string.rb, line 61
def ensure_string!(**opts)
  EnsureIt.raise_error(:ensure_string!,
                       **EnsureIt.ensure_string_error(**opts))
end
ensure_symbol(default: nil, **opts) click to toggle source
# File lib/ensure_it/ensure_symbol.rb, line 3
def ensure_symbol(default: nil, **opts)
  default
end
ensure_symbol!(**opts) click to toggle source
# File lib/ensure_it/ensure_symbol.rb, line 7
def ensure_symbol!(**opts)
  EnsureIt.raise_error(:ensure_symbol!,
                       **EnsureIt.ensure_symbol_error(**opts))
end