Disclaimer

The views expressed on this weblog are mine alone and do not necessarily reflect the views of my employer, Avanade.

Search
Login

Code Snippets makes developers more productive

I created this tool to make reusing common functions, queries or other pieces of code easy as pie.
Friday
Aug292014

Sitecore Field Type Overview Reference

Had this as a reference page in my personal Sitecore OneNote notebook and that I thought may be useful to others. I think it's useful for visualizing field types and thinking through their configuration.

 

Simple Types

Checkbox

Notes

  • Not Supported by Field Renderer

 

Date

 

Notes

  • Use $date and $time in standard values for current date/time

 

Datetime

Notes

  • Use $date and $time in standard values for current date/time

 

File


Notes

  • If the Source property of a File field specifies an item, the Media Browser cannot navigate above that item. The user cannot navigate above the specified media folder. The default Source for File field is theroot item in the Media Library (/Sitecore/Media Library)

 

Image

Notes

  • Source field defines locations of the media library that is relevant.

 

Integer

Multiline Text

 

Number

Password

   Notes

  • Not supported by Field RendererSave

 

Rich Text

 

Notes

  • Stores as XHTML
  • Source set to Editor Profile in Core Database, which can be customized as needed.

 

Single Line Text

 

Word Document

Notes

  • Can only be edited in IE

 

List Types

 

Checklist

Notes

  • DataSource - Sitecore Query of Item, displays children of path provided.
  • Stores Data like Multilist

 

Droplist

 

Notes

  • Datasource - Sitecore Query of Items, displays children of path provided.
  • Name of Item Stored. No Link to actual item. Use sparingly because of this.

 

 

Grouped Droplink

Notes

  • Data Source points to Grand Parent of selectable items. Children are used to group selectable items.
  • References Selected Item Guid so entire item can be easily fetched.

 

Grouped Droplist

Notes

  • Data Source points to Grand Parent of selectable items. Children are used to group selectable items.
  • Name of Item Stored. No Link to actual item. Use sparingly because of this.

 

Multilist

Notes

  • Datasource - Path to item, displays children of path provided.
  • Supports Sitecore Query instead of path to item.

 

Treelist

Notes

  • Fields allowed in datasource (query string format)
    • DataSource — the field data source item, equivalent to using a path as the field source property
    • as described previously
    • DatabaseName — the name of the database containing the data source item
    • IncludeTemplatesForSelection — the user can select items associated with this comma separated list of template names.
    • ExcludeTemplatesForSelection — the user cannot select items associated with this comma separated list of template names
    • IncludeTemplatesForDisplay — the user can navigate items associated with this comma separated list of template names
    • ExcludeTemplatesForDisplay — the user cannot see items associated with a comma separated list of template names
    • IncludeItemsForDisplay — the user can see items with this comma-separated list of IDs
    • ExcludeItemsForDisplay — the user cannot see items with this comma-separated list of IDs.
    • AllowMultipleSelection — the user can select more than one item

 

Treelist ex

Notes

  • Only displays tree when clicked. Loads faster than normal treelist as a result.
  • Fields allowed in datasource (query string format)
    • DataSource — the field data source item, equivalent to using a path as the field source property
    • as described previously
    • DatabaseName — the name of the database containing the data source item
    • IncludeTemplatesForSelection — the user can select items associated with this comma separated list of template names.
    • ExcludeTemplatesForSelection — the user cannot select items associated with this comma separated list of template names
    • IncludeTemplatesForDisplay — the user can navigate items associated with this comma separated list of template names
    • ExcludeTemplatesForDisplay — the user cannot see items associated with a comma separated list of template names
    • IncludeItemsForDisplay — the user can see items with this comma-separated list of IDs
    • ExcludeItemsForDisplay — the user cannot see items with this comma-separated list of IDs.
    • AllowMultipleSelection — the user can select more than one item

 

Link Types

 

Droplink

Notes

  • Datasource - Sitecore Query of Items
  • References Selected Item Guid so entire item can be easily fetched.

 

Droptree

Notes

  • If just path provided, roots tree in that result.
  • Supports Sitecore Query in Datasource

 

General Link

Notes

  • Can point to external links or internal sitecore items.
  • Can include anchor details: target, text, etc.
Saturday
Aug232014

A/B Split Testing with ASP.Net and C#

As software developers we often create things others tell us to make. Usually we do so blindly. There is no feedback loop to tell us what features are working well or whether they are being used at all. With no goals, there is way to tell whether what we build is successful or not. It doesn't have to be this way. Books like the "lean startup" discuss "validated learning" which essentially are techniques to prove that the assumptions you have (or the folks giving you requirements have) are actually true.

One of the biggest tools in the tool box for validating these assumptions is called split testing or A/B testing. To achieve this you must figure out what you want to test. Essentially you need to come up with a set of hypotheses of what feature or change to your website will result in a positive way toward a goal you've set. Goals can be purchases, newsletter sign ups or just clicking the next button on a form you have. The features you want to test can be large like the addition of a brand new features, or tiny like changing the title text on your page or anything in between.

Software developers are often good at figuring out what feature like aspects of an application can be tested this way. Do you get more sign ups with a single long form, or a wizard like 3 step set of short forms? Should the button go over here or over there? Then there are the simpler, harder to predict optimizations. Which headline is going to engage visitors more? What images and colors should be used? There is plenty to learn about your visitors, but since this kind of testing is less intuitive, developers probably shouldn't be the ones coming up with the hypotheses. You need marketers to make these decisions and you need to give them the tools to see the results, change the tests and continuously improve.

Client vs Server Side AB Testing

There are a number of frameworks that are implemented using JavaScript and will manipulate the DOM client side and provide their own web analytics. These include free hosted tools like Google Analytics's Content Experiments, commercial tools like Optimizely and visual website optimizer. None of these can be used server side for more advanced AB testing scenarios.

There are a number of tools available to help ASP.Net developers implement split testing in their applications but most are developer focused. They provide a way to articulate split tests in code.

Fairly Certain

FairlyCertain provides ASP.Net web forms tag and a straight forward API that could be use in code behind or directly from Razor views for MVC. Stats are stored to a flat file and a dashboard ASPX page is provided to show statistics.

Fairly Certain Code Examples:

<%@ Register TagPrefix="ab" Namespace="ExpatSoftware.Helpers.ABTesting.Controls" Assembly="Web" %>

<ab:Test TestName="sidebar_intro_text" runat=server> <ab:alternative Name="learn_how" runat="server">
It lets you quickly test changes to your website
to learn how real users respond to them.
</ab:alternative>
<ab:alternative Name="sell_more_stuff" runat="server">
It lets you test website changes against each other
so that you'll sell more stuff and make more money.
</ab:alternative>
</ab:Test>

MVC Style
<%
if (FairlyCertain.Test("bold_test", "bold", "standard") == "bold")
{%>
AB Testing is <b>freaking awesome</b>.
<% } else { %>
AB Testing is freaking awesome.
<% } %>

<% if (FairlyCertain.Test("show_disclaimer")){%>
(possible side effects may involve vomiting and hair loss)
<% } %>

 

There is a fork of this framework that fixes some bugs and adds a few feature called ABTesting.

ABTestMaster

ABTestMaster was built more for MVC and provides some Controller Action attributes to enable more declarative AB Testing. Similarly Goals can be configured on Actions as well via attribute. This framework supports both csv file and sql data stores and actually has a SaaS offerring that provides dashboards to show your metrics.

ABTestMaster Examples:

[SplitView("Version 1", "Payment", .1)]
public ActionResult Payment()
{
return View();
}

[SplitView("Version 2", "Payment", .9)]
public ActionResult Payment2()
{
return View();
}

[SplitGoal("Checkout")]
[
HttpPost]
public ActionResult Confirm(PaymentData data)
{
return View("Receipt");
}

 

mtelligent

Lastly, we have mtelligent, a server side framework for both ASP.Net Webforms and MVC that I built. This frameworks comes with a dashboard application where even non technical users can create and modify experiments, hypothesis, goals and cohorts. It then has a singleton façade API for accessing experiment parameters and registering conversions. All data is stored in a sql database and the metrics captured allow you to segment your users into cohorts who you can then create more specific experiments on or just personalize content to that cohort directly.

 

mtelligent Code Examples

Variables are associated with an experiment and can be rendered inline:
<h3 style="color:@Html.GetHypothesisVariable("Honey vs Vinegar", "TitleColor")">@Html.GetHypothesisVariable("Honey vs Vinegar", "Title")</h3>
<p class="lead">@Html.GetHypothesisVariable("Honey vs Vinegar", "Copy")</p>
<p align="center">
<img src="@Html.GetHypothesisVariable("Honey vs Vinegar", "Image Source")" />
</p>

The ExperimentManager is a singleton instance that can be used to get details about experiments and register conversions.

ExperimentManager.Current.AddConversion("Honey vs Vinegar");

if (ExperimentManager.Current.GetHypothesis("Honey vs Vinegar").SystemName == "Honey")
{
ViewBag.Message = "Thanks you so much you wonderful user.";
}
else
{
ViewBag.Message = "We knew you would do as you were told. Carry on worm.";
}

Html Helpers are provided to make it easier to use from Razor MVC views

@if (@Html.IsVisitorInCohort("Authenticated Users"))
{
<p>You are in the Authenticated User Cohort.</p>
}
else
{
<p>You are not in the Authenticated User Cohort.</p>
}

I created this framework, before I started getting into Sitecore, which also has it's own built in AB Testing framework. It is fully functional, however it's by no means complete.

Conclusion

AB Testing is a vital tool in measuring and improving your web applications. You have a number of choices both client and server side for implementing AB Tests. Do you know of any other frameworks or approaches? What do you think of the ones I mentioned? Let me know in the comments.

Thursday
Aug212014

Sitecore Development in the Real World - Page Template Development Process

I've noticed that new Sitecore developers often struggle at first to get productive. Between learning Sitecore, supporting tools and project specifics, it can be a bit overwhelming if you haven't done it before. If only there was a step by step guide that laid straight the path of what needed to be done. 

This post will serve to make it easier by laying out the steps needed to build out page templates. I recommend incorporating Team development for Sitecore and Glass Mapper as an ORM, though all steps could be completed without them. It does assume your following an organization structure similar to what I laid out in my "Sitecore Organizational Strategies" post.

A process to follow

Once you've organized your project and are ready to begin creating page templates, here are the steps:

 

 

Create Data Templates

You can't create anything without a data template. Getting to this point, you should have a good idea of what elements are on the page. Before you move forward creating fields, I do recommend ensuring the design of your template is optimal. This amounts to evaluating your templates along 3 considerations:

Field Types - Are you using the right field types?

I think we too often don't give this enough thought. For example, how do you normally choose between multilist, tree list and treelist ex? I love the simplicity of the multi list, but it can be difficult for content authors to know what they're choosing if the titles of items aren't clear. Treelists help by providing the folder structure to give authors context on what is being chosen, but configuring its data source is more complex. Treelist ex are just like treelists but more performant in the content editor as they'll only fetch records on demand.

Data Template Quick Tips

  • Don't create separate fields for link text and link url. The general link field has a text property which should be used and works correctly with the field renderer.
  • Set Rich Text fields data source to one of the HTML editor profiles.
  • Set the data source for File and Image fields to the path to a folder in the media library
  • Use the Xpath Query builder developer tool to test out multi list data source queries
  • Give developer friendly names to fields and Content Author friendly names to fields by specifying field titles.
  • Reorder groups and fields using the field sorting buttons on the builder options tab

 

Inheritance - Are you repeating yourself across templates?

The DRY principle applies to Sitecore development as well. If you think of each template as mapping to a class, getting the inheritance model correct is an essential task. Page templates should inherit from a base template which should include fields like meta tags and other common fields that span pages. At a page level, you need to really consider whether you can think of areas of your page really should be reusable components that could be applied to other pages. For example, say your template had a section for related content that showed 3 related articles to your content. This is the type of feature that I could imagine applying to multiple templates. To implement it I would create a separate template called "Related Articles" with a single mutlilist field. Then I would make my page template inherit from it. Remember when you take this approach, to set each group's Sortorder (show standard fields, in the appearance group) to ensure it gets positioned below the templates build tab defined fields.

Reuse - Are you making your templates as flexible as they need to be?

You should also consider your template from a content reuse perspective. Are you including things in your template that would be better off being linked to and stored in the data folder instead of the content tree? For example, say your page template had a section to display details about a coupon. You could include all the coupon details (image, link, text as needed) directly in the page template. But that would mean every page would require content authors to fill in all the details on every page they create. Sure you can set defaults in standard values, but it still becomes difficult to maintain if the coupon details change. A better solution might be to create a coupon template and a library for common coupons as a subfolder of the data folder in the content tree and use a drop link field to reference which coupon should be associated with your page. This makes it easier to ensure all pages use the same set of coupons and makes it trivial to update the coupons when they change.

 

Map Templates to Classes

Once you've created your data templates, the next step is to create Model classes which can be used to access data template values from your renderings and sublayouts. You may be tempted to bypass this step and just leverage the Sitecore API directly, but this in general leads to more difficult to maintain code. While you can roll your own model classes from scratch, here is where ORM frameworks like Glass Mapper really shine. They make it easy to represent your data templates as .Net classes and provide a framework for automatically mapping Sitecore Items to their corresponding classes.

If you use Team Development for Sitecore (TDS) you can even configure code generation templates to automatically generate glass Model classes for your data templates every time you sync with Sitecore. Leveraging this approach makes this step take next to no effort. After you create your data templates, sync your TDS project with Sitecore and your Model classes automatically get updated to include any changes you made in Sitecore.

One final tip on code generation. You still may find it would be useful to add additional properties or methods to Model classes. As generated classes get overwritten every time they are regenerated, the best approach for this is to leverage partial classes in .Net. Simply create another file with the partial class declaration and add field and methods as needed.

Create Renderings or Sublayouts

If you followed the previous two steps you have two things: a data repository in terms of a Sitecore data template to store CMS field values and a data access mechanism to inject their values into strongly typed model classes. At this point you have everything you need to actually create the presentation components to render those values. Here are some guidelines to follow as you go through the process of actually surfacing CMS values:

Sitecore.Context.Item Considered Harmful

Seriously, don't use Sitecore.Context.Item. If you do, your rendering will only work if it is the item being viewed. It won't support personalization or AB testing. The best practice always is to default to the Data Source parameter and only if it is not set, use the Sitecore.Context.Item. If you're not using glass, you should do this by declaring a property of type Sitecore.Data.Items.Item and setting it as early as possible. If you are using glass, this is automatically handled for you if your sublayout or view rendering inherits from the provided base classes.

Avoid Monolithic Views

Just because you could render everything in the data template in a single rendering or sublayout, doesn’t mean you should. Try to apply the single responsibility principle in that a sublayout or rendering should only be responsible for rendering a single thing. It also goes back to your data template design: If you broke out some fields into a separate template and then inherited it, then it would make sense that to render the inherited fields you should just load a separate rendering or sublayout. The rendering or sublayout could deal with just the inherited class and not the page template class and could be reused easily on other pages that inherited that template.

Static vs Dynamic Binding Considerations

Even though you segmented your sublayouts to make it easier to compose, doesn't mean you should connect everything together using Sitecore placeholders. They are a great tool and necessary if you're looking to support personalization or AB Testing. But ask yourself if it's overkill. One of my pet peeves is seeing placeholders used to specify header and footer controls. Doing that means every single template and page will have a presentation detail configuration to connect those controls to the pages. Will they ever really change? Probably not, which means it's a complete waste of effort that would have been better suited for having the header and footer statically bound in the parent layout.

Avoid Code Behind In your Sublayouts

This is probably more of a personal preference than an actual recommendation, but having spent the last several years implementing MVC solutions, I feel quite at home mixing C# with ascx markup. So much so that I avoid writing code in the code behind where possible. Glass maps the current Item to the Model property and makes it available directly in the ascx. I'll even use a foreach loop inline instead of a repeater. The code just feels cleaner. Or don't use sublayouts at all and use view and controller renderings instead,

Be consistent with Naming and Location

Don't let things get messy. There is a place for everything and for everything there is a place. Organize your folders accordingly in the web project and ensure similar organization is applied in the Layouts items you create within Sitecore.

Configure Standard Values

You're never done with your page template until you connect everything together and completely configure it for use. This amounts to creating and configuring the standard values for your data templates. Here are the main areas you want to hit:

  • Presentation Details
    The first thing you should do is configure your page templates presentation details on its standard values. Configure all the layouts, sublayouts, data source and other parameters as needed. Once that's setup, create an instance of your page in the content tree and test things out. Unless by some miracle you go things working without needing to debug, but that never happens. 
  • Defaults
    The basic of the basics. Set the default values and use $name, $date and $time as needed.
     
  • Insert Options
    How do you intend for you template to be created anyway? And think about not only your template but any referenced template. You may need to make sure the subfolder you created for those content items are configured properly as well.
  • Validation
    This is a topic that deserves its own blog pose, but if there is a checklist of things to do before you're done done, configuring validation belongs there. Understand the difference between error types and make sure you understand how much it's going to inconvenience your content authors.
  • Workflow
    Don't forget to configure workflow for your templates otherwise you'll need to explain how certain content was published without approval. Trust me, not a mistake you want to make.
  • Icons
    For some strange reason I take great pleasure in looking through all the icons available to find an appropriate image for my template. I haven't probably seen them all hundreds of times but damned if I remember whether that icon was in the business or people category. That probably tells you something about me. Anyway, they are recommended and content authors probably do appreciate them, so you're not done till they're configured.

 

Conclusion

If you're new to Sitecore development, following the 4 steps outlined in the post will help point you in the right direction toward becoming productive developing page templates.

Did I miss any steps? Let me know what you think in the comments?

Monday
Aug182014

Sitecore Development in the real world - Organizational Strategies

There is a bit of a delta between understanding Sitecore development after taking training and understanding Sitecore development after delivering a project. Training doesn't discuss design best practices and tradeoffs. Training doesn't cover the use of source control in managing Sitecore artifacts, nor how to best package and deploy them between environments. Training doesn't explain the use of tools like Team Development for Sitecore (TDS) or ORM's like Glass Mapper.

Having spent close to the last year figuring it all out while developing solutions with Sitecore and seeing other developers go through the same process I did to get to the point when things "really clicked," I thought to capture some of my thoughts and lessons learned.

Your Sitecore Instance isn't part of your Project

 In some training courses, you actually will put a visual studio project in the same place where your Sitecore instance is installed. This leads to a fundamental misunderstanding about what Sitecore is and its relationship to the code you write. Sitecore is a platform, and you need to think of it as such. If you think about it like it's only purpose is to house your specific solution, you are going to make design decisions that will make sure that's all that will be possible.

Instead, think that every Sitecore artifact you create and every piece of code that you write will have to be deployed to an environment you may not entirely control. An environment where other code, other sites created by other developers, may run at the same time. This will impact the way you develop with Sitecore from the way you structure your Visual Studio solution to the way you organize Sitecore items in the content tree and more.

Flexibility and Isolation in VS Solution Configuration

From a visual studio perspective, this means that you need to think about your web projects being deployed to a Sitecore instance via xcopy. This means all existing files in the target Sitecore instance will be over written every time you deploy and more to the point, other projects may overwrite your files every time they deploy. This kind of thinking is both limiting and freeing at the same time.

Namespace Gotcha 
Don’t use "Sitecore" in your project namespaces. This will just cause collisions when you need to use Sitecore's API's. Instead make your namespaces relevant to your domain. If it is Sitecore specific, use SC instead. Libraries like Glass do this.

It feels limiting in the defensive measures you will need to take to fend off such issues. Making extensive use of custom Sitecore configuration files and avoiding making changes to the web.config ensure your configuration doesn't get lost and even helps avoid issues when you have to upgrade or patch your Sitecore instance. Putting your layouts, sublayouts, renderings, scripts and css files in subfolders named with your project will ensure their paths are unique and avoids overwriting collisions when multiple solutions have common file names like "header.ascx."

Deployment Strategy
You have a few options to facilitate builds to your local Sitecore Instance. If you're using Team Development for Sitecore (TDS), it makes it extremely easy as it will automatically deploy your web project to your Sitecore instance root on build. If you don't have TDS, you'll need to configure web deploy and a post build step to automate deployment. With either approach, I also recommend "AttachTo," a visual studio extension that will allow you to debug your local IIS instance in one click.

It feels freeing, because by keeping all the pertinent code changes isolated from each other, you can actually break your Sitecore solution into many smaller projects, which can be deployed together or separately, giving you a level of code reuse you wouldn't have otherwise. If you have a feature, a pipeline component or even just a set of templates that have high reuse potential, you can segment each into their own set of projects and package them together (code and artifacts).

Avoiding Sitecore Item Collisions

Lucky for us it is pretty straight forward to avoid other projects overwriting your custom Sitecore items. Simply create subfolders with your project's name before adding any custom items. You need to do this everywhere you create Sitecore items.

In the Layout folders: Layouts, Sublayouts, Renderings, Placeholder Settings etc, you should first create a subfolder to contain your layout items. In the media library you should not only create a subfolder for you project, but ensure every Image field in related data templates use that folder's path in that field's data source. In the dictionary, you should not only create a subfolder for your project, but use a consistent prefix in all your dictionary keys to avoid collisions. Workflows and Validation Rules should also employ the same project folder tactic. This ensures that other projects or packages will not overwrite your Sitecore items unless they are explicitly trying to do so.

Organize your Content Tree

Content organization matters a lot in Sitecore development, and though it's straightforward to move things around after the fact, it is a pain if you have live content that has been indexed, and it's not worth the hassle of dealing with redirect rules if instead you just put the effort to get it right the first time.

By default when you install Sitecore, you simple see a home item under the Content node. I generally recommend deleting that node and creating a structure that makes sense and can scale easily to meet you requirements now and in the future. Sure, you'll need to adjust the Sites configuration ( use a custom include config file ), but it will be more convenient in the long run to manage.

I'll tend to create at least 3 folders for every site hosted in Sitecore I create as well as a Global folder to store content and configuration that impacts multiple sites.

The Home folder is what will be mapped in the sites configuration for the url binding used for the site. In general the nodes under this will have configured presentation details and accessible via url.

The Data folder is basically the root of the "content library" specific to that site. Every type of content you need selectable in your page templates should be organized into subfolders to make it easy to find and select content that is referenced by multiple pages. Your droplink, multilist and treelist fields should reference to paths here to make reusable content selectable. The data folder under global should similarly be used, but for content that can be selected and reused across sites.

Tradeoffs
There is a tradeoff when you put content in the data folder. It makes it a little more difficult to see the relationship between your page instances and the data it references. An alternative would be to make the data template instances children of the page instance itself, and this is a viable approach if and only if the content wont and never will be used by any other pages. Any benefit of association to the page template would immediately be lost if you needed to duplicate content or try to connect content under one page with another.

The configuration folder should hold Sitecore Items that control how the sites behave. In general, these are global static items that help configure things like layout and impact everything on the site. I'll often create a navigation subfolder here to help organize menu items for global navigation. The configuration folder under global should similarly be used but for configuration items that impact multiple sites.

Data Template Organizational Strategy

Besides creating a root folder for your custom data templates, you should give some thought to how to best organize your custom data templates. Every solution will have its own nuances, but I generally recommend a minimum of 4 key folders to give a logic separation to all the templates you will create:

  • Base Templates - here is where I put templates that are meant to be inherited. This includes not only base page templates, but templates that group common fields together for reuse. If you have more than a few base types, consider creating subfolders to make it easier to understand the organization.
  • Page Templates - here is where I define the templates that will be populated in the content tree and available as insert options.
  • Component Templates - here is where I define templates that drive data that is selectable by page templates. Instances of these templates will either be stored in a subfolder of the "data" folder or as a direct descendant of a page template.
  • Configuration Templates - here is where I define templates that control global site behavior. Avoid creating monolithic templates that encompass all possible settings. Instead try to group the configuration settings to logical groups and create instances of them in either the sites or global configuration folders.

You should also carefully consider how you name each template you create and the fields within them. If you are using an ORM like glass and using code generation to create classes, the names of your templates will become the names of your classes and the names of your fields will become the names of that classes members. Remember that at least with fields, you can give a developer friendly name to the field and a content author friendly name by leveraging the fields "title" property (you'll need to click on the field in the content tree instead of the builder tab of the template).

Conclusion

A lot of what I discussed in this post relates to just thinking through your solution and understanding the impacts of the decisions you make on the maintainability of your Sitecore project. In the end, understanding what you are trying to accomplish and how will it be executed before beginning to make design decisions will save time spent on rework.

What do you think? Share your tips for organizing Sitecore items in the comments.

Saturday
Jan252014

Announcing Mtelligent, My first attempt at open source.

Yes it's the name of the blog too, but I decided to name my current side project after it. And I decided to open source it. It's a A/B, Multivariant Testing and Cohort Analysis Framework for ASP.Net.

Instead of writing a blog post about it, I put all the detail into the read me file. You can find it on Git Hub here.

Over the next few weeks, I'll share more details about the internals of the framework, and hopefully make some progress toward closing some of the gaps I've already identified.

I'm releasing it now, because I think it's in good enough shape to share, and I actually wanted to get some feedback on it.