Thinning controllers for easier unit testing and separation of concerns

I wrote this gem, https://github.com/NullVoxPopuli/skinny_controllers

which is influenced by Trailblazer, but less with some different opinions here and there.

The main idea that it pulls out of trailblazer is operations and policies.

Operations are in app/operations

Policies are in app/policies

Operations control what data you are getting, and Policies control if you are allowed to get it.

The goal is to have all the logic in ruby objects that don’t require a controller instance to run (i.e. avoiding put/get/post in your tests).

At the very minimum, you’d need


include SkinnyControllers::Diet

# and then in your action

render json: model

and the rest is implicit.

obviously, not everyone wants the same create / update / destroy functionality, so that can all be configured:

here is an example of the create operation for the EventsController


module EventOperations

class Create < SkinnyControllers::Operation::Base

def run

return unless allowed?

# @model is used here, because the method `model` is memoized off @model

@model = model_class.new(model_params)

@model.save

@model # or just `model`

end

end

end

allowed? is a special method that looks up the corresponding policy:


class EventPolicy < SkinnyControllers::Policy::Base

# for the Create operation for the EventsController is matched to create? in the policy

# o / object here is the instance of the model trying to be created

def create?(o = object)

user.is_admin?

end

end

Thoughts?

I’m using this in a big side project I’ve been working on for event registration. (Rails API + Ember)

example of operation specs:

https://github.com/NullVoxPopuli/aeonvera/blob/master/spec/operations/order_operations_spec.rb

example of policy specs:

https://github.com/NullVoxPopuli/aeonvera/blob/master/spec/policies/package_policy_spec.rb