Controller

Wallaby resources controller is just an ordinary Rails controller that has implemented default resourcesful actions for all its subclasses. Therefore, customization becomes as simple as overriding the existing actions.

First of all, it’s always recommended to create a base controller class Admin::ApplicationController, so that devs have better control of global changes/functions:

# app/controllers/admin/application_controller.rb
class Admin::ApplicationController < Wallaby::ResourcesController
  base_class!
end

This base controller can be generated by Wallaby generator as well:

rails g wallaby:install

See resources_controller configuration if Admin::ApplicationController is taken for other purpose.

NOTE: Admin::ApplicationController has access to all methods in ::ApplicationController. Because its parent Wallaby::ResourcesController inherits from ::ApplicationController unless this is changed in Wallaby base_controller configuration.

Starting with:

Configuration can be set for:

  • .base_class! - marks current controller class as base class.
  • .model_class - specifies the associated model class for current controller class.

The following resourcesful actions can be customized:

  • index - lists the collection of resources.
  • new - displays resource creation form.
  • create - handles resource creation.
  • show - displays single resource.
  • edit - displays resource edit form.
  • update - handles resource update.
  • destroy - handles resource deletion.

For non-resourcesful actions:

  • home - works as the landing page action.

And it is possible:

Other configuration:

  • .theme_name - specifies the theme and layout for current controller class and its subclasses.
  • .resource_decorator - specifies the associated resource decorator class for current controller class.
  • .model_servicer - specifies the associated model servicer class for current controller class.
  • .model_authorizer - specifies the associated model authorizer class for current controller class.
  • .model_paginator - specifies the associated model paginator class for current controller class.

More advanced configuration:

  • .engine_name - specifies the name of engine helper for current controller class and its subclasses.
  • .application_decorator - specifies the associated base resource decorator class for current controller class and its subclasses.
  • .application_servicer - specifies the associated base model servicer class for current controller class and its subclasses.
  • .application_authorizer - specifies the associated base model authorizer class for current controller class and its subclasses.
  • .application_paginator - specifies the associated base model paginator class for current controller class and its subclasses.

Create a controller

Read more at Controller Naming Convention

To do customization at controller level for a model (e.g. Product), it is required to create a controller to inherit from the above base class Admin::ApplicationController:

# app/controllers/admin/products_controller.rb
class Admin::ProductsController < Admin::ApplicationController
end

Then the customization will be as simple as overriding the below default resourcesful actions (index, new, create, show, edit, update and destroy).

Configuration

.base_class!

All controllers will be preloaded and processed by Wallaby in order to build up the mapping between controllers and models. If the controller is considered not to be processed, it can be flagged as abstract by using base_class!:

# app/controllers/admin/special_controller.rb
class Admin::SpecialController < Admin::ApplicationController
  base_class!
end

.model_class

According to Wallaby’s Controller Naming Convention, if a custom controller cannot reflect its associated model, e.g. Admin::GoodsController and Product, model class should be specified for the controller to work properly:

# app/controllers/admin/goods_controller.rb
class Admin::GoodsController < Admin::ApplicationController
  self.model_class = Product
end

Actions

index

To add functionality to the index action, override it with super method index!:

def index
  # do something before the origin action
  index! do |format| # `respond_with` block
    # do something here before rendering
  end
end

Basically, the implementation is as simple as:

def index(options = {}, &block)
  current_authorizer.authorize :index, current_model_class
  respond_with collection, options, &block
end

And it can be replaced completely:

def index
  @collection = Product.all
end

NOTE: @collection MUST be assigned, because it’s used by the view.

new

To add functionality to the new action, override it with super method new!:

def new
  # do something before the origin action
  new! do |format| # `respond_with` block
    # do something here before rendering
  end
end

Basically, the implementation is as simple as:

def new(options = {}, &block)
  current_authorizer.authorize :new, resource
  respond_with resource, options, &block
end

And it can be replaced completely:

def new
  @resource = Product.new new_arrival: true
end

NOTE: @resource MUST be assigned, because it’s used by the view.

create

To add functionality to the create action, override it with super method create!:

def create
  # do something before the origin action
  create!(location: -> { { action: :index } }) do |format| # `respond_with` block
    # do something here before rendering
  end
end

Basically, the implementation is as simple as:

def create(location: -> { { action: :show, id: new_resource.id } }, **responder_options, &block)
  current_authorizer.authorize :create, new_resource
  current_servicer.create new_resource, create_params
  respond_with new_resource, responder_options.merge(location: location), &block
end

And it can be replaced completely:

def create
  @resource = Product.create(resource_params.merge new_arrival: true)
  redirect_to @resource
end

NOTE: @resource MUST be assigned, because it’s used by the view.

show

To add functionality to the show action, override it with super method show!:

def show
  # do something before the origin action
  show! do |format| # `respond_with` block
    # do something here before rendering
  end
end

Basically, the implementation is as simple as:

def show(options = {}, &block)
  current_authorizer.authorize :show, resource
  respond_with resource, options, &block
end

And it can be replaced completely:

def show
  @resource = Product.friendly.find params[:id]
end

NOTE: @resource MUST be assigned, because it’s used by the view.

edit

To add functionality to the edit action, override it with super method edit!:

def edit
  # do something before the origin action
  edit! do |format| # `respond_with` block
    # do something here before rendering
  end
end

Basically, the implementation is as simple as:

def edit(options = {}, &block)
  current_authorizer.authorize :edit, resource
  respond_with resource, options, &block
end

And it can be replaced completely:

def edit
  @resource = Product.friendly.find params[:id]
end

NOTE: @resource MUST be assigned, because it’s used by the view.

update

To add functionality to the update action, override it with super method update!:

def update
  # do something before the origin action
  update!(location: -> { { action: :index } }) do |format| # `respond_with` block
    # do something here before rendering
  end
end

Basically, the implementation is as simple as:

def update(location: -> { { action: :show } }, **responder_options, &block)
  current_authorizer.authorize :update, resource
  current_servicer.update resource, update_params
  respond_with resource, responder_options.merge(location: location), &block
end

And it can be replaced completely:

def update
  @resource = Product.friendly.find params[:id]
  @resource.update resource_params
  redirect_to @resource
end

NOTE: @resource MUST be assigned, because it’s used by the view.

destroy

To add functionality to the destroy action, override it with super method destroy!:

def destroy
  # do something before the origin action
  destroy!(location: -> { { action: :index } }) do |format| # `respond_with` block
    # do something here before rendering
  end
end

Basically, the implementation is as simple as:

def destroy(location: -> { { action: resource_id ? :index : :show } }, **responder_options, &block)
  current_authorizer.authorize :destroy, resource
  current_servicer.destroy resource, {}
  respond_with resource, responder_options.merge(location: location), &block
end

And it can be replaced completely:

def destroy
  @resource = Product.friendly.find params[:id]
  @resource.destroy
  redirect_to @resource
end

home

This is the landing page of the Wallaby Admin Interface (when Wallaby engine is mounted).

Basically, it does nothing as:

def home
  # do nothing
end

It can be replaced completely in the base controller class (e.g. Admin::ApplicationController):

def home
  @reports = build_reports
end

Methods

authenticate_wallaby_user!

This is the method that Wallaby uses as a before_action to check if user has signed in or not. By default, it tries to call authenticate_user! from the base controller (e.g. Devise).

To customize, override it:

def authenticate_wallaby_user!
  # http basic authentication
  authenticate_or_request_with_http_basic do |username, password|
    username == 'too_simple' && password == 'too_naive'
  end
end

Or it can be customized via authenticate_wallaby_user! configuration.

wallaby_user

This is the method that Wallaby uses to return the object of the signed-in user.

NOTE: it is also a helper method available in the view.

To customize, override it:

def wallaby_user
  # user example
  @wallaby_user ||= Class.new do
    def email
      'user@example.com'
    end
  end.new
end

Or it can be customized via wallaby_user configuration.

collection

This is the method to return the collection for index page to display.

NOTE: it is also a helper method available in the view.

NOTE: pagination should be done in this method as well.

To add functionality, override it with super method collection!:

def collection
  collection!(
    params: params, # params to use for the servicer
    paginate: true # whether to paginate the collection
  ) do |records|
    # do something here after origin query is completed
    # but before `@collection` instance variable is set
    records
  end
end

Or it can be replaced completely:

def collection
  @collection ||= Product.new_arrival.page(params[:page]).per(params[:per])
end

NOTE: @collection MUST be assigned, because it’s used by the view.

resource

This is the method to return the resource for show, edit, update and destroy action.

NOTE: it is also a helper method available in the view.

To add functionality, override it with super method resource!:

def resource
  resource! do |record|
    # do something here after origin query is completed
    # but before `@resource` instance variable is set
    record
  end
end

Or it can be replaced completely:

def resource
  @resource ||= Product.find_by_slug params[:id]
end

NOTE: @resource MUST be assigned, because it’s used by the view.

new_resource

This is the method to return a new resource instance for new and create action.

To add functionality, override it with super method new_resource!:

def new_resource
  new_resource! do |record|
    # do something here after the resource is initialized
    # but before `@resource` instance variable is set
    record
  end
end

Or it can be replaced completely:

def new_resource
  @resource ||= Product.new new_arrival: true
end

NOTE: @resource MUST be assigned, because it’s used by the view.

resource_params

This is the method that returns the parameters for mass assignment for create and update action.

Basically, the implementation is as simple as:

def resource_params
  @resource_params ||= current_servicer.permit params, action_name
end

To customize, override it:

def resource_params
  params.require(:product).permit(:name, :sku)
end

More Configuration

.theme_name

A theme is a set of view layout and templates that follow Layout Inheritance and Template Inheritance.

Read more at Theme.

To apply a theme, it simply goes:

# app/controllers/admin/application_controller.rb
class Admin::ApplicationController < Wallaby::ResourcesController
  self.theme_name = 'bootstrap4'
end

.resource_decorator

Wallaby should be able to pick the appropriate resource decorator for current controller. However, if Wallaby doesn’t get it right, resource decorator can be configured as below:

class Admin::ProductDecorator < Admin::ApplicationDecorator
end

class ProductDecorator < Admin::ApplicationDecorator
end

class Admin::ProductsController < Admin::ApplicationController
  resource_decorator # => Admin::ProductDecorator
  self.resource_decorator = ProductDecorator
end

.application_decorator

It is possible for a controller to nominate a different base decorator class so that the controller and its subclasses will use the same base decorator. For example:

class SuperAdminDecorator < Wallaby::ResourceDecorator
  def owner_to_display
    super
  end
end

class AdvisorOnlyDecorator < Wallaby::ResourceDecorator
  def owner_to_display
    '<n/a>'
  end
end

class Admin::ApplicationController < Wallaby::ResourcesController
  application_decorator # nil
  self.application_decorator = SuperAdminDecorator
end

.model_servicer

Wallaby should be able to pick the appropriate model servicer for current controller. However, if Wallaby doesn’t get it right, model servicer can be configured as below:

class Admin::ProductServicer < Admin::ApplicationServicer
end

class ProductServicer < Admin::ApplicationServicer
end

class Admin::ProductsController < Admin::ApplicationController
  model_servicer # => Admin::ProductServicer
  self.model_servicer = ProductServicer
end

.application_servicer

It is possible for a controller to nominate a different base servicer class so that the controller and its subclasses will use the same base servicer. For example:

class SuperAdminServicer < Wallaby::ModelServicer
  def create(resource, params)
    super resource, params.merge(admin: true)
  end
end

class AdvisorOnlyServicer < Wallaby::ModelServicer
  def create(resource, params)
    super resource, params.merge(admin: false)
  end
end

class Admin::ApplicationController < Wallaby::ResourcesController
  application_servicer # nil
  self.application_servicer = SuperAdminServicer
end

.model_authorizer

Wallaby should be able to pick the appropriate model authorizer for current controller. However, if Wallaby doesn’t get it right, model authorizer can be configured as below:

class Admin::ProductAuthorizer < Admin::ApplicationAuthorizer
end

class ProductAuthorizer < Admin::ApplicationAuthorizer
end

class Admin::ProductsController < Admin::ApplicationController
  model_authorizer # => Admin::ProductAuthorizer
  self.model_authorizer = ProductAuthorizer
end

.application_authorizer

It is possible for a controller to nominate a different base authorizer class so that the controller and its subclasses will use the same base authorizer. For example:

class SuperAdminAuthorizer < Wallaby::ModelAuthorizer
  def authorized?(action, subject)
    true
  end
end

class AdvisorOnlyAuthorizer < Wallaby::ModelAuthorizer
  def authorized?(action, subject)
    false
  end
end

class Admin::ApplicationController < Wallaby::ResourcesController
  application_authorizer # nil
  self.application_authorizer = SuperAdminAuthorizer
end

.model_paginator

Wallaby should be able to pick the appropriate model paginator for current controller. However, if Wallaby doesn’t get it right, model paginator can be configured as below:

class Admin::ProductPaginator < Admin::ApplicationPaginator
end

class ProductPaginator < Admin::ApplicationPaginator
end

class Admin::ProductsController < Admin::ApplicationController
  model_paginator # => Admin::ProductPaginator
  self.model_paginator = ProductPaginator
end

.application_paginator

It is possible for a controller to nominate a different base paginator class so that the controller and its subclasses will use the same base paginator. For example:

class SuperAdminPaginator < Wallaby::ModelPaginator
  def authorized?(action, subject)
    true
  end
end

class AdvisorOnlyPaginator < Wallaby::ModelPaginator
  def authorized?(action, subject)
    false
  end
end

class Admin::ApplicationController < Wallaby::ResourcesController
  application_paginator # nil
  self.application_paginator = SuperAdminPaginator
end

.engine_name

Wallaby should be able to determine the engine name (e.g. wallaby_engine) for the mounted engine to generate the resourceful path/url helpers. If Wallaby doesn’t get it right, the engine name can be configured as below:

# app/controllers/admin/application_controller.rb
class Admin::ApplicationController < Wallaby::ResourcesController
  self.engine_name = :manager_engine
end

NOTE: All subclasses of Admin::ApplicationController will inherit the same engine name when it is set.

See Route - Engine Helper for more details.