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::ApplicationControlleris taken for other purpose.
NOTE:
Admin::ApplicationControllerhas access to all methods in::ApplicationController. Because its parentWallaby::ResourcesControllerinherits from::ApplicationControllerunless this is changed in Wallaby base_controller configuration.
Starting with:
- Create a controller to do customization at controller level.
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:
- authenticate_wallaby_user! - authenticates the current user.
- wallaby_user - returns the object for the signed-in user.
- collection - returns the paginated collection.
- resource - returns the resource by given ID.
- new_resource - returns a new resource instance.
- resource_params - returns the white-listed parameters for mass assignment.
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:
@collectionMUST 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:
@resourceMUST 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:
@resourceMUST 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:
@resourceMUST 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:
@resourceMUST 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:
@resourceMUST 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:
@collectionMUST 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:
@resourceMUST 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:
@resourceMUST 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::ApplicationControllerwill inherit the same engine name when it is set.
See Route - Engine Helper for more details.