skinny-controllers

Gem Version Build Status Code Climate Test Coverage Dependency Status

An implementation of role-based policies and operations to help controllers lose weight.

The goal of this project is to help API apps be more slim, and separate logic as much as possible.

This gem is inspired by trailblazer, following similar patterns, yet allowing the structure of the rails app to not be entirely overhauled.

Installation

gem 'skinny_controllers'

or

gem install skinny_controllers

Usage

In a controller:

include SkinnyControllers::Diet
# ...
# in your action
render json: model

and that's it!

The above does a multitude of assumptions to make sure that you can type the least amount code possible.

  1. Your controller name is based off your model name (configurable per controller)
  2. Any defined policies or operations follow the formats (though they don't have to exist):
    • #{Model.name}Policy
    • #{Model.name}Operations
  3. Your model responds to find, and where
  4. Your model responds to is_accessible_to?. This can be changed at SkinnyControllers.accessible_to_method

Your model name might be different from your resource name

Lets say you have a JSON API resource that you'd like to render that has some additional/subset of data. Maybe the model is an Event, and the resource an EventSummary (which could do some aggregation of Event data).

The naming of all the objects should be as follows:

  • EventSummariesController
  • EventSummaryOperations::*
  • EventSummaryPolicy
  • and the model is still Event

In EventSummariesController, you would make the following additions:

class EventSummariesController < ApiController # or whatever your superclass is
  include SkinnyControllers::Diet
  self.model_class = Event

  def index
    render json: model, each_serializer: EventSummariesSerializer
  end

  def show
    render json: model, serializer: EventSummariesSerializer
  end
end

Note that each_serializer and serializer is not part of SkinnyControllers, and is part of ActiveModel::Serializers.

Defining Operations

Operations should be placed in app/operations of your rails app.

For operations concerning an Event, they should be under app/operations/event_operations/.

Using the example from the specs:

module EventOperations
  class Read < SkinnyControllers::Operation::Base
    def run
      model if allowed?
    end
  end
end

alternatively, all operation verbs can be stored in the same file under (for example) app/operations/user_operations.rb

module UserOperations
  class Read < SkinnyControllers::Operation::Base
    def run
      model if allowed?
    end
  end

  class ReadAll < SkinnyControllers::Operation::Base
    def run
      model if allowed?
    end
  end
end

Defining Policies

Policies should be placed in app/policies of your rails app. These are where you define your access logic, and how to decide if a user has access to the object

class EventPolicy < SkinnyControllers::Policy::Base
  def read?(o = object)
    o.is_accessible_to?(user)
  end
end

Globally Configurable Options

All of these can be set on SkinnyControllers, e.g.:

SkinnyControllers.controller_namespace = 'API'

The following options are available:

Option Default Note
operations_namespace '' Optional namespace to put all the operations in.
operations_suffix 'Operations' Default suffix for the operations namespaces.
policy_suffix 'Policy' Default suffix for policies classes.
controller_namespace '' Global Namespace for all controllers (e.g.: 'API')
allow_by_default true Default permission
accessible_to_method is_accessible_to? method to call an the object that the user might be able to access
accessible_to_scope accessible_to scope / class method on an object that the user might be able to access
action_map see skinny_controllers.rb

TODO

  • Configurable Error Renderer
    • Default to JSON API format errors?