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

Question: supporting object literals with unknown keys on custom filters #507

Open
juanazam opened this issue Jun 13, 2023 · 8 comments
Open

Comments

@juanazam
Copy link
Contributor

Hello!

I'm working on supporting Shopify's t filter (translation filter).

Here there is a simple a example of how it works. If we have the following translations file

/locales/en.default.json

{
  "layout": {
    "header": {
      "hello_user": "Hello {{ name }}!"
    }
  }
}

And the following liquid template:

<h1>{{ 'layout.header.hello_user' | t: name: customer.first_name }}</h1>

We would expect this example to render (assuming the first name of the customer is John):

<h1>Hello John</h1>

I'm running into the following issue when implementing this as a custom filter.
This is how I'm defining the filter's parameters:

#[derive(Debug, FilterParameters)]
struct TArgs {
    #[parameter(
        description = "Variables to be used for revaluating liquid once translation is resolved.",
    )]
    variables: Option<Expression>,
}

#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "t",
    description = "Access the desired translation for the recipient local",
    parameters(TArgs),
    parsed(TFilter)
)]
pub struct T;

#[derive(Debug, FromFilterParameters, Display_filter)]
#[name = "t"]
struct TFilter {
    #[parameters]
    args: TArgs,
}

But the problem is that when I run the same example I get the following error:

 "liquid: Unexpected named argument `name`\nfrom: Filter parsing error\n  with:\n    filter=t: name: customer.name\n"}

I haven't been able to get the filter working with object literals with arbitrary keys. What's the best way to accomplish this?

@juanazam juanazam changed the title Question: supporting object literals with unknown keys on Custom filters Question: supporting object literals with unknown keys on custom filters Jun 13, 2023
@juanazam
Copy link
Contributor Author

Hi @epage, sorry to bother you, do you have any insights on how I might be able to achieve this?
Tagging you directly because you seem to be the person with the most knowledge con this crate, thanks in advance!

@juanazam
Copy link
Contributor Author

Hi @epage, sorry to ping you directly again here, do you know if there are any easy workarounds for this issue?

@TomzBench
Copy link

TomzBench commented Aug 17, 2023

I made a custom json filter by importing serde. Since the liquid model implements Serialize/deserialize you can use it to get a serde_json::Value and loop through keys...

use liquid_core::{Display_filter, Filter, FilterReflection, ParseFilter};
use liquid_core::{Error, Result, Runtime};
use liquid_core::{Value, ValueView};

#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "json",
    description = "Convert a JSON string into a liquid object",
    parsed(JsonFilter)
)]
pub struct Json;

#[derive(Debug, Default, Display_filter)]
#[name = "json"]
pub struct JsonFilter {}
impl Filter for JsonFilter {
    fn evaluate(&self, input: &dyn ValueView, _: &dyn Runtime) -> Result<Value> {
        serde_json::from_str(&input.to_kstr().as_str()).map_err(|e| Error::with_msg(e.to_string()))
    }
}

Usage in template:

{% assign obj = '{"name": "john"}' | json %}

@juanazam
Copy link
Contributor Author

Hi @TomzBench, thanks for your reply!
I will give this a try!

@juanazam
Copy link
Contributor Author

Hey @TomzBench this won't work as I would like because of two things.

  1. The first argument of the t filter must be a string.
  2. The second argument must be a key/value pair, and not a JSON serialized object

The example I shared above captures what I need:

{{ 'layout.header.hello_user' | t: name: customer.first_name }}

@TomzBench
Copy link

per the t filter docs it does not appear that you can use templates in the local file.

If you have this JSON local file.

{
  "blog": {
    "comment": {
      "email": "Su correo electrónico"
    }
  }
}

You could do

{{ assign local = '{ "blog": { "comment": ... }' | json }}
{{ "blog.comment.email" | t : local }}

If you dont want to pass the local as an argument to the t filter then you need some other way to initialize the t filter with the local file. Shopify uses a global. so you could do it that way.

You need another scheme to parse the dot notation on the input.

@juanazam
Copy link
Contributor Author

I'm pretty sure you can, the example I shared above came from the same docs on the interpolation section (link).

Regarding the file, I'm already passing it as a global, that's not the issue.
The problem is that when defining the filter, it seems there is no way of passing a literal object as the second parameter of the filter. (the first argument is the lookup path to be used for the file).

Using liquid-rust you can either define parameters as positional or named. If you define them as named, you need to know all keywords in advance which doesn't for me, because the object literal should support any arbitrary object literal.

I haven't been able to figure out if there is a way of bypassing this restriction.

@TomzBench
Copy link

I'm pretty sure you can, the example I shared above came from the same docs on the interpolation section (link).

woops, looks like you're right

Using liquid-rust you can either define parameters as positional or named. If you define them as named, you need to know all keywords in advance which doesn't for me, because the object literal should support any arbitrary object literal.

I haven't been able to figure out if there is a way of bypassing this restriction.

I couldn't find in the docs where you can even pass named parameters. I have been restricting myself to positional and json objects when I have complex args. So if you absolutely need dynamically keyed values and can't use a json string then I think this crate can't do that is my guess. The maintainer doesn't seem available for comment though. Good luck!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants