public static FluidValue Truncate(FluidValue input, FilterArguments arguments, TemplateContext context) { if (input.IsNil()) { return(StringValue.Empty); } var inputStr = input.ToStringValue(); if (inputStr == null) { return(StringValue.Empty); } var ellipsisStr = arguments.At(1).Or(Ellipsis).ToStringValue(); var length = Convert.ToInt32(arguments.At(0).Or(NumberValue.Create(50)).ToNumberValue()); var l = Math.Max(0, length - ellipsisStr.Length); return(inputStr.Length > length ? new StringValue(inputStr.Substring(0, l) + ellipsisStr) : input ); }
public override ValueTask <Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) { // We prefix the identifier to prevent collisions with variables. // Variable identifiers don't represent the same slots as inc/dec ones. // c.f. https://shopify.github.io/liquid/tags/variable/ var prefixedIdentifier = Prefix + Identifier; var value = context.GetValue(prefixedIdentifier); if (value.IsNil()) { value = NumberValue.Zero; } else { value = NumberValue.Create(value.ToNumberValue() + 1); } context.SetValue(prefixedIdentifier, value); value.WriteTo(writer, encoder, context.CultureInfo); return(Normal); }
// Step to next token, taking action as we go internal void NextToken() { while (_tokenindex < _tokenlist.Count - 1) { ++_tokenindex; var token = _tokenlist[_tokenindex]; if (token.TokenType == TokenTypes.LINE) { _symbols.Find("$lineno$").Value = NumberValue.Create(token.LineNumber); if (Logger.Level > 1 || (Logger.Level == 1 && token.SourcePath == _tokenlist[0].SourcePath)) { Logger.WriteLine("{0,3}: {1}", token.LineNumber, token.Value); } } else if (token.TokenType == TokenTypes.Directive) { Directive(token); } else if (token.TokenType == TokenTypes.Bad) { ErrLexer(token.LineNumber, "bad token '{0}'", token.Value); } else { break; } } Logger.Assert(!_tokenlist[_tokenindex].IsWhite); //Logger.WriteLine(6, "Token=<{0}> <{1}>", _tokenlist[_tokenindex], CurrentSymbol); }
// Step to next token, taking action as we go void MoveNext() { if (_stop) { _tokenindex = _tokenlist.Count - 1; } while (_tokenindex < _tokenlist.Count - 1) { ++_tokenindex; var token = _tokenlist[_tokenindex]; if (token.TokenType == TokenTypes.LINE) { _symbols.Find("$lineno$").Value = NumberValue.Create(token.LineNumber); if (Logger.Level > 0) { Logger.WriteLine("{0,3}: {1}", token.LineNumber, token.Value); } } else if (token.TokenType == TokenTypes.Directive) { Directive(token); } else if (token.TokenType == TokenTypes.Bad) { ErrLexer(token.LineNumber, "bad token '{0}'", token.Value); } else { break; } } Logger.Assert(!_tokenlist[_tokenindex].IsWhite); Logger.WriteLine(6, "Token=<{0}> <{1}>", _tokenlist[_tokenindex], CurrentSymbol); }
public Task Handle(EvaluatingLiquidExpression notification, CancellationToken cancellationToken) { var context = notification.TemplateContext; var options = context.Options; options.Scope.SetValue("Request", new ObjectValue(new LiquidRequestAccessor())); options.MemberAccessStrategy.Register <LiquidRequestAccessor, FluidValue>((_, name, _) => { var request = _httpContextAccessor.HttpContext?.Request; if (request == null) { return(NilValue.Instance); } return(name switch { nameof(HttpRequest.QueryString) => new StringValue(request.QueryString.Value), nameof(HttpRequest.ContentType) => new StringValue(request.ContentType), nameof(HttpRequest.ContentLength) => NumberValue.Create(request.ContentLength ?? 0), nameof(HttpRequest.Form) => request.HasFormContentType ? new ObjectValue(request.Form) : NilValue.Instance, nameof(HttpRequest.Protocol) => new StringValue(request.Protocol), nameof(HttpRequest.Path) => new StringValue(request.Path.Value), nameof(HttpRequest.PathBase) => new StringValue(request.PathBase.Value), nameof(HttpRequest.Host) => new StringValue(request.Host.Value), nameof(HttpRequest.IsHttps) => BooleanValue.Create(request.IsHttps), nameof(HttpRequest.Scheme) => new StringValue(request.Scheme), nameof(HttpRequest.Method) => new StringValue(request.Method), _ => NilValue.Instance }); });
public async Task ShouldEvaluateFilters(string source, string expected) { FluidTemplate.TryParse(source, out var template, out var messages); var context = new TemplateContext(); context.Filters.AddFilter("inc", (i, args, ctx) => { var increment = 1; if (args.Count > 0) { increment = (int)args.At(0).ToNumberValue(); } return(NumberValue.Create(i.ToNumberValue() + increment)); }); context.Filters.AddFilter("append", (i, args, ctx) => { var s = i.ToStringValue(); for (var k = 0; k < args.Count; k++) { s += args.At(k).ToStringValue(); } return(new StringValue(s)); }); var result = await template.RenderAsync(context); Assert.Equal(expected, result); }
public async Task ShouldUseCurrentContext() { var e = new ForStatement( new List <Statement> { new AssignStatement("z", new LiteralExpression(NumberValue.Create(1))) }, "i", new RangeExpression( new LiteralExpression(NumberValue.Create(1)), new LiteralExpression(NumberValue.Create(3)) ), null, null, false ); var t = new TemplateContext(); t.SetValue("z", 0); Assert.Equal(0, t.GetValue("z").ToNumberValue()); var sw = new StringWriter(); await e.WriteToAsync(sw, HtmlEncoder.Default, t); Assert.Equal(1, t.GetValue("z").ToNumberValue()); }
public void NumbersEquality() { var num1 = NumberValue.Create(12.5); var num2 = NumberValue.Create(12.5); var num3 = NumberValue.Create(7); var num4 = NumberValue.Create(0); Assert.Equal(num1, num2); Assert.True(num1.Equals(num2)); Assert.NotEqual(num1, num3); Assert.Equal(0, num4.AsNumber()); Assert.Equal(7, num3.AsNumber()); Assert.True(num1.AsBoolean()); Assert.True(num2.AsBoolean()); Assert.True(num3.AsBoolean()); Assert.False(num4.AsBoolean()); Assert.True(num1.CompareTo(num2) == 0); Assert.True(num1.CompareTo(num3) > 0); Assert.True(num4.CompareTo(num3) < 0); Assert.Equal("12.5", num1.AsString()); Assert.Throws <RuntimeException>(() => num1.AsDate()); Assert.Throws <RuntimeException>(() => num1.AsObject()); }
// Output a call according to the symbol type public void OutCall(Symbol symbol, int varargs = 0, CallInfo callinfo = null) { var ci = callinfo ?? symbol.CallInfo; switch (symbol.CallKind) { case CallKinds.FUNC: Logger.Assert(varargs == 0); OutCall(Opcodes.CALL, ci.Name, ci.NumArgs, 0); break; case CallKinds.VFUNC: OutCall(Opcodes.CALLV, ci.Name, ci.NumArgs, varargs); break; case CallKinds.VFUNCT: OutCall(Opcodes.CALLVT, ci.Name, ci.NumArgs, varargs); break; case CallKinds.JFUNC: OutLoad(NumberValue.Create((int)symbol.JoinOp)); OutCall(Opcodes.CALL, ci.Name, ci.NumArgs, varargs); break; case CallKinds.LFUNC: Out(Opcodes.LDLOOKUP); OutCall(Opcodes.CALL, ci.Name, ci.NumArgs, varargs); break; default: throw Logger.Fatal(symbol.CallKind); } }
public async Task ForEvaluatesMemberOptionsOffsetOnly() { var context = new TemplateContext() .SetValue("offset", 3) ; var e = new ForStatement( new List <Statement> { CreateMemberStatement("i") }, "i", new RangeExpression( new LiteralExpression(NumberValue.Create(1)), new LiteralExpression(NumberValue.Create(5)) ), null, new MemberExpression(new IdentifierSegment("offset")), true ); var sw = new StringWriter(); await e.WriteToAsync(sw, HtmlEncoder.Default, context); Assert.Equal("54", sw.ToString()); }
public void Number_ToString_IsStringRepresentationOfNumber() { var value = NumberValue.Create(-13.536M) as IValue; var result = value.ImplicitConversionToStringValue(); Assert.AreEqual("-13.536", result.Value); }
public async Task NegativeLimitShouldStripFromEnd() { var context = new TemplateContext() .SetValue("limit", -3) ; var e = new ForStatement( new List <Statement> { CreateMemberStatement("i") }, "i", new RangeExpression( new LiteralExpression(NumberValue.Create(1)), new LiteralExpression(NumberValue.Create(9)) ), new MemberExpression(new IdentifierSegment("limit")), offset: null, false ); var sw = new StringWriter(); await e.WriteToAsync(sw, HtmlEncoder.Default, context); Assert.Equal("123456", sw.ToString()); }
public void Number_ToNumber_IsSameValue() { var value = NumberValue.Create(13) as IValue; var result = value.ImplicitConversionToNumberValue(); Assert.AreEqual(13, result.Value); }
public void NumberNonZero_ToBool_IsTrue() { var value = NumberValue.Create(-0.1M) as IValue; var result = value.ImplicitConversionToBoolValue(); Assert.AreEqual(true, result.Value); }
public void NumberZero_ToBool_IsFalse() { var value = NumberValue.Create(0) as IValue; var result = value.ImplicitConversionToBoolValue(); Assert.AreEqual(false, result.Value); }
/// <summary> /// Redefines the True/False keywords to be case-insensitive. /// </summary> private void DefineLavaTrueFalseAsCaseInsensitive() { // To redefine the True and False parsers, we need to rebuild the Fluid Primary expression parser. // Fluid defines a Primary expression as: primary => NUMBER | STRING | BOOLEAN | MEMBER // Reproduce the standard Fluid parsers that are internally defined by the default parser. var indexer = Between(LBracket, Primary, RBracket).Then <MemberSegment>(x => new IndexerSegment(x)); var member = Identifier.Then <MemberSegment>(x => new IdentifierSegment(x)).And( ZeroOrMany( Dot.SkipAnd(Identifier.Then <MemberSegment>(x => new IdentifierSegment(x))) .Or(indexer))) .Then(x => { x.Item2.Insert(0, x.Item1); return(new MemberExpression(x.Item2)); }); // Redefine the Fluid keywords "true" and "false" as case-insensitive. var trueParser = Terms.Text("true", caseInsensitive: true); var falseParser = Terms.Text("false", caseInsensitive: true); // Replace the default definition of the Fluid Primary parser. Primary.Parser = Number.Then <Expression>(x => new LiteralExpression(NumberValue.Create(x))) .Or(String.Then <Expression>(x => new LiteralExpression(StringValue.Create(x.ToString())))) .Or(trueParser.Then <Expression>(x => new LiteralExpression(BooleanValue.True))) .Or(falseParser.Then <Expression>(x => new LiteralExpression(BooleanValue.False))) .Or(member.Then <Expression>(x => x)); }
public IValue Evaluate() { if (Operator == "+") { return(Operand.Evaluate().ImplicitConversionToNumberValue()); } if (Operator == "-") { return(NumberValue.Create(-Operand.Evaluate().ImplicitConversionToNumberValue().Value)); } if (Operator == "~") { return(NumberValue.Create(~Operand.Evaluate().ImplicitConversionToNumberValue().BitwiseValue)); } if (Operator == "!") { return(BoolValue.Create(!Operand.Evaluate().ImplicitConversionToBoolValue().Value)); } if (Operator == "++") { return(NumberValue.Create(Operand.Evaluate().ImplicitConversionToNumberValue().Value + 1)); } if (Operator == "--") { return(NumberValue.Create(Operand.Evaluate().ImplicitConversionToNumberValue().Value - 1)); } throw new Exception("Internal error: no implementation for operator '" + Operator + "'"); // TODO: different exception type? }
public override void ConfigureServices(IServiceCollection services) { services.Configure <TemplateOptions>(o => { o.Scope.SetValue("Site", new ObjectValue(new LiquidSiteSettingsAccessor())); o.MemberAccessStrategy.Register <LiquidSiteSettingsAccessor, FluidValue>(async(obj, name, context) => { var liquidTemplateContext = (LiquidTemplateContext)context; var siteService = liquidTemplateContext.Services.GetRequiredService <ISiteService>(); var site = await siteService.GetSiteSettingsAsync(); FluidValue result = name switch { nameof(ISite.SiteName) => new StringValue(site.SiteName), nameof(ISite.PageTitleFormat) => new StringValue(site.PageTitleFormat), nameof(ISite.SiteSalt) => new StringValue(site.SiteSalt), nameof(ISite.SuperUser) => new StringValue(site.SuperUser), nameof(ISite.Calendar) => new StringValue(site.Calendar), nameof(ISite.TimeZoneId) => new StringValue(site.TimeZoneId), nameof(ISite.ResourceDebugMode) => new StringValue(site.ResourceDebugMode.ToString()), nameof(ISite.UseCdn) => BooleanValue.Create(site.UseCdn), nameof(ISite.CdnBaseUrl) => new StringValue(site.CdnBaseUrl), nameof(ISite.PageSize) => NumberValue.Create(site.PageSize), nameof(ISite.MaxPageSize) => NumberValue.Create(site.MaxPageSize), nameof(ISite.MaxPagedCount) => NumberValue.Create(site.MaxPagedCount), nameof(ISite.BaseUrl) => new StringValue(site.BaseUrl), nameof(ISite.HomeRoute) => new ObjectValue(site.HomeRoute), nameof(ISite.AppendVersion) => BooleanValue.Create(site.AppendVersion), nameof(ISite.CacheMode) => new StringValue(site.CacheMode.ToString()), nameof(ISite.Properties) => new ObjectValue(site.Properties), _ => NilValue.Instance }; return(result); }); }); services.AddScoped <ISetupEventHandler, SetupEventHandler>(); services.AddScoped <IPermissionProvider, Permissions>(); services.AddScoped <IAuthorizationHandler, SuperUserHandler>(); services.AddRecipeExecutionStep <SettingsStep>(); services.AddSingleton <ISiteService, SiteService>(); // Site Settings editor services.AddScoped <IDisplayManager <ISite>, DisplayManager <ISite> >(); services.AddScoped <IDisplayDriver <ISite>, DefaultSiteSettingsDisplayDriver>(); services.AddScoped <IDisplayDriver <ISite>, ButtonsSettingsDisplayDriver>(); services.AddScoped <INavigationProvider, AdminMenu>(); services.AddScoped <ITimeZoneSelector, DefaultTimeZoneSelector>(); services.AddTransient <IDeploymentSource, SiteSettingsDeploymentSource>(); services.AddSingleton <IDeploymentStepFactory>(new DeploymentStepFactory <SiteSettingsDeploymentStep>()); services.AddScoped <IDisplayDriver <DeploymentStep>, SiteSettingsDeploymentStepDriver>(); services.AddScoped <IRecipeEnvironmentProvider, RecipeEnvironmentSiteNameProvider>(); }
public async ValueTask <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext ctx) { var text = await GetTextAsync(input); var readTime = _readTimeCalculator.CalculateReadTime(text); return(NumberValue.Create(readTime)); }
public static FluidValue Modulo(FluidValue input, FilterArguments arguments, TemplateContext context) { if (arguments.Count == 0) { throw new ParseException("The filter 'modulo' requires an argument."); } return(NumberValue.Create(Convert.ToInt32(input.ToNumberValue()) % Convert.ToInt32(arguments.At(0).ToNumberValue()))); }
public override Value GetValue(UserItem item) { if (item.Item.CommunityRating.HasValue) { return(NumberValue.Create(item.Item.CommunityRating.GetValueOrDefault())); } return(Value.None); }
public override Value GetValue(UserItem item) { if (item.Item.ProductionYear.HasValue) { return(NumberValue.Create(item.Item.ProductionYear.Value)); } return(Value.None); }
public static ValueTask <FluidValue> AmplifyInput(FluidValue input, FilterArguments arguments, TemplateContext ctx) { var threshold = arguments.Count > 1 ? arguments.At(1).ToNumberValue() : 100; var inputFactor = input.ToNumberValue(); var amp = arguments.At(0).ToNumberValue(); var resultFactor = inputFactor > threshold ? inputFactor * amp : inputFactor * (threshold - amp); return(NumberValue.Create(resultFactor)); }
public void ModuloWithNoArgumentThrows() { var input = NumberValue.Create(6); var arguments = FilterArguments.Empty; var context = new TemplateContext(); Assert.Throws <ParseException>(() => NumberFilters.Modulo(input, arguments, context)); }
public override Value GetValue(UserItem item) { if (item.Item is Audio && item.Item.ParentIndexNumber.HasValue) { return(NumberValue.Create(item.Item.ParentIndexNumber.Value)); } return(Value.None); }
public override ValueTask <FluidValue> GetValueAsync(string name, TemplateContext context) { return(name switch { "size" => NumberValue.Create(123), "first" => NumberValue.Create(456), "last" => NumberValue.Create(789), _ => NilValue.Instance });
public static FluidValue Times(FluidValue input, FilterArguments arguments, TemplateContext context) { var first = arguments.At(0); var resultIsIntegral = input is NumberValue inputNumber && inputNumber.IsIntegral && first is NumberValue firstNumber && firstNumber.IsIntegral; return(NumberValue.Create(input.ToNumberValue() * first.ToNumberValue(), resultIsIntegral)); }
public override Value GetValue(UserItem item) { if (item.Item.RunTimeTicks.HasValue) { return(NumberValue.Create(GetMinutes(item.Item.RunTimeTicks.GetValueOrDefault()))); } return(Value.None); }
public void Truncate(string input, int size, string output) { var source = new StringValue(input); var arguments = new FilterArguments().Add(NumberValue.Create(size)); var context = new TemplateContext(); var result = StringFilters.Truncate(source, arguments, context); Assert.Equal(output, result.Result.ToStringValue()); }
public override Value GetValue(UserItem item) { if (item.Item is Episode && item.Item.IndexNumber.HasValue) { return(NumberValue.Create(item.Item.IndexNumber.Value)); } return(Value.None); }