Skip to content

Commit

Permalink
Merge pull request #96 from avo-hq/feature/helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianthedev authored Aug 27, 2023
2 parents 2310d1c + 9112fe9 commit 778e0cb
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 93 deletions.
7 changes: 6 additions & 1 deletion docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ const config = {
['meta', { name:"msapplication-config", content:"/favicons/browserconfig.xml" }],
['meta', { name:"theme-color", content:"#ffffff" }],
],
rewrites: {
"/3.0/evaluation-host": "/3.0/execution-context"
},
themeConfig: {
siteTitle: false,
logo: "/logo.svg",
Expand Down Expand Up @@ -188,7 +191,6 @@ const config = {
{text: "Custom fields", link: "/3.0/custom-fields"},
{text: "Resource tools", link: "/3.0/resource-tools"},
{text: "Stimulus JS integration", link: "/3.0/stimulus-integration"},
// {text: "Evaluation hosts", link: "/3.0/evaluation-hosts"},
{text: "Custom asset pipeline", link: "/3.0/custom-asset-pipeline"},
],
},
Expand All @@ -204,7 +206,10 @@ const config = {
text: "Internals",
items: [
{text: "Testing", link: "/3.0/testing"},
{text: "Avo::Current", link: "/3.0/avo-current"},
{text: "Avo::ExecutionContext", link: "/3.0/execution-context"},
{text: "Avo::ApplicationController", link: "/3.0/avo-application-controller"},
// {text: "Application Helpers", link: "/3.0/helpers"},
{text: "Avo.asset_manager", link: "/3.0/asset-manager"},
],
},
Expand Down
34 changes: 34 additions & 0 deletions docs/3.0/avo-current.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# `Avo::Current`

`Avo::Current` is based on the `Current` pattern Rails exposes using [`ActiveSupport/CurrentAttributes`](https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html).

On each request Avo will set some values on it.

:::option `user`
This is what will be returned by the [`current_user_method`](./authentication.html#customize-the-current-user-method) that you've set in your initializer.
:::

:::option `params`
Equivalent of `request.params`.
:::

:::option `request`
The Rails `request`.
:::

:::option `context`
The [`context`](./customization.html#context) that you configured in your initializer evaluated in `Avo::ApplicationController`.
:::

:::option `view_context`
An instance of [`ActionView::Rendering`](https://api.rubyonrails.org/classes/ActionView/Rendering.html#method-i-view_context) off of which you can run any methods or variables that are available in your partials.

```ruby
view_context.link_to "Avo", "https://avohq.io"
```
:::

:::option `locale`
The `locale` of the app.
:::

2 changes: 1 addition & 1 deletion docs/3.0/common/associations_attach_scope_option_common.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ field :user,
attach_scope: -> { query.non_admins }
```

Pass in a block where you attach scopes to the `query` object. The block is executed in the [`AssociationScopeHost`](./../evaluation-hosts.html#associationscopehost), so follow the docs to see what variables you have access to.
Pass in a block where you attach scopes to the `query` object. The block is executed in the [`ExecutionContext`](./../execution-context).
:::
2 changes: 1 addition & 1 deletion docs/3.0/common/associations_scope_option_common.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ field :user,
scope: -> { query.approved }
```

Pass in a block where you attach scopes to the `query` object. The block gets executed in the [`AssociationScopeHost`](./../evaluation-hosts.html#associationscopehost), so follow the docs to see what variables you have access to.
Pass in a block where you attach scopes to the `query` object. The block gets executed in the [`ExecutionContext`](./../execution-context).
:::
89 changes: 0 additions & 89 deletions docs/3.0/evaluation-hosts.md

This file was deleted.

109 changes: 109 additions & 0 deletions docs/3.0/execution-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Execution context

Avo enables developers to hook into different points of the application lifecycle using blocks.
That functionality can't always be performed in void but requires some pieces of state to set up some context.

Computed fields are one example.

```ruby
field :full_name, as: :text do
"#{record.first_name} #{record.last_name}"
end
```

In that block we need to pass the `record` so you can compile that value. We send more information than just the `record`, we pass on the `resource`, `view`, `view_context`, `request`, `current_user` and more depending on the block that's being run.

## How does the `ExecutionContext` work?

The `ExecutionContext` is an object that holds some pieces of state on which we execute a lambda function.

```ruby
module Avo
class ExecutionContext

attr_accessor :target, :context, :params, :view_context, :current_user, :request

def initialize(**args)
# If target don't respond to call, handle will return target
# In that case we don't need to initialize the others attr_accessors
return unless (@target = args[:target]).respond_to? :call

args.except(:target).each do |key,value|
singleton_class.class_eval { attr_accessor "#{key}" }
instance_variable_set("@#{key}", value)
end

# Set defaults on not initialized accessors
@context ||= Avo::Current.context
@params ||= Avo::Current.params
@view_context ||= Avo::Current.view_context
@current_user ||= Avo::Current.current_user
@request ||= Avo::Current.request
end

delegate :authorize, to: Avo::Services::AuthorizationService

# Return target if target is not callable, otherwise, execute target on this instance context
def handle
target.respond_to?(:call) ? instance_exec(&target) : target
end
end
end

# Use it like so.
SOME_BLOCK = -> {
"#{record.first_name} #{record.last_name}"
}

Avo::ExecutionContext.new(target: &SOME_BLOCK, record: User.first).handle
```

This means you could throw any type of object at it and it it responds to a `call` method wil will be called with all those objects.

:::option `target`
The block you'll pass to be evaluated. It may be anything but will only be evaluated if it responds to a `call` method.
:::

:::option `context`
Aliased to [`Avo::Current.context`](./avo-current#context).
:::

:::option `current_user`
Aliased to [`Avo::Current.user`](./avo-current#user).
:::

:::option `view_context`
Aliased to [`Avo::Current.view_context`](./avo-current#view_context).
:::

:::option `request`
Aliased to [`Avo::Current.request`](./avo-current#request).
:::

:::option `params`
Aliased to [`Avo::Current.params`](./avo-current#params).
:::

:::option Custom variables
You can pass any variable to the `ExecutionContext` and it will be available in that block.
This is how we can expose `view`, `record`, and `resource` in the computed field example.

```ruby
Avo::ExecutionContext.new(target: &SOME_BLOCK, record: User.first, view: :index, resource: resource).handle
```
:::

:::option `helpers`
Within the `ExecutionContext` you might want to use some of your already defined helpers. You can do that using the `helpers` object.

```ruby
# products_helper.rb
class ProductsHelper
# Strips the "CODE_" prefix from the name
def simple_name(name)
name.gsub "CODE_", ""
end
end

field :name, as: :text, format_using: -> { helpers.simple_name(value) }
:::
5 changes: 5 additions & 0 deletions docs/3.0/field-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ field :is_featured, as: :boolean, visible: -> { resource.record.published_at.pre
On form submissions, the `visible` block is evaluated in the `create` and `update` controller actions. That's why you have to check if the `resource.record` object is present before trying to use it.
:::

[Check how to use your application's helpers within any field context.](./helpers)


```ruby
# `resource.record` is nil when submitting the form on resource creation
field :name, as: :text, visible -> { resource.record.enabled? }
Expand All @@ -55,6 +58,8 @@ field :name, as: :text, visible -> { resource.record&.enabled? }

You might need to show a field with a value you don't have in a database row. In that case, you may compute the value using a block that receives the `record` (the actual database record), the `resource` (the configured Avo resource), and the current `view`. With that information, you can compute what to show on the field in the <Index /> and <Show /> views.

[Check how to use your application's helpers within any computed field context.](./helpers)

```ruby
field 'Has posts', as: :boolean do
record.posts.present?
Expand Down
2 changes: 2 additions & 0 deletions docs/3.0/fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Through fields you tell Avo what to fetch from the database and how to display i

Avo ships with various simple fields like `text`, `textarea`, `number`, `password`, `boolean`, `select`, and more complex ones like `markdown`, `key_value`, `trix`, `tags`, and `code`.

[Check how to use your application's helpers within any field context.](./helpers)

## Declaring fields

You add fields to a resource through the `fields` method using the `field DATABASE_COLUMN, as: FIELD_TYPE, **FIELD_OPTIONS` notation.
Expand Down
2 changes: 1 addition & 1 deletion docs/3.0/fields/tags.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ end

The `suggestions` option can be an array of strings, an object with the keys `value`, `label`, and (optionally) `avatar`, or a lambda that returns an array of that type of object.

The lambda is run inside a [`RecordHost`](./../evaluation-hosts.html#recordhost), so it has access to the `record` along with other things (check the link).
The lambda is run inside a [`ExecutionContext`](./../execution-context.html), so it has access to the `record`, `resource`, `request`, `params`, `view`, and `view_context` along with other things.

```ruby{5-21}
# app/models/post.rb
Expand Down
2 changes: 2 additions & 0 deletions docs/3.0/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Each `Resource` maps out one of your models. There can be multiple `Resource`s a

All resources are located in the `app/avo/resources` directory.

[Check how to use your application's helpers within any resource context.](./helpers)

## Resources from model generation

```bash
Expand Down

0 comments on commit 778e0cb

Please sign in to comment.