Project

api-blocks

0.0
No release in over 3 years
Low commit activity in last 3 years
Simple and consistent rails api extensions
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

>= 0
>= 0
~> 6.0
>= 0
>= 3.0.0
= 1.8.1
>= 0

Runtime

 Project Readme

ApiBlocks

Gem Code Climate Inch

ApiBlocks provides simple and consistent Rails API extensions.

Links:

Installation

gem 'api-blocks'

Configuration

In an initializer such as config/initializers/api_blocks.rb you can enable the optional blueprinter and batch-loader integration:

ApiBlocks.configure do |config|
  config.blueprinter.use_batch_loader = true
end

This allows you to use batch-loader in order to avoid n+1 queries when serializing associations in blueprints.

This has some caveats which are documented in association_extractor.rb.

ApiBlocks::Controller

Include ApiBlocks::Controller in your api controller:

class Api::V1::ApplicationController < ActionController::API
  include ApiBlocks::Controller

  pundit_scope :api, :v1
end

Including the module will:

  • Setup ApiBlocks::Responder as a responder.
  • Add the verify_request_format! before_action hook.
  • Setup Pundit, rescue its errors, setup its validation hooks and provide the pundit_scope method.

ApiBlocks::Responder

An ActionController::Responder with better error handling and Dry::Monads::Result support.

Errors are handled for the following cases:

  • The responded resource is an ApplicationRecord subclass and has error.
  • The responded resource is a ActiveRecord::RecordInvalid exception.
  • Otherwise the error is re-raised to be handled through the usual Ruby On Rails error handlers.

In addition, the responder will render resources on POST and PUT rather than returning a redirection.

ApiBlocks::Interactor

It implements a basic interactor base class using dry-transaction and dry-validation under the hood.

It provides to predefined steps:

  • validate_input! which will validate the interactor input according to its schema.
  • database_transaction! an around step that wraps the interactor in an ActiveRecord transaction.

Example:

class Requests::MarkAsRead < ApiBlocks::Interactor
  input do
    schema do
      required(:request).filled(type?: Request)
    end
  end

  around :database_transaction!
  step :validate_input!

  try :update_request!, catch: ActiveRecord::RecordInvalid
  try :create_history_item!, catch: ActiveRecord::RecordInvalid

  def update_request!(request:)
    request.update!(read_at: Time.now.utc)
    request
  end

  def create_history_item!(request)
    request.request_history_items.create!(kind: :read)
    request
  end
end

ApiBlocks::Doorkeeper::Passwords

Implement an API for passwords reset using doorkeeper and devise.

Include the ApiBlocks::Doorkeeper::Passwords::Controller module in your passwords api controller and define the user_model method to return the concerned devise user model.

# app/controllers/api/v1/passwords_controller.rb
class Api::V1::PasswordsController < Api::V1::ApplicationController
  include ApiBlocks::Doorkeeper::Passwords::Controller

  private

  def user_model
    User
  end
end

Then add the approriate routes to your configuration.

# config/routes.rb
Rails.application.routes.draw do
  scope module: :api do
    namespace :v1 do
      resources :passwords, only: %i[create] do
        get :callback, on: :collection
        put :update, on: :collection
      end
    end
  end
end

Include the ApiBlocks::Doorkeeper::ResetPassword module so devise will forward the doorkeeper application to the mailer.

# app/models/user.rb
class User < ApplicationRecord
  include ApiBlocks::Doorkeeper::ResetPassword
end

Include the reset password Doorkeeper::Application extensions.

# config/initializers/doorkeeper.rb

Doorkeeper.configure do
  # ...
end

class ::Doorkeeper::Application < ActiveRecord::Base
  include ApiBlocks::Doorkeeper::Passwords::Application
end

Override your devise mailer #reset_password_instructions method to add the application parameter.

# app/mailers/devise_mailer.rb

class DeviseMailer < Devise::Mailer
  def reset_password_instructions(record, token, application = nil, _opts = {})
    @token = token
    @application = application
  end
end

Update the devise mailer template to link to the callback API.

# app/views/devise/mailer/reset_password_instructions.html.erb
<p><%= link_to "Change my password", callback_v1_passwords_url(reset_password_token: @token) %></p>

Finally, generate the required migrations:

bundle exec rails g api_blocks:doorkeeper:passwords:migration

ApiBlocks::Doorkeeper::Invitations

Implement an API for devise_invitable using doorkeeper.

Include the ApiBlocks::Doorkeeper::Invitations::Controller module in your api controller and define the user_model method to return the concerned devise user model.

# app/controllers/api/v1/invitations_controller.rb
class Api::V1::InvitationsController < Api::V1::ApplicationController
  include ApiBlocks::Doorkeeper::Invitations::Controller

  private

  def user_model
    User
  end
end

Add the approriate routes to your configuration.

# config/routes.rb
Rails.application.routes.draw do
  scope module: :api do
    namespace :v1 do
      resources :invitations, only: %i[create show] do
        get :callback, on: :collection
        put :update, on: :collection
      end
    end
  end
end

Include the invitations Doorkeeper::Application extensions.

# config/initializers/doorkeeper.rb

Doorkeeper.configure do
  # ...
end

class ::Doorkeeper::Application < ActiveRecord::Base
  include ApiBlocks::Doorkeeper::Invitations::Application
end

Override your devise mailer #invitation_instructions method to add the application parameter.

# app/mailers/devise_mailer.rb

class DeviseMailer < Devise::Mailer
  def invitation_instructions(_record, token, application: nil, **_opts)
    @token = token
    @application = application

    super
  end
end

Update the devise mailer template to link to the callback API.

# app/views/devise/mailer/invitation_instructions.html.erb
<p><%= link_to  t("devise.mailer.invitation_instructions.accept"), callback_v1_invitations_url(invitation_token: @token, client_id: @application.uid) %></p>

Finally, generate the required migrations:

bundle exec rails g api_blocks:doorkeeper:invitations:migration

External Resources

License

Licensed under the MIT license, see the separate LICENSE.txt file.