cybernetlab/ensure_it

View on GitHub
lib/ensure_it/ensure_string.rb

Summary

Maintainability
A
3 hrs
Test Coverage
# @!method ensure_string(opts = {})
#
# Ensures that subject is a string. Without options for symbols returns
# converted to string value, for strings, returns self, for others - value,
# specified in default option or nil. With `numbers: true` option, also
# converts numbers to string.
#
# With `downcase: true` option downcases value.
#
# If `values` option specified, returns default value if converted value
# is not included in specified array. **Warning:** there are no type checks
# of array elements, so if you, for example, specify array of integers here,
# this method will allways return default value.
#
# `name_of` option, if specified, should be one of: `:local`,
# `:instance_variable`, `:class_variable`, `:setter`, `:getter`, `:checker`,
# `:bang`, `:method`, `:class` or its string equivalents. This option allow you
# to be ensured that return value is a valid name of local variable,
# instance variable, class variable, setter method, getter method, checker
# method (ending with `?`), bang method (ending with `!`), any method or
# class, respectively to option values above. See examples below for details.
# If you want to convert underscored downcased notations of classes, use
# `downcase: true` option. Also, with `name_of: :class`, `exist: true` option
# can specified to ensures that class exists.
#
# @example  usage of `name_of` option
# ```ruby
#   'some text'.ensure_string(name_of: :local) # => nil
#   'some_text'.ensure_string(name_of: :local) # => 'some_text'
#
#   'some_text'.ensure_string(name_of: :instance_variable) # => '@some_text'
#   '@some_text'.ensure_string(name_of: :instance_variable) # => '@some_text'
#   'some_text='.ensure_string(name_of: :instance_variable) # => '@some_text'
#
#   'some_text'.ensure_string(name_of: :setter) # => 'some_text='
#   'some_text?'.ensure_string(name_of: :setter) # => 'some_text='
#   'some_text='.ensure_string(name_of: :setter) # => 'some_text='
#
#   'some_text'.ensure_string(name_of: :class) # => nil
#   'Some::Text'.ensure_string(name_of: :class) # => Some::Text
#   'some_text'.ensure_string(name_of: :class, downcase: true) # => SomeText
#   'some/text'.ensure_string(name_of: :class, downcase: true) # => Some::Text
#   'Some::Text'.ensure_string(name_of: :class, exist: true) # => nil
#   'Object'.ensure_string(name_of: :class, exist: true) # => 'Object'
# ```
#
# @param [Hash] opts the options
# @option opts [Object] :default (nil) default value for wrong subject
# @option opts [Array] :values an array of possible values
# @option opts [Boolean] :downcase convert string to downcase
# @option opts [<String, Symbol>] :name_of string should be a name of variable
#   or method

# @private
module EnsureIt
  patch Object do
    def ensure_string(default: nil, **opts)
      default
    end

    def ensure_string!(**opts)
      EnsureIt.raise_error(:ensure_string!,
                           **EnsureIt.ensure_string_error(**opts))
    end
  end

  patch String do
    def ensure_string(default: nil, **opts)
      return self if opts.empty?
      catch :wrong do
        return EnsureIt.ensure_string(self, **opts)
      end
      default
    end

    def ensure_string!(**opts)
      return self if opts.empty?
      catch :wrong do
        return EnsureIt.ensure_string(self, **opts)
      end
      EnsureIt.raise_error(:ensure_string!,
                           **EnsureIt.ensure_string_error(**opts))
    end
  end

  patch Symbol do
    def ensure_string(default: nil, **opts)
      return to_s if opts.empty?
      catch :wrong do
        return EnsureIt.ensure_string(to_s, **opts)
      end
      default
    end

    def ensure_string!(**opts)
      return to_s if opts.empty?
      catch :wrong do
        return EnsureIt.ensure_string(to_s, **opts)
      end
      EnsureIt.raise_error(:ensure_string!,
                           **EnsureIt.ensure_string_error(**opts))
    end
  end

  patch Numeric do
    using EnsureIt if ENSURE_IT_REFINED

    def ensure_string(default: nil, numbers: false, **opts)
      return default if numbers != true
      catch :wrong do
        return EnsureIt.ensure_string(to_s, **opts)
      end
      default
    end

    def ensure_string!(numbers: false, **opts)
      if numbers == true
        catch :wrong do
          return EnsureIt.ensure_string(to_s, **opts)
        end
      end
      EnsureIt.raise_error(
        :ensure_string!,
        **EnsureIt.ensure_string_error(numbers: numbers, **opts)
      )
    end
  end

  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

  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
end