Decorators
Usage
We define only a single decorator per model that must inherit from AF83::Decorator. This model decorator handles both collections and instances. To decorate an Item object:
# app/controllers/items_controller.rb
def index
# ...
@items = ItemDecorator.decorate(@items)
# ...
end
def show
# ...
@item = @item.decorate
# ...
end
The actions on the collection are defined at the root of the decorator, while those for instances are defined with the help of a with_instance_decorator block.
To define an action, we use the action_link method. The method accepts these options:
-
on: only shows the link on the pages for the listed actions (e.g.on: %i(show index)) -
if: takes aProcand only displays the link if the proc result is truthy -
policy: only shows the link if the current user has the permission for the decorated object and action link (takes an action argument, for example:edit, or:create) -
feature: likepolicy, but for afeature -
weight: allow the links' display order to be changed, withweight: 1shown first (by default, the declaration order is used)
Actions are also split into groups. These can be customised in several ways:
groups: {
primary: true,
secondary: %i(index show),
any_other_group: :index
}
Shortcuts are available for the primary, secondary, and footer groups. These enable us to write action_link primary: :index instead of action_link groups: { primary: :index }.
Link content is defined in a block. Each method called on the object passed into the block is converted into an attribute on the resulting HTML tag, except for these:
-
extra_class: Adds one or more classes to the link. Takes either anArrayor aString. -
class: Replaces the link's classes. Takes either anArrayor aString. -
type: Allows the HTML tag to be changed. The only value currently implemented is:button. All others fall back on<a>.
Shortcuts
Four methods are available to define common actions:
-
create_action_link: adds acreatelink for a collection -
show_action_link: adds ashowlink on an instance -
edit_action_link: adds aneditlink on an instance -
destroy_action_link: adds adestroylink on an instance
These are defined in the AF83::Decorator::EnhancedDecorator module.
Behavior
The default behavior is as follows:
On the index page
Actions from the collection's primary group are rendered in the main section of the header (and retained in the "sticky" header). Below the main header, actions for the collection's secondary group are rendered.
In the TableBuilderHelper, all actions in the primary, secondary, and footer groups are rendered in a menu next to each instance, separated by group. The footer group always comes last.
On the show page
Actions from the object's primary group are rendered in the main section of the header (and retained in the "sticky" header). Below the main header, actions for the object's secondary group are rendered.
Examples
Simple decorator
class CalendarDecorator < AF83::Decorator
decorates Calendar
create_action_link
with_instance_decorator do |instance_decorator|
instance_decorator.show_action_link
instance_decorator.edit_action_link
instance_decorator.destroy_action_link do |l|
l.data {{ confirm: h.t('calendars.actions.destroy_confirm') }}
end
end
end
More complex decorator
class LineDecorator < AF83::Decorator
decorates Chouette::Line
create_action_link do |l|
l.content t('lines.actions.new')
l.href { h.new_line_referential_line_path(context[:line_referential]) }
end
with_instance_decorator do |instance_decorator|
instance_decorator.show_action_link do |l|
l.content t('lines.actions.show')
l.href { [context[:line_referential], object] }
end
instance_decorator.action_link do |l|
l.content t('lines.actions.show_network')
l.href { [context[:line_referential], object.network] }
end
instance_decorator.action_link do |l|
l.content t('lines.actions.show_company')
l.href { [context[:line_referential], object.company] }
l.disabled { object.company.nil? }
end
can_edit_line = ->(){ h.policy(Chouette::Line).create? && context[:line_referential].organisations.include?(context[:current_organisation]) }
instance_decorator.with_condition can_edit_line do
edit_action_link do |l|
l.content {|l| l.primary? ? h.t('actions.edit') : h.t('lines.actions.edit') }
l.href { h.edit_line_referential_line_path(context[:line_referential], object.id) }
end
action_link on: :index, secondary: :index do |l|
l.content t('lines.actions.new')
l.href { h.new_line_referential_line_path(context[:line_referential]) }
end
end
instance_decorator.action_link policy: :deactivate, secondary: :show, footer: :index do |l|
l.content { h.deactivate_link_content('lines.actions.deactivate') }
l.href { h.deactivate_line_referential_line_path(context[:line_referential], object) }
l.method :put
l.data confirm: h.t('lines.actions.deactivate_confirm')
l.extra_class "delete-action"
end
instance_decorator.action_link policy: :activate, secondary: :show, footer: :index do |l|
l.content { h.activate_link_content('lines.actions.activate') }
l.href { h.activate_line_referential_line_path(context[:line_referential], object) }
l.method :put
l.data confirm: h.t('lines.actions.activate_confirm')
l.extra_class "delete-action"
end
instance_decorator.destroy_action_link do |l|
l.content { h.destroy_link_content('lines.actions.destroy') }
l.href { h.line_referential_line_path(context[:line_referential], object) }
l.data confirm: h.t('lines.actions.destroy_confirm')
l.extra_class "delete-action"
end
end
end
Breadcrumb
The breadcrumb is built with the Gretel gem. Its configuration is stored in the config/breadcrumbs.rb file.