# app/avo/resources/post.rb
class Avo::Resources::Post < Avo::BaseResource
  def fields
    tool Avo::ResourceTools::PostInfo, show_on: :edit
  end
end
```

### Using path helpers

Because you're in a Rails engine, you will have to prepend the engine object to the path.

#### For Avo paths

Instead of writing `resources_posts_path(1)` you have to write `avo.resources_posts_path(1)`.

#### For the main app paths

When you want to reference paths from your main app, instead of writing `posts_path(1)`, you have to write `main_app.posts_path`.

## Add custom fields on forms

**From Avo 2.12**

You might want to add a few more fields or pieces of functionality besides the CRUD-generated fields on your forms. Of course, you can already create new custom fields to do it in a more structured way, but you can also use a resource tool to achieve more custom behavior.

You have access to the `form` object that is available on the new/edit pages on which you can attach inputs of your choosing. You can even achieve nested form functionality.

You have to follow three steps to enable this functionality:

1. Add the inputs in a resource tool and enable the tool on the form pages
2. Tell Avo which `params` it should permit to write to the model
3. Make sure the model is equipped to receive the params

In the example below, we'll use the `Avo::Resources::Fish`, add a few input fields (they will be a bit unstyled because this is not the scope of the exercise), and do some actions with some of them.

We first need to generate the tool with `bin/rails g avo:resource_tool fish_information` and add the tool to the resource file.

```ruby{3}
class Avo::ResourcesFish < Avo::BaseResource
  def fields
    tool Avo::ResourceTools::FishInformation, show_on: :forms
  end
end
```

In the `_fish_information.html.erb` partial, we'll add a few input fields. Some are directly on the `form`, and some are nested with `form.fields_for`.

The fields are:

- `fish_type` as a text input
- `properties` as a multiple text input which will produce an array in the back-end
- `information` as nested inputs which will produce a `Hash` in the back-end

```erb{13-36}

<div class="flex flex-col">
  <%= render Avo::PanelComponent.new(title: @resource.model.name) do |c| %>
    <% c.with_tools do %>
      <%= a_link('/admin', icon: 'heroicons/solid/academic-cap', style: :primary) do %>
        Primary
      <% end %>
    <% end %>

    <% c.with_body do %>
      <div class="flex flex-col p-4 min-h-24">
        <div class="space-y-4">
          <% if form.present? %>
            <%= form.label :fish_type %>
            <%= form.text_field :fish_type, value: 'default type of fish', class: input_classes %>
            <br>

            <%= form.label :properties %>
            <%= form.text_field :properties, multiple: true, value: 'property 1', class: input_classes %>
            <%= form.text_field :properties, multiple: true, value: 'property 2', class: input_classes %>
            <br>

            <% form.fields_for :information do |information_form| %>
              <%= form.label :information_name %>
              <%= information_form.text_field :name, value: 'information name', class: input_classes %>
              <div class="text-gray-600 mt-2 text-sm">This is going to be passed to the model</div>
              <br>
              <%= form.label :information_history %>
              <%= information_form.text_field :history, value: 'information history', class: input_classes %>
              <div class="text-gray-600 mt-2 text-sm">This is going to be passed to the model</div>
              <br>
              <%= form.label :information_age %>
              <%= information_form.text_field :age, value: 'information age', class: input_classes %>
              <div class="text-gray-600 mt-2 text-sm">This is NOT going to be passed to the model</div>
            <% end %>
          <% end %>
        </div>
      </div>
    <% end %>
  <% end %>
</div>
```

Next, we need to tell Avo and Rails which params are welcomed in the `create`/`update` request. We do that using the `extra_params` option on the `Avo::Resources::Fish`. Avo's internal implementation is to assign the attributes you specify here to the underlying model (`model.assign_attributes params.permit(extra_params)`).

```ruby{2}
class Avo::Resources::Fish < Avo::BaseResource
  self.extra_params = [:fish_type, :something_else, properties: [], information: [:name, :history]]

  def fields
    tool Avo::ResourceTools::FishInformation, show_on: :forms
  end
end
```

The third step is optional. You must ensure your model responds to the params you're sending. Our example should have the `fish_type`, `properties`, and `information` attributes or setter methods on the model class. We chose to add setters to demonstrate the params are called to the model.

```ruby
class Fish < ApplicationRecord
  self.inheritance_column = nil # required in order to use the type DB attribute

  def fish_type=(value)
    self.type = value
  end

  def properties=(value)
    # properties should be an array
    puts ["properties in the Fish model->", value].inspect
  end

  def information=(value)
    # properties should be a hash
    puts ["information in the Fish model->", value].inspect
  end
end
```

If you run this code, you'll notice that the `information.information_age` param will not reach the `information=` method because we haven't allowed it in the `extra_params` option.

## Where to add logic

It's a good practice not to keep logic in view files (partials).
You can hide that logic inside the tool using instance variables and methods, and access it in the partial using the `tool` variable.

[Here's an example](https://github.com/avo-hq/main.avodemo.com/commit/c8ecb9b53a770103a993df4c2b3acec0a1faf737) on how you could do that.

```ruby{8,10}
class Avo::ResourceTools::PostInfo < Avo::BaseResourceTool
  self.name = "Post info"
  # self.partial = "avo/resource_tools/post_info"

  attr_reader :foo

  def initialize(**kwargs)
    super **kwargs # It's important to call super with the same keyword arguments

    # You'll have access to the following objects:
    # resource - when attached to a resource
    # parent - which is the object it's attached to (resource if attached to a resource)
    # view

    @foo = :bar # Add your variables
  end

  def custom_method_call
    :called
  end
end
```

```erb{7,12}
<div class="flex flex-col">
  <%= render Avo::PanelComponent.new title: "Post info" do |c| %>

    <% c.with_body do %>
      <p>
        This variable was declared in the initializer:
        <%= tool.foo %>
      </p>

      <p>
        This is a method called on the tool:
        <%= tool.custom_method_call %>
      </p>
    <% end %>
  <% end %>
</div>
```

---