Skip to content

Commit

Permalink
Add filterAndJoin() and classes() helpers.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 576560779
  • Loading branch information
nicholasyu-google authored and copybara-github committed Oct 25, 2023
1 parent fe76537 commit 9fd68af
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ public static ImmutableList<SoySourceFunction> functions() {
// go/keep-sorted start
new BooleanFunction(),
new CeilingFunction(),
new ClassesFunction(),
new ConcatListsFunction(),
new ConcatMapsMethod(),
new FilterAndJoinFunction(),
new FloorFunction(),
new HtmlToTextFunction(),
new IsFiniteFunction(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,4 +492,17 @@ public static boolean isFinite(SoyValue arg) {
public static boolean booleanFunc(SoyValue value) {
return value != null && value.coerceToBoolean();
}

/** Joins items with a delimiter, filtering out falsey values. */
public static String filterAndJoin(String delim, List<SoyValue> values) {
return values.stream()
.filter(SoyValue::coerceToBoolean)
.map(SoyValue::coerceToString)
.collect(joining(delim));
}

/** Joins items with a space, filtering out falsey values. */
public static String classes(List<SoyValue> values) {
return filterAndJoin(" ", values);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2023 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.template.soy.basicfunctions;

import com.google.template.soy.plugin.java.restricted.JavaPluginContext;
import com.google.template.soy.plugin.java.restricted.JavaValue;
import com.google.template.soy.plugin.java.restricted.JavaValueFactory;
import com.google.template.soy.plugin.java.restricted.SoyJavaSourceFunction;
import com.google.template.soy.plugin.javascript.restricted.JavaScriptPluginContext;
import com.google.template.soy.plugin.javascript.restricted.JavaScriptValue;
import com.google.template.soy.plugin.javascript.restricted.JavaScriptValueFactory;
import com.google.template.soy.plugin.javascript.restricted.SoyJavaScriptSourceFunction;
import com.google.template.soy.plugin.python.restricted.PythonPluginContext;
import com.google.template.soy.plugin.python.restricted.PythonValue;
import com.google.template.soy.plugin.python.restricted.PythonValueFactory;
import com.google.template.soy.plugin.python.restricted.SoyPythonSourceFunction;
import com.google.template.soy.shared.restricted.Signature;
import com.google.template.soy.shared.restricted.SoyFunctionSignature;
import com.google.template.soy.shared.restricted.SoyPureFunction;
import java.lang.reflect.Method;
import java.util.List;

/** Joins items with a space, filtering out falsey values. */
@SoyPureFunction
@SoyFunctionSignature(
name = "classes",
value =
@Signature(
parameterTypes = {"list<string | bool | null | undefined>"},
returnType = "string"))
public final class ClassesFunction
implements SoyJavaSourceFunction, SoyJavaScriptSourceFunction, SoyPythonSourceFunction {

@Override
public JavaScriptValue applyForJavaScriptSource(
JavaScriptValueFactory factory, List<JavaScriptValue> args, JavaScriptPluginContext context) {
return factory.callNamespaceFunction("soy", "soy.$$classes", args.get(0));
}

@Override
public PythonValue applyForPythonSource(
PythonValueFactory factory, List<PythonValue> args, PythonPluginContext context) {
return factory.global("runtime.classes").call(args.get(0));
}

// lazy singleton pattern, allows other backends to avoid the work.
private static final class Methods {
static final Method CLASSES_FN =
JavaValueFactory.createMethod(BasicFunctionsRuntime.class, "classes", List.class);
}

@Override
public JavaValue applyForJavaSource(
JavaValueFactory factory, List<JavaValue> args, JavaPluginContext context) {
return factory.callStaticMethod(Methods.CLASSES_FN, args.get(0));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2023 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.template.soy.basicfunctions;

import com.google.template.soy.plugin.java.restricted.JavaPluginContext;
import com.google.template.soy.plugin.java.restricted.JavaValue;
import com.google.template.soy.plugin.java.restricted.JavaValueFactory;
import com.google.template.soy.plugin.java.restricted.SoyJavaSourceFunction;
import com.google.template.soy.plugin.javascript.restricted.JavaScriptPluginContext;
import com.google.template.soy.plugin.javascript.restricted.JavaScriptValue;
import com.google.template.soy.plugin.javascript.restricted.JavaScriptValueFactory;
import com.google.template.soy.plugin.javascript.restricted.SoyJavaScriptSourceFunction;
import com.google.template.soy.plugin.python.restricted.PythonPluginContext;
import com.google.template.soy.plugin.python.restricted.PythonValue;
import com.google.template.soy.plugin.python.restricted.PythonValueFactory;
import com.google.template.soy.plugin.python.restricted.SoyPythonSourceFunction;
import com.google.template.soy.shared.restricted.Signature;
import com.google.template.soy.shared.restricted.SoyFunctionSignature;
import com.google.template.soy.shared.restricted.SoyPureFunction;
import java.lang.reflect.Method;
import java.util.List;

/** Joins items with the specified delimiter, filtering out falsey values. */
@SoyPureFunction
@SoyFunctionSignature(
name = "filterAndJoin",
value =
@Signature(
parameterTypes = {"string", "list<string | bool | null | undefined>"},
returnType = "string"))
public final class FilterAndJoinFunction
implements SoyJavaSourceFunction, SoyJavaScriptSourceFunction, SoyPythonSourceFunction {

@Override
public JavaScriptValue applyForJavaScriptSource(
JavaScriptValueFactory factory, List<JavaScriptValue> args, JavaScriptPluginContext context) {
return factory.callNamespaceFunction("soy", "soy.$$filterAndJoin", args.get(0), args.get(1));
}

@Override
public PythonValue applyForPythonSource(
PythonValueFactory factory, List<PythonValue> args, PythonPluginContext context) {
return factory.global("runtime.filter_and_join").call(args.get(0), args.get(1));
}

// lazy singleton pattern, allows other backends to avoid the work.
private static final class Methods {
static final Method FILTER_AND_JOIN_FN =
JavaValueFactory.createMethod(
BasicFunctionsRuntime.class, "filterAndJoin", String.class, List.class);
}

@Override
public JavaValue applyForJavaSource(
JavaValueFactory factory, List<JavaValue> args, JavaPluginContext context) {
return factory.callStaticMethod(Methods.FILTER_AND_JOIN_FN, args.get(0), args.get(1));
}
}
23 changes: 23 additions & 0 deletions javascript/soyutils_usegoog.js
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,27 @@ const $$insertWordBreaks = function(value, maxCharsBetweenWordBreaks) {
return result;
};

/**
* Joins items with a delimiter, filtering out falsey values.
* @param {string} delim The delimiter to join on.
* @param {...string|boolean|null|undefined} values The values to join.
* @return {string} The joined string.
*/
const $$filterAndJoin = function(delim, ...values) {
return values.filter((s) => s).join(delim);
};


/**
* Joins items with a space, filtering out falsey values.
* @param {...string|boolean|null|undefined} values The values to join.
* @return {string} The joined string.
*/
const $$classes = function(...values) {
return $$filterAndJoin(' ', ...values);
};


/**
* Conditionally concatenates two attribute values with a delimiter if they are
* both non-empty.
Expand Down Expand Up @@ -2626,6 +2647,8 @@ exports = {
$$insertWordBreaks,
$$concatAttributeValues,
$$concatCssValues,
$$classes,
$$filterAndJoin,
$$truncate,
$$listContains,
$$listIndexOf,
Expand Down
25 changes: 25 additions & 0 deletions python/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,31 @@ def soy_round(num, precision=0):
return rounded_number


def filter_and_join(delim, values):
"""Joins items with a delimiter, filtering out falsey values.
Args:
delim: The delimiter to use.
values: The values to join.
Returns:
The joined string.
"""
return delim.join([x for x in values if x])


def classes(values):
"""Joins items with a space, filtering out falsey values.
Args:
values: The values to join.
Returns:
The joined string.
"""
return filter_and_join(' ', values)


######################
# Utility functions. #
######################
Expand Down

0 comments on commit 9fd68af

Please sign in to comment.