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

Quoted strings as tag arguments have their quotes escaped and preserved #235

Open
unitof opened this issue Jul 6, 2020 · 11 comments
Open

Comments

@unitof
Copy link

unitof commented Jul 6, 2020

Is it possible to have a quoted string as an argument to a tag, without the surrounding quotes being preserved in the output?

Selmer, as expected, does treat the quoted string as a single argument, but passes the string to the tag function with its quotes, escaped, as part of the string.

Or perhaps there's another notation which allows a string with spaces to be treated as a single argument?

(use 'selmer.parser)

(add-tag! :foo
  (fn [args context-map]
    (str "foo " (first args))))

(render "{% foo \"quux quuz\" %}" {})
=>"foo \"quux quuz\""

Using render-file removes the need to escape the quotes in the template file, but still introduces them in the output:

file.txt:

{% foo "quux quuz" %}
(render-file "file.txt" {})
=>"foo \"quux quuz\""

The {% safe %} tag has no discernible effect:

file.txt:

{% safe %}{% foo "quux quuz" %}{% endsafe %}
(render-file "file.txt" {})
=>"foo \"quux quuz\""
@unitof
Copy link
Author

unitof commented Jul 6, 2020

I took a look at the built-in cycle tag to see if it handles quotes strings any differently, but it has the same behavior.

In other words, I don't think it's currently possible to write a {% cycle %} tag that cycles between strings that include spaces, without literal "s being included in the output.

(render "{% for i in items %}This is {% cycle \"an apple\" \"a banana\" %}. {% endfor %}" {:items (range 5)})

=>

"This is \"an apple\". This is \"a banana\". This is \"an apple\". This is \"a banana\". This is \"an apple\". "

Removing the literal quotes formats the output as desired, but—of course—treats the strings as four separate arguments:

(render "{% for i in items %}This is {% cycle an apple a banana %}. {% endfor %}" {:items (range 5)})

=>

"This is an. This is apple. This is a. This is banana. This is an. "

My desired output (in this specific example) is:

"This is an apple. This is a banana. This is an apple. This is a banana. This is an apple. "

Is there any tag syntax workaround to achieve that?

Note that this behavior also applies to tag-content within block-style tags, which is actually where it's most relevant to my current work.

I definitely could add a (clojure.string/replace tag-content #"\"" "") to my custom tag function, but that feels a bit icky.

@yogthos
Copy link
Owner

yogthos commented Jul 6, 2020

Yeah, the best option at the moment is to do replace in the custom tag. I'd be open to adding support for handling quoted strings, but I probably won't be able to get around to it in the near future. If you'd be ok doing a PR for this, I could certainly help with that.

@unitof
Copy link
Author

unitof commented Jul 6, 2020

Alright, I'll see if I have time to learn how to approach this! But for now will do the replace workaround.

Would you prefer that this change the default behavior (quotes always get removed, but that technically would break backward compatibility), or be a new setting on the parser, like (parser strip-quotes-on!)?

Also would need to figure out how it could support quotes if they are desired in output strings. Probably via a double-escaping like \\\".

@yogthos
Copy link
Owner

yogthos commented Jul 6, 2020

I think it would be best to avoid breaking changes to make upgrading smoother, so escaping would be preferable.

@boxxxie
Copy link
Contributor

boxxxie commented Jul 7, 2022

the render function could preserve the old behavior via and options field, or add the behavior via options.

@yogthos
Copy link
Owner

yogthos commented Jul 7, 2022

Yeah, adding flags to hint behavior would be a reasonable approach here.

@unitof
Copy link
Author

unitof commented Jul 7, 2022

Obviously I didn't get to this—sorry! Still eager to try, but I'm very very new to Clojure so would take me a while and I may need help. Feel free to take it on someone else before I do!

@yogthos
Copy link
Owner

yogthos commented Jul 7, 2022

Definitely no rush, and if you do get around to it then I'm happy to help with a review.

@seancorfield
Copy link
Collaborator

We rely on this behavior explicitly at work, with a custom tag we've added that takes arguments generated by a (JS) preprocessor. We check in the tag if the argument starts and ends with " and if so, we attempt to read it as EDN to convert it to a plain string (and if that fails, we leave it as-is). We also use that as a hint that the argument string should then be render'd -- and we wouldn't want to call render on all (string) arguments.

Which is a long-winded way of saying "please don't change the default behavior but make this possible via some option to the render functions".

@didibus
Copy link
Contributor

didibus commented Jul 6, 2023

PR: #304 adds resolve-arg which will return the literal without the double quotes. If you don't want to also render the arg if a template, you can just use (parse-literal arg) from selmer.filter-parser. parse-literal will remove the double quotes if any.

@yogthos
Copy link
Owner

yogthos commented Jul 6, 2023

And new release 1.12.59 is up on Clojars with the feature.

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

No branches or pull requests

5 participants