Skip to content

Commit

Permalink
closes #112 Add syntax for late-bound viewdata eval
Browse files Browse the repository at this point in the history
inside a ${expr} or #stmt you can have #foo.bar terms that are passed to object Eval("foo.bar")

it may also be followed by a format string, like ${#price '#,##0.00'} or ${#terms.due "MM/dd yyyy"} which result in a call to string Eval("price", "{0:#,##0.00}")

because this calls the asp.net mvc ViewData.Eval method, to work in monorail it will require adding an implementation of object Eval(string expession) and string Eval(string expression, string format) to the base spark view class.


git-svn-id: http://dev.dejardin.org/svn/spark/trunk@298 0f556331-6e29-4ecb-911f-9ab3f335dbaa
  • Loading branch information
loudej committed Jan 16, 2009
1 parent e4dca0e commit 8383b02
Show file tree
Hide file tree
Showing 14 changed files with 210 additions and 53 deletions.
4 changes: 2 additions & 2 deletions src/CommonAssemblyInfo.cs
Expand Up @@ -14,10 +14,10 @@

[assembly: ComVisibleAttribute(false)]
[assembly: AssemblyVersionAttribute("1.0")]
[assembly: AssemblyFileVersionAttribute("1.0.295.0")]
[assembly: AssemblyFileVersionAttribute("1.0.296.0")]
[assembly: AssemblyCopyrightAttribute("Copyright © Louis DeJardin 2008-2009")]
[assembly: AssemblyProductAttribute("Spark")]
[assembly: AssemblyCompanyAttribute("Louis DeJardin")]
[assembly: AssemblyConfigurationAttribute("release")]
[assembly: AssemblyInformationalVersionAttribute("1.0.295.0")]
[assembly: AssemblyInformationalVersionAttribute("1.0.296.0")]

4 changes: 2 additions & 2 deletions src/CommonVersionInfo.h
@@ -1,8 +1,8 @@
// this is an auto-generated file
#define VERSIONINFO_VERSIONSTRING "1.0.295.0"
#define VERSIONINFO_VERSIONSTRING "1.0.296.0"
#define VERSIONINFO_MAJOR 1
#define VERSIONINFO_MINOR 0
#define VERSIONINFO_BUILD 295
#define VERSIONINFO_BUILD 296
#define VERSIONINFO_REVISION 0
#define VERSIONINFO_COPYRIGHT "Copyright © Louis DeJardin 2008-2009"
#define VERSIONINFO_COMPANY "Louis DeJardin"
Expand Down
19 changes: 19 additions & 0 deletions src/Spark.Tests/Parser/CodeGrammarTester.cs
Expand Up @@ -208,5 +208,24 @@ public void ClassKeywordUsedAsIdentifier()
Assert.AreEqual(@"var @class=1;", Combine(result7.Value));
}

[Test]
public void LateBoundSyntaxBecomesEvalFunction()
{
var result1 = _grammar.Expression(Source("#foo.bar"));
Assert.AreEqual(@"Eval(""foo.bar"")", (string) result1.Value);

var result2 = _grammar.Expression(Source("#foo .bar"));
Assert.AreEqual(@"Eval(""foo"") .bar", (string)result2.Value);

var result3 = _grammar.Expression(Source("(string)#foo+'bar'"));
Assert.AreEqual(@"(string)Eval(""foo"")+""bar""", (string)result3.Value);

var result4 = _grammar.Statement1(Source("Logger.Warn(#some.thing)"));
Assert.AreEqual(@"Logger.Warn(Eval(""some.thing""))", (string)new Snippets(result4.Value));

var result5 = _grammar.Statement1(Source("Logger.Warn(#some.thing)"));
Assert.AreEqual(@"Logger.Warn(Eval(""some.thing""))", (string)new Snippets(result5.Value));
}

}
}
@@ -0,0 +1,5 @@

<p>${#alpha}</p>
<p>${H(#alpha)}</p>
# Output.Write(#beta);
<p>4${#nosuchthing}2</p>
3 changes: 3 additions & 0 deletions src/Spark.Tests/Spark.Tests.csproj
Expand Up @@ -254,6 +254,9 @@
<None Include="Spark.Tests.Views\Home\ConditionalAttributeDelimitedBySpaces.spark">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Spark.Tests.Views\Home\LateBoundEvalResolvesViewData.spark">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Spark.Tests.Views\Home\OnceAttributeWorksOnSpecialNodes.spark">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
119 changes: 71 additions & 48 deletions src/Spark.Tests/SparkViewFactoryTester.cs
Expand Up @@ -875,56 +875,56 @@ public void OnceAttribute()
}


[Test]
public void EachAttributeWorksOnSpecialNodes()
{
mocks.ReplayAll();
var viewContext = MakeViewContext("EachAttributeWorksOnSpecialNodes", null);
factory.RenderView(viewContext);
mocks.VerifyAll();
string content = sb.ToString();

ContainsInOrder(content,
"<p>name-0-alpha</p>",
"<p>name-1-beta</p>",
"<p>name-2-gamma</p>",
"<span>one</span>",
"<span>two</span>",
"<span>three</span>");
}

[Test]
public void IfAttributeWorksOnSpecialNodes()
{
mocks.ReplayAll();
var viewContext = MakeViewContext("IfAttributeWorksOnSpecialNodes", null);
factory.RenderView(viewContext);
mocks.VerifyAll();
string content = sb.ToString();

ContainsInOrder(content,
"<p>name-0-alpha</p>",
"<p>name-2-gamma</p>",
"<span>one</span>",
"<span>three</span>");

Assert.IsFalse(content.Contains("beta"));
Assert.IsFalse(content.Contains("two"));
}

[Test]
[Test]
public void EachAttributeWorksOnSpecialNodes()
{
mocks.ReplayAll();
var viewContext = MakeViewContext("EachAttributeWorksOnSpecialNodes", null);
factory.RenderView(viewContext);
mocks.VerifyAll();
string content = sb.ToString();

ContainsInOrder(content,
"<p>name-0-alpha</p>",
"<p>name-1-beta</p>",
"<p>name-2-gamma</p>",
"<span>one</span>",
"<span>two</span>",
"<span>three</span>");
}

[Test]
public void IfAttributeWorksOnSpecialNodes()
{
mocks.ReplayAll();
var viewContext = MakeViewContext("IfAttributeWorksOnSpecialNodes", null);
factory.RenderView(viewContext);
mocks.VerifyAll();
string content = sb.ToString();

ContainsInOrder(content,
"<p>name-0-alpha</p>",
"<p>name-2-gamma</p>",
"<span>one</span>",
"<span>three</span>");

Assert.IsFalse(content.Contains("beta"));
Assert.IsFalse(content.Contains("two"));
}

[Test]
public void OnceAttributeWorksOnSpecialNodes()
{
mocks.ReplayAll();
{
mocks.ReplayAll();
var viewContext = MakeViewContext("OnceAttributeWorksOnSpecialNodes", null);
factory.RenderView(viewContext);
mocks.VerifyAll();
string content = sb.ToString();

ContainsInOrder(content,
"<p>name-0-alpha</p>",
"<span>foo1</span>",
"<span>bar0</span>",
factory.RenderView(viewContext);
mocks.VerifyAll();
string content = sb.ToString();

ContainsInOrder(content,
"<p>name-0-alpha</p>",
"<span>foo1</span>",
"<span>bar0</span>",
"<span>quux2</span>");

Assert.IsFalse(content.Contains("name-1"));
Expand All @@ -934,5 +934,28 @@ public void OnceAttributeWorksOnSpecialNodes()
Assert.IsFalse(content.Contains("bar1"));
Assert.IsFalse(content.Contains("bar3"));
}

[Test]
public void LateBoundEvalResolvesViewData()
{
mocks.ReplayAll();
var viewData = new StubViewData()
{
{"alpha", "<strong>hi</strong>"},
{"beta", "yadda"}
};
var viewContext = MakeViewContext("LateBoundEvalResolvesViewData", null, viewData);
factory.RenderView(viewContext);
mocks.VerifyAll();
string content = sb.ToString();

ContainsInOrder(content,
"<p><strong>hi</strong></p>",
"<p>&lt;strong&gt;hi&lt;/strong&gt;</p>",
"yadda",
"<p>42</p>");

}

}
}
5 changes: 5 additions & 0 deletions src/Spark.Tests/Stubs/StubSparkView.cs
Expand Up @@ -45,6 +45,11 @@ public string H(object content)
{
return HttpUtility.HtmlEncode(Convert.ToString(content));
}

public object Eval(string expression)
{
return ViewData.Eval(expression);
}
}

public abstract class StubSparkView<TModel> : StubSparkView
Expand Down
@@ -0,0 +1,4 @@

<p>${#Foo} ${#Bar.Text}</p>


@@ -0,0 +1,5 @@

<p>${#cost "#,##0.00"}</p>

<p>${#terms.Due 'yyyy/MM/dd'}</p>

@@ -0,0 +1,3 @@

<viewdata model="Spark.Web.Mvc.Tests.Models.Comment"/>
<p>${#Text}</p>
11 changes: 10 additions & 1 deletion src/Spark.Web.Mvc.Tests/Spark.Web.Mvc.Tests.csproj
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.21022</ProductVersion>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{C8E58F51-8630-4420-8630-8E634EC8900E}</ProjectGuid>
<OutputType>Library</OutputType>
Expand Down Expand Up @@ -90,6 +90,15 @@
<None Include="AspNetMvc.Tests.Views\Home\childview.spark">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="AspNetMvc.Tests.Views\Home\EvalWithFormatString.spark">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="AspNetMvc.Tests.Views\Home\EvalWithAnonModel.spark">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="AspNetMvc.Tests.Views\Home\EvalWithViewDataModel.spark">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="AspNetMvc.Tests.Views\Home\foreach.spark">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
Expand Down
37 changes: 37 additions & 0 deletions src/Spark.Web.Mvc.Tests/SparkViewFactoryTester.cs
Expand Up @@ -470,5 +470,42 @@ public void CreatingViewEngineWithSimpleContainer()
Assert.AreSame(viewFolder, viewEngine.ViewFolder);
Assert.AreSame(viewFolder, viewFactory.ViewFolder);
}


[Test]
public void EvalWithViewDataModel()
{
mocks.ReplayAll();
FindViewAndRender("EvalWithViewDataModel", new Comment { Text = "Hello" });
mocks.VerifyAll();

var content = output.ToString();
Assert.That(content.Contains("<p>Hello</p>"));
}

[Test]
public void EvalWithAnonModel()
{
mocks.ReplayAll();
FindViewAndRender("EvalWithAnonModel", new { Foo = 42, Bar = new Comment { Text = "Hello" } });
mocks.VerifyAll();

var content = output.ToString();
Assert.That(content.Contains("<p>42 Hello</p>"));
}


[Test]
public void EvalWithFormatString()
{
mocks.ReplayAll();
FindViewAndRender("EvalWithFormatString", new { Cost = 134567.89, terms = new { due = new DateTime(1971, 10, 14) } });
mocks.VerifyAll();

var content = output.ToString();
Assert.That(content.Contains("<p>134,567.89</p>"));
Assert.That(content.Contains("<p>1971/10/14</p>"));
}

}
}
9 changes: 9 additions & 0 deletions src/Spark.Web.Mvc/SparkView.cs
Expand Up @@ -140,6 +140,15 @@ public string H(object value)
return Html.Encode(value);
}

public object Eval(string expression)
{
return ViewData.Eval(expression);
}
public string Eval(string expression, string format)
{
return ViewData.Eval(expression, format);
}

protected virtual void SetViewData(ViewDataDictionary viewData)
{
_viewData = viewData;
Expand Down
35 changes: 35 additions & 0 deletions src/Spark/Parser/Code/CodeGrammar.cs
Expand Up @@ -107,9 +107,33 @@ public CodeGrammar()
var identifier = availableIdentifier
.Or(Snip(Ch('@').And(identifierOrKeyword), hit => "@" + hit.Down));

// parsing late bound properties #a.b.c into Eval("a.b.c")

var dotProperty = Snip(Ch('.').And(identifierOrKeyword),
hit => hit.Left + hit.Down);

var formatAppendage = Swap(Opt(Rep(Ch(' ', '\t')))
.And(Ch('\"').And(Rep1(ChNot('\"'))).And(Ch('\"'))
.Or(Ch('\'').And(Rep1(ChNot('\''))).And(Ch('\'')))),
hit => ", \"{0:" + new string(hit.Down.Left.Down.ToArray()) + "}\"");

var lateBound = Ch('#')
.And(Snip(identifierOrKeyword))
.And(Snip(Rep(dotProperty)))
.And(Opt(formatAppendage))
.Build(hit => (IList<Snippet>) new Snippets("Eval(\"")
.Concat(hit.Left.Left.Down)
.Concat(hit.Left.Down)
.Concat(new Snippets("\""))
.Concat(hit.Down ?? new Snippet[0])
.Concat(new Snippets(")"))
.ToList());


var codeStretch = Snip(Rep1(
Swap(Ch("[["), "<")
.Or(Swap(Ch("]]"), ">"))
.Or(lateBound)
.Or(Snip(ChNot('\"', '\'', '{', '}')))
.Unless(identifier.Or(keyword).Or(SpecialCharCast))
.Unless(Ch("%>").Or(Ch("@\"")).Or(Ch("@'")).Or(Ch("//")).Or(Ch("/*")))));
Expand Down Expand Up @@ -137,6 +161,7 @@ public CodeGrammar()
var statementPiece =
Swap(Ch("[["), "<")
.Or(Swap(Ch("]]"), ">"))
.Or(lateBound)
.Or(Snip(ChNot('\"', '\'')))
.Unless(SpecialCharCast)
.Unless(Ch("@\"").Or(Ch("@'")).Or(Ch("//")).Or(Ch("/*")));
Expand Down Expand Up @@ -261,6 +286,16 @@ static ParseAction<IList<Snippet>> Swap<TValue>(ParseAction<TValue> parser, stri
return new ParseResult<IList<Snippet>>(result.Rest, new[] { snippet });
};
}
static ParseAction<IList<Snippet>> Swap<TValue>(ParseAction<TValue> parser, Func<TValue, string> replacement)
{
return position =>
{
var result = parser(position);
if (result == null) return null;
var snippet = new Snippet { Value = replacement(result.Value) };
return new ParseResult<IList<Snippet>>(result.Rest, new[] { snippet });
};
}

public ParseAction<IList<Snippet>> ExpressionTerms;
public ParseAction<Snippets> Expression;
Expand Down

0 comments on commit 8383b02

Please sign in to comment.