class Sinatra::ParamChecker::ParamScope

Constants

OPTIONS

Attributes

params[R]

Public Class Methods

new() click to toggle source
# File lib/sinatra/param_checker.rb, line 12
def initialize
  @params = []
end

Public Instance Methods

optional(name, options = {}) click to toggle source
# File lib/sinatra/param_checker.rb, line 22
def optional(name, options = {})
  param = [:optional, name.to_s, options]
  validate_opts! param
  @params << param
end
required(name, options = {}) click to toggle source
# File lib/sinatra/param_checker.rb, line 16
def required(name, options = {})
  param = [:required, name.to_s, options]
  validate_opts! param
  @params << param
end
validate!(params) click to toggle source
# File lib/sinatra/param_checker.rb, line 28
def validate!(params)
  validated_params = @params.reduce({}) do |memo, e|
    type, name, opts = e
    begin
      # handle default
      v = if params[name].nil?
        case type
        when :optional
          (opts[:default].call if opts[:default].respond_to?(:call)) || opts[:default]
        when :required
          raise InvalidParameterError, "param #{name} not found"
        end
      else
        params[name]
      end

      unless v.nil?
        coerced_value = coerce(v, opts[:type], opts)
        validate(coerced_value, opts)
        memo[name] = coerced_value
      end
      memo
    rescue InvalidParameterError => e
      e.param, e.options = name, opts
      raise e
    end
  end

  validated_params.reduce(params) do |memo, pair|
    k, v = pair
    memo[k] = v
    memo
  end
end

Private Instance Methods

check_type(type, value) click to toggle source
# File lib/sinatra/param_checker.rb, line 149
def check_type(type, value)
  return value.is_a?(String) if type == UUID
  return [true, false].include?(value) if [TrueClass, FalseClass, Boolean].include?(type)
  return true if (value.is_a?(type) rescue false)
  false
end
coerce(value, type, options = {}) click to toggle source
# File lib/sinatra/param_checker.rb, line 65
def coerce(value, type, options = {})
  return value if value.is_a?(type)
  case type.name.split('::').last.to_sym
  when :Integer
    Integer(value)
  when :Float
    Float(value)
  when :String
    String(value)
  when :UUID
    String(value)
  when :File
    if value.is_a?(Hash) && value[:tempfile].is_a?(::Tempfile)
      value
    else
      raise "cannot coerce value to #{type}"
    end
  when :Date
    Date.parse(value)
  when :Time
    Time.parse(value)
  when :DateTime
    DateTime.parse(value)
  when :Array
    delimiter = options[:delimiter] || ','
    Array(value.split(delimiter))
  when :Hash
    delimiter = options[:delimiter] || ','
    separator = options[:separator] || ':'
    value.split(delimiter).map do |c|
      c.split(separator, 2)
    end.to_h
  when :TrueClass, :FalseClass, :Boolean
    if /(false|f|no|n|0)$/i === value.to_s
      false
    elsif /(true|t|yes|y|1)$/i === value.to_s
      true
    else
      raise "cannot coerce value to Boolean"
    end
  else
    raise "cannot coerce value to #{type}"
  end
rescue => _e
  raise InvalidParameterError, "#{type} expected"
end
syntax_err(msg) click to toggle source
# File lib/sinatra/param_checker.rb, line 145
def syntax_err(msg)
  fail SyntaxError, "ParamChecker: #{msg}"
end
validate(v, opts) click to toggle source
# File lib/sinatra/param_checker.rb, line 113
def validate(v, opts)
  case opts[:type].name.split('::').last.to_sym
  when :String
    if opts[:regexp] && !(v =~ opts[:regexp])
      raise InvalidParameterError, 'wrong format'
    end
  when :UUID
    unless v =~ /\A[a-f0-9]{32}\z/
      raise InvalidParameterError, 'uuid expected'
    end
  when :Integer, :Float
    if opts[:type] == Integer && opts[:range] && !opts[:range].include?(v)
      raise InvalidParameterError, "not in range(#{opts[:range]})"
    end
    if opts[:max] && v > opts[:max]
      raise InvalidParameterError, "greater than #{opts[:max]}"
    end
    if opts[:min] && v < opts[:min]
      raise InvalidParameterError, "smaller than #{opts[:min]}"
    end
  when :File
    tf = v[:tempfile] rescue nil
    raise InvalidParameterError, 'File expected' unless Tempfile === tf
  end

  unless [Hash, Array, File].include?(opts[:type])
    if opts[:values] && !opts[:values].include?(v)
      raise InvalidParameterError, 'invalid enumeration member'
    end
  end
end
validate_opts!(param) click to toggle source
# File lib/sinatra/param_checker.rb, line 157
def validate_opts!(param)
  type, name, opts = param

  syntax_err "#{name}: missing option :type" unless opts[:type]
  unless opts[:type].is_a?(Class)
    syntax_err "#{name}: :type value should be class"
  end

  prefix = "#{name}(#{opts[:type]})"
  opts.each do |k, v|
    syntax_err "#{prefix}: unsupported option :#{k}" unless OPTIONS.include?(k)
    case k
    when :default
      if type == :required
        syntax_err "#{prefix}: :default can be used only with :optional params"
      end
      if [File, Array, Hash].include?(opts[:type])
        syntax_err "#{prefix}: :default cannot be used with :type File, Array or Hash"
      end

      unless check_type(opts[:type], v)
        syntax_err "#{prefix}: :default must be #{opts[:type]}"
      end
    when :values
      if [File, Array, Hash].include?(opts[:type])
        syntax_err "#{prefix}: :values cannot be used with :type File, Array or Hash"
      end
      unless Array === v && v.size > 0
        syntax_err "#{prefix}: :values must be Array(size > 0)"
      end
      v.each do |val|
         unless check_type(opts[:type], val)
           syntax_err "#{prefix}: values in :values must be #{opts[:type]}"
         end
      end
    when :min, :max
       unless [Integer, Float].include?(opts[:type])
         syntax_err "#{prefix}: :#{k} can be used only with :type Integer and Float"
       end
       unless check_type(opts[:type], v)
         syntax_err "#{prefix}: :#{k} must be #{opts[:type]}"
       end
    when :range
      unless opts[:type] == Integer
        syntax_err "#{prefix}: :range can be used only with :type Integer"
      end
      unless Range === v && Integer === v.begin
        syntax_err "#{prefix}: :range must be Range of Integer"
      end
    when :regexp
      unless opts[:type] == String
        syntax_err "#{prefix}: :regexp can be used only with :type String"
      end
      unless Regexp === v
        syntax_err "#{prefix}: :regexp must be Regexp"
      end
    when :delimiter
      unless [Array, Hash].include?(opts[:type])
        syntax_err "#{prefix}: :delimiter can be used only with :type Array and Hash"
      end
    when :separator
      unless opts[:type] == Hash
        syntax_err "#{prefix}: :separator can be used only with :type Hash"
      end
    end
  end
end