darthjee/azeroth

View on GitHub
lib/azeroth/resourceable.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
# frozen_string_literal: true

require 'active_support'

module Azeroth
  # @api public
  # @author Darthjee
  #
  # Concern for building controller methods for the routes
  #
  # @see Resourceable::ClassMethods
  module Resourceable
    extend ActiveSupport::Concern

    included do
      rescue_from ActiveRecord::RecordNotFound, with: :not_found
    end

    autoload :Builder, 'azeroth/resourceable/builder'
    autoload :ClassMethods, 'azeroth/resourceable/class_methods'

    class << self
      # @method self.resource_for(name, **options)
      # @api public
      #
      # @param name [String, Symbol] Name of the resource
      # @param options [Hash] resource building options
      # @option options only [Array<Symbol,String>,Symbol,String] List of
      #   actions to be built
      # @option options except [Array<Symbol,String>,Symbol,String] List of
      #   actions to not to be built
      # @option options decorator [Azeroth::Decorator,TrueClass,FalseClass]
      #   Decorator class or flag allowing/disallowing decorators
      # @option options before_save [Symbol,Proc] method/block
      #   to be ran on the controller before saving the resource
      # @option options after_save [Symbol,Proc] method/block
      #   to be ran on the controller after saving the resource
      # @option options build_with [Symbol,Proc] method/block
      #   to be ran when building resource
      #   (default proc { <resource_collection>.build(resource_params) }
      # @option options update_with [Symbol,Proc] method/block
      #   to be ran when updating resource
      #   (default proc { <resource>.update(resource_params) }
      # @option options paginated [TrueClass,FalseClass] flag defining if index
      #   endpoint should be paginated
      # @option options per_page [Integer] number of entries returned per
      #   page on index
      #
      # @return [Array<MethodDefinition>] list of methods created
      #
      # @see Options::DEFAULT_OPTIONS
      #
      # @example Controller without delete
      #   class DocumentsController < ApplicationController
      #     include Azeroth::Resourceable
      #
      #     resource_for :document, except: :delete
      #   end
      #
      # @example Controller with only create, show and list
      #   class DocumentsController < ApplicationController
      #     include Azeroth::Resourceable
      #
      #     resource_for :document, only: %w[create index show]
      #   end
      #
      # @example complete example gmaes and publishers
      #   class PublishersController < ApplicationController
      #     include Azeroth::Resourceable
      #     skip_before_action :verify_authenticity_token
      #
      #     resource_for :publisher, only: %i[create index]
      #   end
      #
      #   class GamesController < ApplicationController
      #     include Azeroth::Resourceable
      #     skip_before_action :verify_authenticity_token
      #
      #     resource_for :game, except: :delete
      #
      #     private
      #
      #     def games
      #       publisher.games
      #     end
      #
      #     def publisher
      #       @publisher ||= Publisher.find(publisher_id)
      #     end
      #
      #     def publisher_id
      #       params.require(:publisher_id)
      #     end
      #   end
      #
      #   ActiveRecord::Schema.define do
      #     self.verbose = false
      #
      #     create_table :publishers, force: true do |t|
      #       t.string :name
      #     end
      #
      #     create_table :games, force: true do |t|
      #       t.string :name
      #       t.integer :publisher_id
      #     end
      #    end
      #
      #   class Publisher < ActiveRecord::Base
      #     has_many :games
      #   end
      #
      #   class Game < ActiveRecord::Base
      #     belongs_to :publisher
      #   end
      #
      #   class Game::Decorator < Azeroth::Decorator
      #     expose :id
      #     expose :name
      #     expose :publisher, decorator: NameDecorator
      #   end
      #
      # @example requesting games and publishers
      #   post "/publishers.json", params: {
      #     publisher: {
      #       name: 'Nintendo'
      #     }
      #   }
      #
      #   publisher = JSON.parse(response.body)
      #   # returns
      #   # {
      #   #   'id' => 11,
      #   #   'name' => 'Nintendo'
      #   # }
      #
      #   publisher = Publisher.last
      #   post "/publishers/#{publisher['id']}/games.json", params: {
      #     game: {
      #       name: 'Pokemon'
      #     }
      #   }
      #
      #   game = Game.last
      #
      #   JSON.parse(response.body)
      #   # returns
      #   # {
      #   #   id: game.id,
      #   #   name: 'Pokemon',
      #   #   publisher: {
      #   #     name: 'Nintendo'
      #   #   }
      #   # }
      #
      # @example Controller with before_save
      #   class PokemonsController < ApplicationController
      #     include Azeroth::Resourceable
      #
      #     resource_for :pokemon,
      #                  only: %i[create update],
      #                  before_save: :set_favorite
      #
      #     private
      #
      #     def set_favorite
      #       pokemon.favorite = true
      #     end
      #
      #     def pokemons
      #       master.pokemons
      #     end
      #
      #     def master
      #       @master ||= PokemonMaster.find(master_id)
      #     end
      #
      #     def master_id
      #       params.require(:pokemon_master_id)
      #     end
      #   end
      #
      # @example Controller with paginated index response
      #
      #   class PaginatedDocumentsController < ApplicationController
      #     include Azeroth::Resourceable
      #
      #     resource_for :document, only: 'index', paginated: true
      #   end
      #
      #   30.times { create(:document) }
      #
      #   get '/paginated_documents.json'
      #
      #   # returns Array with 20 first documents
      #   # returns in the headers pagination headers
      #   {
      #     'pages' => 2,
      #     'per_page' => 20,
      #     'page' => 1
      #   }
      #
      #   get '/paginated_documents.json?page=2'
      #
      #   # returns Array with 10 next documents
      #   # returns in the headers pagination headers
      #   {
      #     'pages' => 2,
      #     'per_page' => 20,
      #     'page' => 2
      #   }
    end

    private

    # @api private
    # @private
    #
    # returns 404 as HTTP status
    #
    # @return [TrueClass]
    def not_found
      head :not_found
    end
  end
end