skinny-controllers
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.
- Your controller name is based off your model name (configurable per controller)
- Any defined policies or operations follow the formats (though they don't have to exist):
#{Model.name}Policy#{Model.name}Operations
- Your model responds to
find, andwhere - Your model responds to
is_accessible_to?. This can be changed atSkinnyControllers.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:
EventSummariesControllerEventSummaryOperations::*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?