Skip to content

Instantly share code, notes, and snippets.

@MicahBrown
Created December 24, 2016 20:33
Show Gist options
  • Save MicahBrown/a3bdbf8dc776c6f6a09aeb77296edb55 to your computer and use it in GitHub Desktop.
Save MicahBrown/a3bdbf8dc776c6f6a09aeb77296edb55 to your computer and use it in GitHub Desktop.
Load and Authorize Resource
require 'active_support/concern'
# Derived from cancan gem (https://github.com/ryanb/cancan)
# This authorizes by checking the current resource's associated user.
#
# Options:
# :auth_method - Pass a symbol corresponding to the current resource's user
# association that you'd like to authorize against.
#
# Default => :user
#
# :require_user - If enabled (true), it will throw an error if the authorized
# resource is missing the associated user to authorize against.
#
# Default => true
#
# :only,
# :except,
# :unless,
# :if - Typical controller callback conditional options.
#
# Default => [:edit, :update, :destroy] # using :only
module AuthorizeResource
extend ActiveSupport::Concern
class_methods do
def authorize_resource *args
ControllerResource.add_before_action(self, :authorize_resource, *args)
end
end
class ControllerResource
def self.add_before_action(controller_class, method, *args)
options = args.extract_options!
resource_name = args.first
exceptions = options.slice(:only, :except, :if, :unless)
exceptions[:only] = [:edit, :update, :destroy] unless exceptions.present?
controller_class.send(:before_action, exceptions) do |controller|
ControllerResource.new(controller, resource_name, options.except(:only, :except, :if, :unless)).send(method)
end
end
def initialize(controller, *args)
@controller = controller
@params = controller.params
@options = args.extract_options!
@name = args.first || @params[:controller].singularize.to_sym
end
def authorize_resource
raise_not_found unless authorized?
end
def user
@controller.current_user
end
def resource_instance
@controller.instance_variable_get("@#{@name}")
end
def raise_not_found
raise ActionController::RoutingError.new('Not Found')
end
def authorized?
auth_method = @options.key?(:auth_method) ? @options[:auth_method] : :user
require_user = @options.key?(:require_user) ? @options[:require_user] : true
object_user = resource_instance.send(auth_method)
raise(ArgumentError, "object doesn't have a user") if require_user && !object_user.is_a?(User)
user.is_a?(User) && object_user == user
end
end
end
require 'active_support/concern'
# Derived from cancan gem (https://github.com/ryanb/cancan)
# Loads current resource using controller resource.
#
# Options:
# :parent - If the resource symbol is specified, we'll assume that we
# are loading a parent object for a nested resource. Setting it
# to false tells the module to load the nested resource instead.
#
# :id_param - Specify the id param to find the resource with (e.g. use
# params[:object_id] instead of params[:id]).
#
# :param_method - Specify a method (as a symbol) to use for whitelisting with
# with strong params when building a new record.
#
# Example:
# load_resource param_method: :object_params
#
# private
# def object_params
# params.require(:object).permit(:name)
# end
#
# :find_by - Specify the field (as a symbol) to find the resource with. If
# you need to search for an object by the :token field instead
# of :id (the default), for example, you can do so with this option.
#
# :only,
# :except,
# :unless,
# :if - Typical controller callback conditional options.
#
# Default => [:show, :edit, :update, :new, :create, :destroy] # using :only
module LoadResource
extend ActiveSupport::Concern
class_methods do
def load_resource *args
ControllerResource.add_before_action(self, :load_resource, *args)
end
end
# Handle the load and authorization controller logic so we don't clutter up all controllers with non-interface methods.
# This class is used internally, so you do not need to call methods directly on it.
class ControllerResource # :nodoc:
def self.add_before_action(controller_class, method, *args)
options = args.extract_options!
resource_name = args.first
exceptions = options.slice(:only, :except, :if, :unless)
exceptions[:only] = [:show, :edit, :update, :new, :create, :destroy] unless exceptions.present?
controller_class.send(:before_action, exceptions) do |controller|
ControllerResource.new(controller, resource_name, options.except(:only, :except, :if, :unless)).send(method)
end
end
def initialize(controller, *args)
@controller = controller
@params = controller.params
@options = args.extract_options!
@name = args.first || @params[:controller].singularize.to_sym
end
def load_resource
self.resource_instance ||= load_resource_instance
end
def parent?
@options.has_key?(:parent) ? @options[:parent] : @name && @name != name_from_controller.to_sym
end
def assign_attributes?
@params[:action] == 'create'
end
def name_from_controller
@params[:controller].sub("Controller", "").underscore.split('/').last.singularize
end
def id_param
if @options[:id_param]
@params[@options[:id_param]]
else
@params[parent? ? :"#{@name}_id" : :id]
end.to_s
end
def load_resource_instance
if !parent? && new_actions.include?(@params[:action].to_sym)
build_resource
elsif id_param || @options[:singleton]
find_resource
end
end
def find_resource
key = @options[:find_by] || :id
resource_class.where(key => id_param).first!
end
def build_resource
resource = resource_class.new
resource.assign_attributes(resource_params) if assign_attributes?
resource
end
def resource_params
@options[:param_method] ? @controller.send(@options[:param_method]) : @params[@name]
end
def resource_class
@name.to_s.camelcase.constantize
end
def resource_instance=(instance)
@controller.instance_variable_set("@#{@name}", instance)
end
def resource_instance
@controller.instance_variable_get("@#{@name}")
end
private
def new_actions
[:new, :create]
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment