module ApiHammer::Rails

the contents of this file are to let you halt a controller in its processing without having to have a return in the actual action. this lets helper methods which do things like parameter validation halt.

it is designed to function similarly to Sinatra's handling of throw(:halt), but is based around exceptions because rails doesn't catch anything, just rescues.

Public Class Methods

included(klass) click to toggle source
# File lib/api_hammer/rails.rb, line 7
def self.included(klass)
  (@on_included || []).each do |included_proc|
    included_proc.call(klass)
  end
end

Public Instance Methods

check_required_params(*checks) click to toggle source

halts with a 422 Unprocessable Entity and an appropriate error body if required params are missing

simple:

check_required_params(:id, :name)

less simple:

check_required_params(:id, :person => [:name, :height], :lucky_numbers => Array)
  • `params` must be present

  • `params` must be present and be a hash

  • `params[:name]` must be present

  • `params[:height]` must be present

  • `params` must be present and be an array

# File lib/api_hammer/rails/check_required_params.rb, line 20
def check_required_params(*checks)
  errors = Hash.new { |h,k| h[k] = [] }
  check_required_params_helper(checks, params, errors, [])
  halt_unprocessable_entity(errors) if errors.any?
end
halt(status, body, render_options = {}) click to toggle source

halt and render the given body

# File lib/api_hammer/rails/halt.rb, line 42
def halt(status, body, render_options = {})
  raise(ApiHammer::Rails::Halt.new(body.inspect, body, render_options.merge(:status => status)))
end
handle_halt(halt) click to toggle source

handle a raised ApiHammer::Halt or subclass and render it

# File lib/api_hammer/rails/halt.rb, line 30
def handle_halt(halt)
  render_options = halt.render_options ? halt.render_options.dup : {}
  # rocket pants does not have a render method, just render_json
  if respond_to?(:render_json, true)
    render_json(halt.body || {}, render_options)
  else
    render_options[:json] = halt.body || {}
    render(render_options)
  end
end
unmunged_request_params() click to toggle source

request parameters (not query parameters) without the nil/empty array munging that rails does

# File lib/api_hammer/rails/unmunged_request_params.rb, line 3
def unmunged_request_params
  # Thread.exclusive is not optimal but we need to ensure that any other params parsing occurring in other
  # threads is not affected by disabling munging
  #
  # TODO when we are on a rails which has ActionDispatch::Request::Utils.perform_deep_munge, use that instead
  # of clobbering methods
  @unmunged_params ||= Thread.exclusive do
    if ActionDispatch::Request.const_defined?(:Utils) && ActionDispatch::Request::Utils.respond_to?(:deep_munge)
      # rails 4
      deep_munge_owner = (class << ActionDispatch::Request::Utils; self; end)
    else
      # rails 3
      deep_munge_owner = ActionDispatch::Request
    end

    unless deep_munge_owner.method_defined?(:real_deep_munge)
      deep_munge_owner.send(:alias_method, :real_deep_munge, :deep_munge)
    end
    deep_munge_owner.send(:define_method, :deep_munge) { |hash| hash }

    begin
      unmunged_params = nil
      newenv = request.env.merge('action_dispatch.request.request_parameters' => nil)
      ActionDispatch::ParamsParser.new(proc do |env|
        unmunged_params = env['action_dispatch.request.request_parameters']
      end).call(newenv)
      unmunged_params || ActionDispatch::Request.new(newenv).request_parameters
    ensure
      deep_munge_owner.send(:alias_method, :deep_munge, :real_deep_munge)
    end
  end
end

Private Instance Methods

check_required_params_helper(check, subparams, errors, parents) click to toggle source

helper

# File lib/api_hammer/rails/check_required_params.rb, line 28
def check_required_params_helper(check, subparams, errors, parents)
  key = parents.join('#')
  add_error = proc { |message| errors[key] << message unless errors[key].include?(message) }
  if subparams.nil?
    add_error.call(I18n.t(:"errors.required_params.not_provided", :default => "%{key} is required but was not provided", :key => key))
  elsif check
    case check
    when Array
      check.each { |subcheck| check_required_params_helper(subcheck, subparams, errors, parents) }
    when Hash
      if subparams.is_a?(Hash) || (Object.const_defined?('ActionController') && subparams.is_a?(::ActionController::Parameters))
        check.each do |key_, subcheck|
          check_required_params_helper(subcheck, subparams[key_], errors, parents + [key_])
        end
      else
        add_error.call(I18n.t(:"errors.required_params.must_be_hash", :default => "%{key} must be a Hash", :key => key))
      end
    when Class
      unless subparams.is_a?(check)
        add_error.call(I18n.t(:"errors.required_params.must_be_type", :default => "%{key} must be a %{type}", :key => key, :type => check.name))
      end
    else
      if subparams.is_a?(Hash) || (Object.const_defined?('ActionController') && subparams.is_a?(::ActionController::Parameters))
        check_required_params_helper(nil, subparams[check], errors, parents + [check])
      else
        add_error.call(I18n.t(:"errors.required_params.must_be_hash", :default => "%{key} must be a Hash", :key => key))
      end
    end
  end
end