Skip to content

Commit

Permalink
Merge pull request #184 from sedubois/actions
Browse files Browse the repository at this point in the history
Thanks, @sedubois, for your time and attention!
  • Loading branch information
adrianthedev authored Apr 11, 2024
2 parents 5ace68c + 8d9d70f commit 4cbf5cc
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 101 deletions.
160 changes: 67 additions & 93 deletions docs/3.0/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,52 @@ Once you attach an action to a resource using the `action` method inside the `ac
Since version <Version version="2.13" /> you may use the [customizable controls](./customizable-controls) feature to show the actions outside the dropdown.
:::

## Overview
## Creating an action

You generate one running `bin/rails generate avo:action toggle_active`, creating an action configuration file.
To generate an action configuration file, run `bin/rails generate avo:action toggle_active`.

```ruby
class Avo::Actions::ToggleActive < Avo::BaseAction
self.name = 'Toggle inactive'
# app/avo/actions/toggle_active.rb

def fields
field :notify_user, as: :boolean, default: true
field :message, as: :text, default: 'Your account has been marked as inactive.'
end
class Avo::Actions::ToggleActive < Avo::BaseAction
self.name = "Toggle Active"
# self.visible = -> do
# true
# end

def handle(**args)
query, fields, current_user, resource = args.values_at(:query, :fields, :current_user, :resource)
# def fields
# # Add Action fields here
# end

def handle(query:, fields:, current_user:, resource:, **args)
query.each do |record|
if record.active
record.update active: false
else
record.update active: true
end

# Optionally, you may send a notification with the message to that user from inside the action
UserMailer.with(user: record).toggle_active(fields["message"]).deliver_later
# Do something with your records.
end
end
end
```

## Registering an action

To add the action to a resource, declare it inside the `actions` method as follows:

```ruby{9}
class Avo::Resources::User < Avo::BaseResource
self.title = :name
def fields
field :id, as: :id
end
succeed 'Perfect!'
def actions
action Avo::Actions::ToggleActive
end
end
```

You may add fields to the action just as you do it in a resource. Adding fields is optional. You may have actions that don't have any fields attached.
## The `fields` method (optional)

You may add fields to the action just as you do it in a resource. Adding fields is optional.

:::warning
The `belongs_to` field will only work on the <Show /> and <Edit /> page of a record. It won't work on the <Index /> page of a resource.
Expand All @@ -74,42 +87,19 @@ More about this on the [authorization page](./authorization#attachments).

![Actions](/assets/img/actions/action-fields.jpg)

The `handle` method is where the magic happens. That is where you put your action logic. In this method, you will have access to the `query` (same value as `records` (if there's only one, it will be automatically wrapped in an array)) and the values passed to the `fields`.

```ruby
def handle(**args)
query, fields = args.values_at(:query, :fields)

query.each do |record|
if record.active
record.update active: false
else
record.update active: true
end

# Optionally, you may send a notification with the message to that user.
UserMailer.with(user: record).toggle_active(fields["message"]).deliver_later
end

succeed 'Perfect!'
end
```

## Registering actions

To add an action to one of your resources, you need to declare it inside the `actions` method on the resource using the `action` method.
## The `handle` method

```ruby{9}
class Avo::Resources::User < Avo::BaseResource
self.title = :name
This is where the magic happens. This method contains your action logic.

def fields
field :id, as: :id
end
The handle method receives the following arguments:
- `query` and `records`: both names can be used interchangeably. Single records are automatically wrapped in an array.
- `fields`
- `current_user`
- `resource`

def actions
action Avo::Actions::ToggleActive
end
```ruby
def handle(query:, fields:, current_user:, resource:, **args)
# Do something
end
```

Expand All @@ -121,7 +111,7 @@ The default response is to reload the page and show the _Action ran successfully

### Message responses

You will have four message response methods at your disposal `succeed`, `error`, `warn`, and `inform`. These will render the user green, red, orange, and blue alerts.
Four message response methods are at your disposal: `succeed`, `error`, `warn`, and `inform`. These render green, red, orange, and blue alerts.

```ruby{4-7}
def handle(**args)
Expand Down Expand Up @@ -154,10 +144,8 @@ end
After you notify the user about what happened through a message, you may want to execute an action like `reload` (default action) or `redirect_to`. You may use message and action responses together.

```ruby{14}
def handle(**args)
records = args[:records]
records.each do |record|
def handle(query:, **args)
query.each do |record|
if record.admin?
error "Can't mark inactive! The user is an admin."
else
Expand All @@ -178,10 +166,8 @@ The available action responses are:
When you use `reload`, a full-page reload will be triggered.

```ruby{9}
def handle(**args)
records = args[:records]
records.each do |project|
def handle(query:, **args)
query.each do |project|
project.update active: false
end
Expand All @@ -199,10 +185,8 @@ Example:
`redirect_to path, allow_other_host: true, status: 303`

```ruby{9}
def handle(**args)
records = args[:records]
records.each do |project|
def handle(query:, **args)
query.each do |project|
project.update active: false
end
Expand All @@ -229,13 +213,11 @@ class Avo::Actions::DownloadFile < Avo::BaseAction
self.name = "Download file"
self.may_download_file = true
def handle(**args)
records = args[:records]
def handle(query:, **args)
filename = "projects.csv"
report_data = []
records.each do |project|
query.each do |project|
report_data << project.generate_report_data
end
Expand Down Expand Up @@ -277,16 +259,12 @@ class Avo::Actions::KeepModalOpenAction < Avo::BaseAction
field :birthday, as: :date
end

def handle(**args)
begin
user = User.create args[:fields]
rescue => error
error "Something happened: #{error.message}"
keep_modal_open
return
end

def handle(fields:, **args)
User.create fields
succeed "All good ✌️"
rescue => error
error "Something happened: #{error.message}"
keep_modal_open
end
end
```
Expand Down Expand Up @@ -332,12 +310,12 @@ class Avo::Actions::City::PreUpdate < Avo::BaseAction
field :population, as: :boolean
end
def handle(**args)
def handle(query:, fields:, **args)
navigate_to_action Avo::Actions::City::Update,
arguments: {
cities: args[:query].map(&:id),
render_name: args[:fields][:name],
render_population: args[:fields][:population]
cities: query.map(&:id),
render_name: fields[:name],
render_population: fields[:population]
}
end
end
Expand All @@ -353,9 +331,9 @@ class Avo::Actions::City::Update < Avo::BaseAction
field :population, as: :number if arguments[:render_population]
end
def handle(**args)
def handle(fields:, **args)
City.find(arguments[:cities]).each do |city|
city.update! args[:fields]
city.update! fields
end
succeed "City updated!"
Expand Down Expand Up @@ -409,9 +387,7 @@ class Avo::Actions::DummyAction < Avo::BaseAction
self.name = "Dummy action"
self.standalone = true
def handle(**args)
fields, current_user, resource = args.values_at(:fields, :current_user, :resource)
def handle(query:, fields:, current_user:, resource:, **args)
# Do something here
succeed 'Yup'
Expand All @@ -429,9 +405,7 @@ class Avo::Actions::DummyAction < Avo::BaseAction
self.standalone = true
self.visible = -> { view == :index }
def handle(**args)
fields, current_user, resource = args.values_at(:fields, :current_user, :resource)
def handle(query:, fields:, current_user:, resource:, **args)
# Do something here
succeed 'Yup'
Expand Down Expand Up @@ -485,9 +459,9 @@ self.authorize = -> {
}
```

## Actions arguments
## Custom action arguments

Actions can have different behaviors according to their host resource. In order to achieve that, arguments must be passed like on the example below:
Actions can have different behaviors according to their host resource. In order to achieve that, arguments can receive additional arguments as follows:

```ruby{12-14}
class Avo::Resources::Fish < Avo::BaseResource
Expand Down
4 changes: 1 addition & 3 deletions docs/3.0/recipes/export-to-csv.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ class Avo::Actions::ExportCsv < Avo::BaseAction
field :created_at, as: :boolean
end

def handle(**args)
records, resource, fields = args.values_at(:records, :resource, :fields)

def handle(records:, fields:, resource:, **args)
# uncomment if you want to download all the records if none was selected
# records = resource.model_class.all if records.blank?

Expand Down
9 changes: 4 additions & 5 deletions docs/3.0/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ You can find them all [here](https://github.com/avo-hq/avo/blob/main/lib/avo/tes
Given this `Avo::Actions::ReleaseFish`, this is the `spec` that tests it.

```ruby

class Avo::Actions::ReleaseFish < Avo::BaseAction
self.name = "Release fish"
self.message = "Are you sure you want to release this fish?"
Expand All @@ -29,12 +30,10 @@ class Avo::Actions::ReleaseFish < Avo::BaseAction
field :message, as: :textarea, help: "Tell the fish something before releasing."
end

def handle(**args)
args[:records].each do |record|
record.release
end
def handle(query:, fields:, **_)
query.each(&:release)

succeed "#{args[:records].count} fish released with message '#{args[:fields][:message]}'."
succeed "#{query.count} fish released with message '#{fields[:message]}'."
end
end

Expand Down

0 comments on commit 4cbf5cc

Please sign in to comment.