Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pretty_generated option for code fields #3727

Open
Paul-Bob opened this issue Mar 7, 2025 · 4 comments
Open

Add pretty_generated option for code fields #3727

Paul-Bob opened this issue Mar 7, 2025 · 4 comments
Labels
😎 Cool Developer Experience (DX) Good first issue Good for newcomers Help wanted We could use some help with this

Comments

@Paul-Bob
Copy link
Contributor

Paul-Bob commented Mar 7, 2025

Context

Currently, it’s a common pattern to use format_using alongside update_using to render a prettified version of a code field's content while ensuring valid parsing on update. A typical example involves formatting JSON fields like so:

field :body, as: :code,
  format_using: -> {
    JSON.pretty_generate(JSON.parse(value.to_json))
  },
  update_using: -> {
    JSON.parse(value)
  }

This approach, while effective, introduces redundancy and boilerplate code. To streamline this, we propose adding a first-party pretty_generated option, allowing developers to enable this behavior with a simple flag:

field :body, as: :code, pretty_generated: true
@Paul-Bob Paul-Bob added this to Issues Mar 7, 2025
@Paul-Bob Paul-Bob moved this to Triage in Issues Mar 7, 2025
@Paul-Bob Paul-Bob moved this from Triage to To Do in Issues Mar 7, 2025
@Paul-Bob Paul-Bob added Help wanted We could use some help with this Good first issue Good for newcomers Developer Experience (DX) 😎 Cool labels Mar 7, 2025
@ObiWanKeoni
Copy link
Contributor

Hi @Paul-Bob 👋
Can I suggest a new JSON field that inherits from code_field and has this option?

Thought being that when we render things like custom_css, pretty_generated would have no (or perhaps disastrous) effects when attempting to pretty generate a json value. I think having a json field would also allow us to infer the language as javascript for syntax highlighting and have conditional rendering if, by chance, an app used a text field instead of JSONB and wanted to render it as pretty JSON.

TL;DR instead of:

field :body, as: :code, pretty_generated: true

maybe

field :body, as: :json, pretty_generated: true

What do you think?

@Paul-Bob
Copy link
Contributor Author

Hi @ObiWanKeoni

Can I suggest a new JSON field that inherits from code_field and has this option?

Suggestions and feedback are always welcome!

Thought being that when we render things like custom_css, pretty_generated would have no (or perhaps disastrous) effects when attempting to pretty generate a json value.

You're right. This option needs to be well-documented in terms of when to use it. It's intended for a specific use case, and the documentation should explicitly clarify that. To be honest, format_using and update_using already provide great flexibility, while pretty_generated is meant to be an option to reduce boilerplate code in a specific, common scenario.

I think having a json field would also allow us to infer the language as javascript for syntax highlighting and have conditional rendering if, by chance, an app used a text field instead of JSONB and wanted to render it as pretty JSON.

Have you encountered such use cases before? Could you elaborate on the scenario?

Thank you!

@ObiWanKeoni
Copy link
Contributor

Have you encountered such use cases before? Could you elaborate on the scenario?

Absolutely, I encounter this regularly. I have JSON fields all over my applications where this pattern would be extremely useful. Currently I'm implementing this boilerplate in numerous places:

field :input, as: :code, stacked: true, theme: 'material-darker', language: 'javascript',
                   format_using: -> { value.is_a?(Hash) ? JSON.pretty_generate(value) : value },
                   update_using: -> { value.is_a?(String) ? JSON.parse(value) : value }

The use case is very common with APIs and data processing applications. We receive JSON data (often as a Hash), need to display it pretty-printed in the UI for readability, but then parse it back when saving - nothing you're unfamiliar with I'm sure.

This pattern repeats so much that I've considered creating a custom field type just for this purpose especially since changing some of this data across json fields is tedious (even if it's a trivial find/replace). But I believe having it upstream would benefit many developers beyond my organization.

The proposed :json field would:

  1. Default to JavaScript highlighting
  2. Handle pretty printing automatically (or with an option)
  3. Manage conversions between strings and Hash objects
  4. Provide a cleaner, more semantic API for what is fundamentally a JSON-specific operation

The last item I think is the main point I'm making - if we are going to add options for JSON-specific representations, we might benefit from extracting that into a separate field since JSON carries different semantics than a base code representation anyway.

Ultimately though, I'm comfortable implementing my own version of this if necessary

Thanks @Paul-Bob - as always, really appreciate the work you are doing here. Love the product

@Paul-Bob
Copy link
Contributor Author

I understand the suggested direction.

We intend to keep the :json field to implement it with an advanced JSON editor.

Thank you for the context.

Default to JavaScript highlighting

The code field already defaults to javascript (source code)

I'm comfortable implementing my own version of this if necessary

A simple way to implement this on your end is to override the field method when as is set to :json. You can achieve this by ejecting Avo::BaseResource and modifying its behavior accordingly.

Here's a suggested approach:

Step 1: Create a Custom BaseResource

Generate the following file:

# app/avo/base_resource.rb
module Avo
  class BaseResource < Avo::Resources::Base
    def field(id, as:, **kwargs)
      return super if as != :json

      super id,
        as: :code,
        stacked: true,
        theme: 'material-darker',
        format_using: -> { value.is_a?(Hash) ? JSON.pretty_generate(value) : value },
        update_using: -> { value.is_a?(String) ? JSON.parse(value) : value },
        **kwargs
    end
  end
end

Step 2: Use the Custom Field in Your Resource

Now, you can use this custom behavior in your resource:

# app/avo/resources/city.rb
class Avo::Resources::City < Avo::BaseResource
  def fields
    field :input, as: :json
  end
end

Thanks @Paul-Bob - as always, really appreciate the work you are doing here. Love the product

Thank you for being an awesome community member!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
😎 Cool Developer Experience (DX) Good first issue Good for newcomers Help wanted We could use some help with this
Projects
Status: To Do
Development

No branches or pull requests

2 participants