-
Parrot 1.0 Release (04 Feb 2013)
I’m proud to announce that Parrot is going 1.0 this week. It went through a lot of changes to get it here. Several major “rewrites” of portions of the system to handle usages I had not conceived of while writing. The more interesting change was the removal of the dependency resolver. Thanks to GrumpyDev for his help in that area.
Javascript
One of the more interesting releases is that of a javascript parrot parser. It can be used in webpages or possibly with nodejs once a wrapper is written. Including the required files and jQuery makes it as simple as
var html = $("#template").parrot();You can then insert it in your webpage just like any html string.
$("body").html($("#homepage").parrot());
Here’s how it’s used on the home page for Parrot.
Nancy
Also with this release a version of the Parrot View Engine for the NancyFx. Porting Parrot over to Nancy was one of the ways I found everything that could be wrong with Parrot. This caused several rewrites as I realized I was really dependent on Asp.net. I think this has streamlined how it works.
Asp.Net
And of course, there’s support for Asp.Net MVC.
Nuget
Installing parrot is easy with Nuget
PM> Install-Package Parrot.AspNetPM> Install-Package Nancy.ViewEngines.ParrotLive demo

So checkout the website at http://thisisparrot.com/ and try Parrot out for yourself.
-Ben Dornis
-
Lambda expressions and closures (26 Nov 2012)
I’ve been dealing a lot with anonymous methods lately I started getting interested in how they work, especially when dealing with closures. I was curious how these variables worked.
for(int i = 0; i < 10; i++) { Task.Factory.StartNew(() => Console.WriteLine(i)); }One might think that this would output
0, 1, 2, 3, 4...9however as the anonymous methods might not be run at the time they’re defined and the loop may finish faster and setito10before the anonymous methods are even executed (depending on the speed of your system). The output on my system results in 10 lines of10. The reason for this is how c# generates the code for this. I’ve simplified it for brevity.//original code class Program { static void Main() { for (int i = 0; i < 10; i++) { Task.Factory.StartNew(() => Console.WriteLine(i)); } } } //after compilation //this code has been simplified //for readability class Program { static void Main() { c__DisplayClass2 cDisplayClass2 = new c__DisplayClass2(); for (cDisplayClass2.i = 0; cDisplayClass2.i < 10; cDisplayClass2.i++) { Task.Factory.StartNew((Action)(cDisplayClass2.b__0)); } } } [CompilerGenerated] private sealed class c__DisplayClass2 { public int i; public void b__0() { Console.WriteLine(this.i); } }Wow. The lambda expression sure does hide a lot of code. Let’s see what’s going on here. Notice a new class was created and it has a public property
i. This replaces the local variable in thefor/loop. This is how we can access the closure within the anonymous method. It no longer exists in the context of a local variable of the function but as a global field of the generated class. As the methods don’t necessarily execute when called you must make sure to make a local copy of that variable. If not the member variable might change before execution.What if you’re accessing a member field of the calling class?
//original class Program { private string test = "test"; static void Main() { var p = new Program(); p.Run(); } private void Run() { Task.Factory.StartNew(() => Console.WriteLine(test)); } } //after compilation //this code has been simplified //for readability class Program { private string test = "test"; private static void Main(string[] args) { new Program().Run(); } private void Run() { Task.Factory.StartNew((Action)(b__0)); } [CompilerGenerated] private void b__0() { Console.WriteLine(this.test); } }As we can see, this time, the
CompilerGeneratedmethod resides in the same class. This is required because it needs to access the same field of the calling class. However, if you need both, access to a local variable and one to a member variable it creates a second class with a references to the caller.//after compilation //this code has been simplified //for readability class Program { private string test = "test1"; private void Run() { c__DisplayClass1 cDisplayClass1 = new c__DisplayClass1(); cDisplayClass1.__this = this; Task.Factory.StartNew((Action)(b__0)); } [CompilerGenerated] private sealed class c__DisplayClass1 { public string test1; public Program __this; public void b__0() { Console.WriteLine(this.__this.test + this.test1); } } }Knowing how the compiler acts on your code is important. Hopefully this will give you some insight as to how some of the magic with anonymous methods/functions happens.
-
Engaging the community (21 Sep 2012)
If you never engage others, you never chat about what you’ve learned, never ask questions – you’ll never truly grow. It’s through discussion and debate that I’ve found to be the best way to change your views and expand your horizons. I’ve learned more discussing with friends than I think I would learn on my own reading blog posts and what not. When you’re programming by google you’re stuck in a little bubble. You tend to search for things you already know or were exposed enough to already want to learn more. You might be exposed to new ideas on how to do a particular method or implementation but others who understand what you’re trying to accomplish might promote other methods or implementations you might have never thought of before.
Recently I started a project in which I was designing my own language for an MVC View Engine I’m calling Parrot. This is pretty new territory for me. I’ve designed basic languages before. All based on something I was intimately familiar with. Very basic parsing based on substrings and indexofs. In other words horrible. I would have gone down this same path if I weren’t discussing this new project with a long time friend of mine.
He suggested I write or use an existing parser/generator engine. What’s that?! Lots of acronyms and strange words were thrown around including LALR parser, SLR, LR(1) and so forth. I’ve never heard of these before. After some reading I discovered GoldParser. This was an amazing discovery. I was able to get the basics of the new language down fairly quickly and get a prototype of it running within the day. New changes to the language came over the week each with modifications to the grammars used by GoldParser. This allowed a lot of flexibility. If I had written my own simple parser I doubt I would have had the ability to change it so easily and advance so quickly.
Through discussion and by engaging others I was able to learn something new that will benefit me for years to come.
Friendships
However, you still need to have friends you can bounce ideas off of. People that listen to what you say and give you advice when needed or just act like a Rubber Duck. But you have to get out there and mingle with other developers. There are Nerd Dinners, Code Camps, DevCamps, User Groups, JabbR, tons of ways to meet other devs who probably share the same passions you do.
What do you get out of this? The most obvious answer is you make friends. I have met some interesting people by attending the Socal Code Camp. It’s a free event where developers of all kinds can come together and share knowledge and learn from each other. This event has been one of the turning points in my development career. I’ve made friends that I’ve stayed in touch with since we met. You never know what these new found contacts might bring to the friendship. Some bring knowledge. Some bring opportunities. But they all bring something to the table and likewise so do you, even if it’s just a new friend.
The most important thing is to meet the people you call peers. Take that first step. It can be a tough one but it will pay off in the long run. Some devs stay in the background and try not to be social. Don’t be one of those. Push forward and engage them. You never know who you’ll meet or where that meeting will take you.
-Ben Dornis
-
The Parrot Grammar (10 Sep 2012)
Creating an element
Creating an html element is really simple – just type a word.
div a ul some-random-nameThese will create an html element (using the default html renderer) with the word as the tag.
<div></div> <a></a> <ul></ul> <some-random-name></some-random-name>Adding an id or class to your new element
There are two ways to add an id or class declaration to your elements. We’ll focus on the simple way in this example.
div#sample-idThis will simply create an html element with the id tag set to “sample-id”.
<div id="sample-id"></div>The same method is used for classes but with a . instead.
div.sample-class <div class="sample-class"></div>You may combine these two in one declaration. An element may only have one id tag but can have as many class declarations you’d like.
div#sample-id.sample-class.other.info <div class="sample-class other info" id="sample-id"></div>Adding other attributes
Ids and classes aren’t the only attributes you’ll want to add. Adding additional attributes is simple. wrap a simple key/value pair with brackets. Multiple attributes can be added with a space in between. You can also add the id/class attributes here as well but it’s not recommended.
div[name="value"] div[name="firstname" data-val-id="something"] <div name="value"></div> <div name="firstname" data-val-id="something"></div>Adding children
There are two ways to add children. To add multiple children you’ll usually want to wrap them in a parent using curly braces. To add a single child you can append it using the
>syntax.ul { li li li li } div > a > span <ul> <li></li> <li></li> <li></li> <li></li> </ul> <div><a><span></span></a></div>Adding siblings
Sometimes you don’t need or want to wrap multiple children in curly braces. You can add siblings to an element by using the
+selector syntax.ul > li + li + li + li <ul> <li></li> <li></li> <li></li> <li></li> </ul>Model parameters
To bind an element to a model you just add it to the element declaration surrounded by parenthesis
().div(User)This element is now bound to the User property of your model. This affects children properties as well and allows for simplified nesting.
div(User) > span(FirstName) + span(LastName)String literals
There are several ways to output a string literal.
span > | String that reads until newline character "String wrapped in quotes" @"String which doesn't care about newlines" 'String wrapped in single quotes' @'Multiline string wrapped in single quotes'Variable outputs
There are two shortcuts to output a variable. If you pass in a variable to a block element and do not provide any children the variable with be added to the dom as with
ToString()applied and added as the first child to that element. You can also encode output using@or raw output using=. (PageTitle = “Parrot”)title(PageTitle) title > :PageTitle title > =PageTitle <title>Parrot</title>Shortcuts
You can shortcut divs by leaving div out when using #sample-id or .sample-class.
#sample-id <div id="sample-id"></div>Some elements can override the default tag name such as a ul or ol. The default for these elements (as children) are li.
ul > #sample-id <ul><li id="sample-id"></li></ul>You can shortcut common attributes such as
typeas well. For instance when adding an input element you can specify it’s type by adding a:followed by type.input:text input:radio <input type="text" /> <input type="radio" />Parser
This is a very brief overview of the grammar for parrot. I don’t anticipate any near-future changes. I am currently working on an alternative parser for the language to avoid a reference to GoldParser. I hope to have it released in the next couple weeks.
-Ben Dornis
-
How I learned to stop worrying and love my community (28 Aug 2012)
How I got started in programming.
I was 8 years old and I was playing little league. For two years I was a member of the LA Dodgers and the Expos. I worked really hard at baseball. But I was no good. I probably held the record for the most walks in the history of little league because I couldn’t hit the ball. Ever. My mother made me an offer I couldn’t refuse. Hit the ball (and get on base) and she’d buy me a Nintendo. I wanted one since the day it came out. I would play Gouls and Ghosts at my friends house for hours on end. But, alas, I still couldn’t hit the ball. During my second year and almost to the end of the season my coach was thrown out of the game. I don’t recall why, but our team had renewed vigor. The next time I was up at bat. I hit the ball. I was shocked. Didn’t know what to do. Finally I ran and made it to first base. My mother was elated.
True to her word she bought me a Nintendo and a subscription to Nintendo Power. My sports life was over. (For the most part) I was hooked on games. Dragon Quest (which came with my subscription) was the first game I truly owned. It was glorious. I made it my goal in life at that point to be a game designer. I wanted to make the next Dragon Quest.
Alas, money was a hard thing to come by in my life. It was 2 years before I was able to buy a computer. I saved 300 dollars and the classifieds yielded my first Windows 3.1 system. Truth be told I almost never used Windows. I was stuck in DOS and QBASIC. I didn’t quite understand what I was doing but I could print to the screen. I bought a book on the subject and started getting lines and colors on the screen. I moved on from QBASIC to Pascal because that’s what LORD was written in. I wanted to write mods. It became such an addiction I would stay up late at night with a blanket over the screen so that my parents couldn’t see the glow of the blue monitor through the door. I even started my own BBS…parents hated when I tied up the phone line. It was a single line BBS but it was fun.
No matter what though, I couldn’t write games. I had some sort of mental block when it came to how they worked. I could write applications. I had a knack for business logic. So I stuck with that. However, I would never show anybody my code. Always embarrassed by what they would think of it. I never felt I was good enough. A couple of my friends were really smart and that didn’t help. Most of them would always understand something before me or they were developing in c++ or already writing Windows applications. Here I was stuck in QBASIC/Pascal.
But I persevered. I trudged on. I wasn’t discouraged from programming. It just made me work that much harder. I started learning PHP, Visual Basic, Borland C++ but I never really did anything that I considered great. It was great fun. I knew this was my future. I started work at a background screening company in ~2005 and that’s where I really began to focus on business. I had a lot of great internal projects and they were great in terms of getting work done but I was embarrassed by the code. We didn’t have code reviews in this shop. So it wasn’t much of an issue. I would sometimes share my work with a co-worker (long time friend of 18+ years) but I would always try to keep it to myself. I was usually proud with the outcome of my code but not with the code itself.
I started to go public
Economy tanked. I lost my job. I didn’t have any “contacts” to call on for help and see if there were any openings. That’s when I decided to start a blog. This blog. But what would separate me from the rest of the programming blogs out there? I needed something big to get attention. I just started looking at the Razor Engine by Microsoft for ASP.NET MVC. There was no editor for it available. I thought I could make a highlighter. That would rock.
I had no clue what I was doing. Visual Studio Extensions were the worst thing I’ve ever seen in my life. But somehow, I wrote it. I pushed it out there and it was loved…people were asking me for the source. Oh god. I couldn’t share my source. I don’t know what I’m doing. I’ve only been developing for 20 years. They’re going to laugh at me. I delayed it as much as possible. As soon as the official MVC 3 release came out I wouldn’t have to share my source since nobody would care. That worked for a while. A month after release I went to a webcamp at Microsoft in Los Angeles. Phil Haack was going to be there. He’s popular. I thought if I showed him my syntax highlighter he might be impressed and something magic would happen. I didn’t know what that would be but I had to try.
Things got interesting
That magic didn’t happen. But there was this one guy sitting off to the left. I didn’t know who he was but I was content to talk to him while I waited to talk to Haack. I forget how the conversation went but I ended up showing him my blog and he was my first ever follower on twitter. That man was Jon Galloway. Awesome!
This chance meeting forever changed this developer’s life.
Immediately I felt comfortable sharing with him. He encouraged me to join some open source projects. I really wanted to start with ASP.NET MVC but he suggested I start smaller. I continued to focus on just snippets of code. Nothing ever huge. I would submit a patch here and there nothing ever big. Then one Stackoverflow question set afire an idea for my first open source project. What became of my answer was RazorEngine. Galloway encouraged me to release it to the public. I started a series of posts on how this RazorEngine worked. I couldn’t believe the support from random internet people. Nobody said anything bad. All positive. It was even featured on the asp.net Daily Spotlight. I couldn’t believe it.
Eventually Jon Galloway invited me to contribute to the then MVCConf and talk about Razor. I was all ready to do it and I came down with the flu. I was relieved. I couldn’t stand the thought of people laughing at me while I made a fool of myself trying to explain Razor. I kept working on my projects. Kept releasing my source. It helped that whenever I showed Galloway one of my ideas he had nothing but good things to say. Always encouraging. It got to the point where I was finally ready to speak. Jon had a lot of advice as to how to proceed. I took the plunge. I signed up as a speaker for SocalCodeCamp.
Scary.
I wanted to quit. Wanted to cancel for some reason. I never told anyone I wanted to cancel but Jon still encouraged me; probably assuming I had jitters. It was a disaster. I had a demo fail. I had some questions I couldn’t answer. I had some people up and walk out on me. It was heartbreaking. I assumed I was horrible. I was never good at explaining things but I tried anyway. That code camp I met other devs such as Hattan Shobokshi and Dustin Davis. They were both encouraging. They explained their first time. Hattan gave me some advice related to his own personal experience. (Paraphrased) “You never know why they’re leaving. They might have a phone-call or have to go to the bathroom. I had one guy not pay attention the whole time. I thought I blew it but it turns out he was just tired but he heard every word.”
These people gave me more of a support structure. People that were encouraging and selfless in their willingness to share their knowledge and experience. I’ve since given that talk twice. The second time was great. I had so many people asking questions. They were engaged. They were actually interested in what I had to say. It was amazing. Some even stayed late during their lunch break. Very encouraging. So much so that I am planning a third session and a collaborative session with Hattan at the next SocalCodeCamp.
During the time between RazorEngine and now I’ve released many open source projects. Some are good, some are bad. But I no longer worry about it. I don’t worry that people will laugh at me or berate my code. The programming community that I think I’ve grown to be a part of has been nothing but supportive. I haven’t received a single drop of discouragement.
I’ve tried to take my experience and help others. I hope that I’ve accomplished that with some of you and continue to do that in the future. I want to try and give others the same chance that I’ve been given. I may not work for Microsoft or have 50k followers on twitter. But I’ll still help out when I can.
If you’re just starting out or you’ve been programming for 20+ years it can be scary to share your code with others. I hope I can encourage some of you to share your code. Share ideas. Make friends. Take that step. You never know where it might lead but it will lead nowhere if you don’t take a chance.
-Ben Dornis
-
Why you should care about Parrot a new Asp.net mvc view engine (17 Aug 2012)
This post highlights some of the main features of what Parrot is with a little detail thrown in.
Test drive
I have a test drive page that allows you to try out Parrot from your browser. Note that the test drive actually uses the alpha bits available for download from Nuget to render pages. It’s a work in progress (standard Works On My Machine disclaimer) but it really helps demonstrate how Parrot works.
Simplicity
Creating a clean view is very simple. Much of the fluff has been removed and you’re left with only what you need to define the document. Html can be verbose and an attempt has been made to minimize the amount of text needed to define a document to what is necessary. To that end I’ve decided to use the css selector syntaxes. I’m going to show you a sample of html then Parrot and then a short-hand Parrot (that works only for divs)
<div id="some-id"></div> div#some-id #some-idThey all produce the same output. Notice the cleaner syntax of Parrot. I think it allows more of a focus on the structure of the html and how models interact.
Model support
The default html renderers that ship with Parrot.Mvc have great model support. Automatically scoped variables as well as support for IEnumerables.
Scoped variables
By default variables are scoped based on their nearest parent with a parameter (defaults to the passed in model). As you traverse down the children the model values are automatically mapped to a property of the parent model parameter
var model = new { User = new { Name = "Buildstarted", Location = "Los Angeles" } }; div(User) { label> "Name" span(Name) label > "Location" span(Location) }You can see in the above code that we didn’t have to tell the span style=“display:inline;”>Model.User.Name when we wanted to output the value. We just had to tell it name. When a model value is resolved it looks for a property on the parent that matches and will output that. Likewise the default model for all elements is
thiswhich is the same asModelIEnumerable support
IEnumerable support was something I wanted to include as a default thing in Parrot. Any time an element definition includes an IEnumerable as the first parameter and has children it will automatically loop over the parameter and create the child definition passing current element as a parameter to it. This is great for
ul,ol,tbodyand so forth. To illustrate this let’s take aList<string>and pass it into a ul element.var model = new { Authors = new List<string> { "Ray Bradbury", "Harry Harrison", "Douglas Adams", "Isaac Asimov", "Arthur C. Clarke" } }; ul(Authors) { li(this) } <ul> <li>Ray Bradbury</li> <li>Harry Harrison</li> <li>Douglas Adams</li> <li>Isaac Asimov</li> <li>Arthur C. Clarke</li> </ul>Note the parameter for
liisthisbecause the element we want to output is the current item in the list. If this were a property we wanted to output likeNamewe would just useli(Name)orli > :Name. By default all html elements support IEnumerable parameters. Some, such as any self closing element, will ignore the parameter.Renderers
Renderers are the heart and soul of Parrot. The grammar is just a vehicle to the renderers. Every single element and any custom element can have it’s own renderer leading to a lot of control over the output. This also allows for a lot of flexibility. Model support, in addition, is entirely a function of the renderers. You could technically have renderers that require fully qualified model properties.
Bottom line: Parrot view engine is built from renderers, and you can easily write your own renderers to add keywords that extend the language however you’d like.
Creating a new renderer is as easy as implementing an interface and adding your new class to a
RendererFactory. Any time the renderer comes across an element it will query the RenderFactory for it’s renderer and use it to parse the block.There are currently several custom renderers including a renderer that supports a
layoutview,foreachanddoctype.//child view layout("viewname") { ...content goes here... } //layout view html > body { content }Renderers can also override the default element for short-cut statements. For instance the following will not output the
divbut will instead outputliul(Authors) > .item(this) <ul> <li class="item">Ray Bradbury</li> ... </ul>Future posts
I’m going to go into more detail about the grammar in a future blog post as well as go into details about creating a custom renderer and how the rendererfactory works.
Nuget
I’ll try to go into detail on each feature of Parrot as questions come in. But in the mean time you can try out Parrot for yourself. The alpha bits are available on Nuget. You can ask questions on Jabbr in the Parrot room. I’ll answer them when I can. You can always bug me on Twitter.
-Ben Dornis
-
This is Parrot (02 Aug 2012)
For the past week and a half I’ve been working on a new view engine for Asp.Net MVC called Parrot. (I chose the name because there were only 2 nuget packages that showed up with that name – and it sort of fit with what a view engine is. It repeats something but different.)
Parrot was designed to be similar to css. Something familiar that everyone should just be able to pick up without much knowledge. Here’s a quick example.
ul#phoneNumbers.phone(PhoneNumbers) { li[data-val-id=Id] { label(Type) span(Number) } }Note the ul element is followed by an optional id and optional class declarations. There’s an additional
(PhoneNumbers)parameter. This tells us that the ul is bound to the model propertyPhoneNumbers. Since the model property isIEnumerablethe engine automatically loops over the child template and applies the current enumerable element to it. If we had a model like the following:new { PhoneNumbers = new [ new { Id = 1, Type = "home", Number = "212 555-1234" }, new { Id = 2, Type = "fax", Number = "646 555-4567" } ] } <ul class="phone" id="phoneNumbers"> <li data-val-id="1"> <label>home</label> <span>212 555-1234</span> </li> <li data-val-id="2"> <label>fax</label> <span>646 555-4567</span> </li> </ul>Each element can have a custom renderer. This allows for extreme flexibility when it comes to what parrot does. Suppose you want to loop a bunch of divs but inlined with other elements. You can create your own renderer for a custom element name like
foreach.Now instead of looping within a ul it just outputs a list of divs.
foreach(PhoneNumbers) { div[data-val-id=Id] { label(Type) span(Number) } }You can even inject your own RendererFactory that will replace all renderers with something completely different. If you don’t want to output html but instead want to output markdown or maybe html but raw with syntax highlighting or anything. Completely extensible.
This is just the start. The language is still going through refinements as different usage scenarios popup but the basic structure should remain the same.
You can download the source from Github here.
-Ben Dornis
-
Offline programming (17 Jul 2012)
This is the first in an n-series of posts about programming while offline. I’ll be posting about issues and solutions to these issues that I come across.
Why?
I recently bought a house but it’s not quite ready to move into yet. My wife and I also expecting our first child and since we’ve already gone through the process of finding a doctor and hospital and what not to have the baby at we’re just not ready to move. However, we go there just about every weekend. Little things that need to be done such as the lawn. Receiving furniture. Minor remodeling.
With the length of time I spend there I don’t feel justified in paying for internet access just yet.
This has led to a small conundrum. I am unable to develop the same there as I do at my apt.
Almost every project I have relies on access to Nuget. CDN hosted js files such as jQuery. Most of my public projects are hosted on Github.
Simple solutions
Nuget is easy enough to replace by downloading all of the packages that I use onto a removable drive. Access is generally slower than Nuget.org because it has no index to search.
CDN hosted files could be hosted locally as well but with changes to how I’m pointing to them.
Github hosted projects are probably the easiest because git makes this nice and easy. I can push all of my commits at a later time, however it does mean I need to pull my repos to a removable drive.
Other issues
A lot of my projects also revolve around Amazon’s AWS. It’s what we use at work. I love trying out new things related to their offerings but there’s no emulation mode available to do offline testing.
I also love to read about technologies that I don’t know about or would like to learn more about. I’ve been reading about Redis and Memcached and other similar libraries/services. Harder to read about them offline.
I also tend to chat a lot with other devs about issues I might be having with some code. I don’t have internet on my phone and find phone keyboards to be too awkward anyway to chat on anyway so that’s a non-issue. This will make collaboration a bit harder.
However
Sometimes you need a break from all the distractions. The latest Youtube video viraling out of control on the internet or funny cat pictures won’t consume some of my precious time. No email from work.
Twitter won’t interrupt me with the latest technology or blog post written by one of my peers. Every weekend I miss out on all the interesting stuff that may have happened. I’ve made it a rule to just ignore it. I don’t go back and browse the history of what I might have missed. (Unless it was directed at me specifically)
Final
So far offline development hasn’t been a picnic. Lots of downsides with a few upsides. I’ll be going over these in more detail when/if I find better ways around them.
-Ben Dornis
-
DescriptionFor mvc 3 (17 Jul 2012)
I’m surprised this particular feature doesn’t exist for MVC – it’s such a simple function.
I needed a description of the fields being entered for a form and I decided to use the Description parameter of the Display attribute. To add the attribute you just:
public class RegistrationModel { [Display(Name = "Referrer", Description="If anyone referred you to us please enter their name?")] public string Referrer { get; set; } }Nice and simple. However, there’s no easy way to get the description text out of the property.
public static IHtmlString DescriptionFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression) { return DescriptionFor<TModel, TValue>(helper, expression, null); } public static IHtmlString DescriptionFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, string descriptionText) { ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData); string text = descriptionText ?? metadata.Description; if (string.IsNullOrEmpty(text)) return new HtmlString(""); TagBuilder tag = new TagBuilder("span"); tag.SetInnerText(text); return new HtmlString(tag.ToString(TagRenderMode.Normal)); }This technique is also useful if you want to create additional attributes with html helpers for some custom stuff. Like a mask attribute or something.
-Ben Dornis
-
Socal code camp this weekend part two (19 Jun 2012)
After the very first public speaking engagement at the last Code Camp I’ve decided to try again. I didn’t get any feedback last time but I hope I’ve noticed enough that went wrong that this time there will be fewer mistakes. No promises though. I’ll be speaking at Socal Code Camp at UC San Diego about Routing in Asp.Net on Saturday, the 23rd, at 2:45.
If you’re attending my session please come back to this post afterward and leave comments. Positive or negative doesn’t matter to me as long as they’re constructive. Anything to help improve my future sessions.
I’ve put my session layout on Trello.com and if you have any comments or would like me to omit anything please leave a note there.
Update
Thanks everyone for attending. This was an excellent audience. Lots of questions. No demo fails. Live, raw, code…The only thing is I forgot to look at my notes and forgot a section :) However, you all stayed anyway until I finished. Thanks for making it a great session everybody. I look forward to giving this session one more time at the next code camp!
Source code used in the session can be found https://github.com/Buildstarted/RoutingSession
Thanks in advance, Ben Dornis