|
|
|
## 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:
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
# 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 a `Proc` and 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`: like `policy`, but for a `feature`
|
|
|
|
- `weight`: allow the links' display order to be changed, with `weight: 1` shown first (by default, the declaration order is used)
|
|
|
|
|
|
|
|
Actions are also split into **groups**. These can be customised in several ways:
|
|
|
|
```ruby
|
|
|
|
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 an `Array` or a `String`.
|
|
|
|
- `class`: **Replaces** the link's classes. Takes either an `Array` or a `String`.
|
|
|
|
- `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 a `create` link for a collection
|
|
|
|
- `show_action_link`: adds a `show` link on an instance
|
|
|
|
- `edit_action_link`: adds an `edit` link on an instance
|
|
|
|
- `destroy_action_link`: adds a `destroy` link 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
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
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
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
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](https://github.com/lassebunk/gretel) gem. Its configuration is stored in the `config/breadcrumbs.rb` file. |