Monthly Archives: July 2013

Extending your Tab – Your code, your plugins, part 3

So far we have covered a couple of different cases around how you could introduce custom Tabs into your system to gain a better understanding of how your system is operating (Creating a simple Tab – Your code, your plugins, part 1 and Creating a typical Tab – Your code, your plugins, part 2).

These tabs have been great to get up and running, but there is a chance that after a while, you may want to get more from your tab. Specifically, to extend its look and feel, and even to enhance the data that you are seeing inside the plugin.

New Concepts

  • Controlling the layout of your tab
  • Render compound/nested objects
  • Pivoting the root table layout

Use Case 3: Show the content of a shopping cart style component you might have written, using a custom layout

This time around, it’s the same use case that we saw in part 2 but we are adding on the fact that we would like to control the layout. We want to augment what we see so the layout is clear and the data representation matches our mental model, not just the object structure we happen to be dealing with. We want to control the headers/titles, order of the columns, prefixes/postfixes, styling, etc.

Existing code
I’m going to assume that you have seen how we got the base plugin up and running (if you haven’t, checkout part 2). The code in question is that of a typical Tab that returns an object collection.

public class TabCart : AspNetTab
{
    public override string Name
    {
        get { return "Cart"; }
    }

    public override object GetData(ITabContext context)
    {
        var cart = ShoppingCart.GetCart(context.GetHttpContext());
        var items = cart.GetCartDetials();

        return items;
    }

    public override RuntimeEvent ExecuteOn
    {
        get { return RuntimeEvent.EndSessionAccess; }
    }
}

Controlling the layout of your tab
Problem
By default when we return data from our ITab.GetData() method, the object contains no styling information. The rendering engine gets us pretty close to what we want to see but still doesn’t get us all the way there.

Solution
As it turns out Glimpse supports an extension model which favours Interface Segregation. This is just a fancy way of saying that extra functionality can be added to a Tab by making a Tab implement a given interface. This interface will augment the Tab and allow it to perform functions that a base Tab would would otherwise not be able to.

This is what we would like to see:
LayoutTab
You will note that it looks similar to the previous version of the tab we had, but its layout is different.

Sample Code
In this case we are going to add on the ITabLayout interface which requires that we add on a GetLayout() method. This method should return an object which describes how the data is to be structured or laid out. To help controlling these layouts we have a fluent API (which was created by Kristoffer Ahl of Fluent Security fame). The following gives you a basic taste of what you could do to achieve the above:

using Glimpse.AspNet.Extensibility; 
using Glimpse.AspNet.Extensions;
using Glimpse.Core.Extensibility;
using Glimpse.Core.Tab.Assist;
using MvcMusicStore.Models;

namespace MvcMusicStore.Framework
{
    public class TabCart : AspNetTab, ITabLayout
    {
        //NEW CODE
        private static readonly object Layout = TabLayout.Create()
                .Row(r =>
                {
                    r.Cell("{{albumTitle}} ({{albumId}})").AsKey().WithTitle("Album (Id)");
                    r.Cell("albumPrice").AlignRight().Prefix("$").WidthInPixels(100).WithTitle("Price");
                    r.Cell("genreName").WithTitle("Genre");
                    r.Cell("artistName").WithTitle("Artist");
                    r.Cell("count").Class("mono").WidthInPixels(70).WithTitle("Count");
                    r.Cell("dateCreated").WithTitle("Added");
                    r.Cell("recordId").WithTitle("Record Id");
                    r.Cell("cartId").WithTitle("Cart Id"); 
                }).Build();
        //NEW CODE
         
        public override string Name
        {
            get { return "Cart"; }
        }

        public override object GetData(ITabContext context)
        {
            var cart = ShoppingCart.GetCart(context.GetHttpContext());
            var items = cart.GetCartDetials();

            return items;
        }

        public override RuntimeEvent ExecuteOn
        {
            get { return RuntimeEvent.EndSessionAccess; }
        }

        //NEW CODE
        public object GetLayout()
        {
            return Layout;
        }
        //NEW CODE
    }
}

Render compound/nested objects
Problem
Having seen what the above has produced and the fact that we are starting to gain a real insight into how our shopping cart is operating, we have decided that we want to take things to a whole new level. We don’t want to repeat the shopping cart ID for every row (as we only want to see it once) and we want to see other aggregated information about out cart inside the tab (i.e. total value, etc).

Solution
As it turns out, Glimpse is capable of being able to render nested objects. When it detects a property which is a complex vs. primitive type, it recursively rips through that object and starts the rendering process again. For us, this means that we could construct an object that has the data we want listed above the table of cart entries.

Here is the next iteration of what we would like to see:
SummaryCartTab

Sample Code
Knowing that we want to show some more data, we can go back to the Tab and make some tweaks. Looking at the below, we can see that we have extended the model that we are returning and have added some more details to the meta data that’s returned.

public class TabCart : AspNetTab, ITabLayout
{
    //UPDATED CODE
    private static readonly object Layout = TabLayout.Create()
            .Cell("items", TabLayout.Create().Row(r =>
                {
                    r.Cell("{{albumTitle}} ({{albumId}})").AsKey().WithTitle("Album (Id)");
                    r.Cell("albumPrice").AlignRight().Prefix("$").WidthInPixels(100).WithTitle("Price");
                    r.Cell("genreName").WithTitle("Genre");
                    r.Cell("artistName").WithTitle("Artist");
                    r.Cell("count").Class("mono").WidthInPixels(70).WithTitle("Count");
                    r.Cell("dateCreated").WithTitle("Added");
                    r.Cell("recordId").WithTitle("Record Id");
                })).Build();
    //UPDATED CODE
     
    public override string Name
    {
        get { return "Cart"; }
    }

    public override object GetData(ITabContext context)
    {
        var httpContext = context.GetHttpContext();

        var cart = ShoppingCart.GetCart(httpContext);
        var items = cart.GetCartDetials();

        //UPDATED CODE
        var root = new
        {
            CartId = ShoppingCart.GetCartId(httpContext), 
            Total = items.Any() ? items.Sum(x => x.AlbumPrice).ToString() : "--", 
            Items = items
        };
        //UPDATED CODE

        return root;
    }

    public override RuntimeEvent ExecuteOn
    {
        get { return RuntimeEvent.EndSessionAccess; }
    }

    public object GetLayout()
    {
        return Layout;
    }
}

Pivoting the root table layout
Problem
As fate would have it, the above still isn’t good enough. We like the fact that the layout has progressed forward but, having seen other Tabs within Glimpse that are displayed more succinctly, we want to do more. In addition, the above rendering kinda squashes the Items table.

Solution
Another nice feature that the Glimpse rendering engine supports is the ability to pivot the way in which key/value Objects are displayed. When you do this, instead of seeing a key and value column, headings are rendered for the keys and the values are rendered in block under the corresponding header.

This time around, the below is what we would like to see:
PivotLayoutTab

Sample Code
Knowing that Glimpse can deal with nested objects and that we have the ability to pivot, we are going to group together the summary details. You will note that the pivot functionality is added by having our Tab implement the ILayoutControl interface. This is another example where we are using Interface Segregation to add functionality.

using System.Linq;
using Glimpse.AspNet.Extensibility; 
using Glimpse.AspNet.Extensions;
using Glimpse.Core.Extensibility;
using Glimpse.Core.Tab.Assist;
using MvcMusicStore.Models;

namespace MvcMusicStore.Framework
{
    public class TabCart : AspNetTab, ITabLayout, ILayoutControl
    {
        private static readonly object Layout = TabLayout.Create()
                .Cell("items", TabLayout.Create().Row(r =>
                    {
                        r.Cell("{{albumTitle}} ({{albumId}})").AsKey().WithTitle("Album (Id)");
                        r.Cell("albumPrice").AlignRight().Prefix("$").WidthInPixels(100).WithTitle("Price");
                        r.Cell("genreName").WithTitle("Genre");
                        r.Cell("artistName").WithTitle("Artist");
                        r.Cell("count").Class("mono").WidthInPixels(70).WithTitle("Count");
                        r.Cell("dateCreated").WithTitle("Added");
                        r.Cell("recordId").WithTitle("Record Id");
                    })).Build();
         
        public override string Name
        {
            get { return "Cart"; }
        }

        //NEW CODE
        public bool KeysHeadings
        {
            get { return true; }
        }
        //NEW CODE

        public override object GetData(ITabContext context)
        {
            var httpContext = context.GetHttpContext();

            var cart = ShoppingCart.GetCart(httpContext);
            var items = cart.GetCartDetials();

            var root = new
            {
                //UPDATED CODE
                Details = new {
                        CartId = ShoppingCart.GetCartId(httpContext), 
                        Total = items.Any() ? items.Sum(x => x.AlbumPrice).ToString() : "--"
                    },
                //UPDATED CODE
                Items = items
            };

            return root;
        }

        public override RuntimeEvent ExecuteOn
        {
            get { return RuntimeEvent.EndSessionAccess; }
        }

        public object GetLayout()
        {
            return Layout;
        }
    }
}

Working Sample
If you want to run what we have in the first sample, here is the link (94e9f99eda), or for the modified Tab, here is the link (e1fb2ca944), or for the finial pivoted Tab (e6d5eb04b2). As usual feel free to play around with it and modify.

Creating a typical Tab – Your code, your plugins, part 2

In the previous post – Creating a simple Tab – Your code, your plugins, part 1 – we looked at how we could take the data you store inside of a configuration object in your system, and create a tab which shows that data.

This time around we are going to be taking that a step further and looking at how we can customize how your Tab looks and how the data is displayed.

New Concepts

  • Creating a tab which renders a list of objects
  • Render data which is continuously changing
  • Controlling when a tab is executed

Use Case 2: Show the content of a shopping cart style of component you might have written

In this example, we have a shopping cart that our site uses to track items that a user is purchasing. When applying this to your own situation, think of the shopping cart as a generic representation of any component in your site that has transient state, based on the actions that the user performs.

For this case, our shopping cart is fairly simple and we wont be aiming to show more than the content of the shopping cart. In your own scenarios, you will likely be able to draw even more value than what we are showing here and start to apply some of the details/factors that exist in most real e-commerce solution (i.e. applied promotions, culture, discounts, markups, etc).

Existing code
Here is the general interface of the API that we are interacting with which manages the state of the cart:

public class ShoppingCart
{
    //...
    public static ShoppingCart GetCart(HttpContextBase context)
    {
        var cart = new ShoppingCart();
        cart.ShoppingCartId = cart.GetCartId(context); 
        return cart;
    }
     
    public string GetCartId(HttpContextBase context)
    {
        //Pulls cartId from Session
    }

    
    public List<CartItem> GetCartDetials()
    { 
        //Pull cart items from Cache
    }
    //...
}

public class CartItem
{ 
    public string AlbumTitle { get; set; }
    
    public decimal AlbumPrice { get; set; }
    
    public string GenreName { get; set; }
    
    public string ArtistName { get; set; }
    
    public int AlbumId { get; set; }
    
    public int Count { get; set; }
    
    public DateTime DateCreated { get; set; }
    
    public int RecordId { get; set; }
    
    public string CartId { get; set; }
}

Problem
Under normal circumstances, when developing this functionality within a site, its difficult to know whether the internal state of your model matches what you expect. Many different factors can contribute to what is stored, and and the cart summary page usually isn’t tailored to try to inform you about object state.

Solution
Assuming you have read part 1 in the series and had a crack at creating a basic Tab, you are becoming more adventurous and decide that a Glimpse Tab could hold the answer. You decide that to start with, you would like to show the basic content of the cart.

This is what we would like to see:
CartTab

Sample Code
As per usual, the runtime will detect any class in your solution that inherits from ITab. To produce what we have in mind with the awareness that you have access to the API described, the following is how we create your Tab:

public class TabCart : AspNetTab
{
    public override string Name
    {
        get { return "Cart"; }
    }

    public override object GetData(ITabContext context)
    {
        var cart = ShoppingCart.GetCart(context.GetHttpContext());
        var items = cart.GetCartDetials();

        return items;
    }

    public override RuntimeEvent ExecuteOn
    {
        get { return RuntimeEvent.EndSessionAccess; }
    }
}

In this example we have introduced a new concept with the override of ITab.ExecuteOn. This property dictates at what point in the request your Tab will be executed. By default, if you use the base classes Glimpse has, tabs run at the end of the request (RuntimeEvent.EndRequest). But in this case, we need to access a cartId which is stored in Session and in asp.net Session state is only available during the whole request. Hence we want to tap in at the last point which session is available.

Working Sample
If you are interested, here is the link (3a65662aea) to the MVC Music Store sample which has the commit for the above functionality. Feel free to play around with it and modify.

Creating a simple Tab – Your code, your plugins, part 1

When talking about Glimpse and extensions, a lot of attention goes towards the package ecosystem. This contains plugins which provide insights into the various frameworks you use. This makes it very easy to get up and running, and gain deep insights into your application and the systems it’s build on. The barrier to entry is very low and the reward is high, but you still aren’t getting the most out of Glimpse.

With very little effort, it’s possible to have this same instrumentation for the custom code in YOUR application. Image if your application could tell you its current state and give you a picture of logic its executed. What if people working on your project had a full diagnostics console which showed how the custom parts of your application is operating.

This would make dealing with parts of the system you hadn’t built much easier, and provide a much better onramp to those coming to your project for the first time. This woul bring the same level of understanding that you have come to expect from the standard Glimpse packages, right to the custom parts of your application.

For me some examples of what might be possible here are as follows:

  • Show the current state of a custom configuration object in your system
  • Show the content of a shopping cart style of component you might have written
  • Current state of a work flow that you have progressed through
  • Security checks that were conducted to build the page (what passed/failed)
  • etc…

This post is the first in a series of posts looking at your code and your plugins. We will be looking at how Glimpse can be leveraged to show specific details for your system. To start with, we are going to take the first example listed above and see what code it would take to make it work.

Use Case 1: Show the current state of a custom configuration object in your system

In this example, we have a database table called Configuration. The idea is that we want to store information like tax rates and markups in the database so we can update them easily. For this case, we are going to put aside that you could do this via the web.config and focus on the idea.

Existing code
Here is the code that makes up the static Configuration hook mentioned above:

public static class Configuration
{
    public static ConfigurationModel Current { get; set; }
}

public class ConfigurationModel
{
    public string Currency { get; set; }
    
    public double TaxRate { get; set; }
    
    public int DefaultCategory { get; set; }

    public double MarkupRate { get; set; }
}

Problem
Under normal circumstances, we would have very little idea about the state of these object. We could go to the database and query the table directly, but at that point we are assuming that this is the same as what the application has in memory.

In this case, because we aren’t happy with making this assumption and we want to know what we actually have in memory – the thought being:

  • Incase some exception caused only a partial load, or
  • we have some impedance mismatch between the database and the class, or
  • we have some sort of post processor which interprets/modifies the data before populating the object

With our current set of tools, we could set break points in our code so we can manually inspect the object or dump the object out to a log file. Unfortunately, these solutions are time consuming, cumbersome and less than ideal.

Solution
Having heard about Glimpse and used it in other projects, you decide to see if it has anything to offer. You find out that you can easily create Tabs to show arbitrary data. You decide that you would like to see a Tab which shows the state of your Configuration object.

This is what we would like to see:
CustomTab

Sample Code
Creating Tabs within Glimpse is incredibly easy. Once Glimpse has been added to your project, the runtime will detect any class in your solution that inherits from ITab. This process means that there is no configuration xml or registering of classes to get up and running.

This is the only code you would need to give Glimpse the data that you would like to populate your Tab:

public class TabConfiguration : AspNetTab 
{
    public override string Name
    {
        get { return "Setup"; }
    }

    public override object GetData(Glimpse.Core.Extensibility.ITabContext context)
    {
        return MvcMusicStore.Framework.Configuration.Current;
    }
}

Extra Code
For the sake of completeness, here is a trivial example of how you might manually go about populating the Configuration object in the first place (note, this is just one of many):

private void LoadConfiguration()
{
    var connectionString = ConfigurationManager.ConnectionStrings["MusicStoreEntities"];
    var factory = DbProviderFactories.GetFactory(connectionString.ProviderName);
    using (var connection = factory.CreateConnection())
    {
        connection.ConnectionString = connectionString.ConnectionString;
        connection.Open();

        MvcMusicStore.Framework.Configuration.Current = connection.Query<ConfigurationModel>("SELECT * FROM Configuration").First(); 
    }
}

protected void Application_Start()
{
    //...
    LoadConfiguration();
    //...
}

Working Sample
If you are interested, here is the link (4639363cff) to the MVC Music Store sample which has the commit for the above functionality. Feel free to play around with it and modify.

Glimpse 1.5.0 released

Well its that time again and we have another release ready to go. This one has a couple of cool new features and a whole bunch of fixes/improvements. Of late we have been pushing so hard to get new features out the door we decided that at a certain point “a chicken needs to come home to roost”.

Configuration Generator
This feature will hopefully make it more discoverable how to turn various features of Glimpse on or off, without having to go searching for it. It is also designed to help reduce the support burden we have by allowing people to discover and build the config they have. PLEASE NOTE, this only generates the config you need to copy over to your web.config, these changes aren’t made live to the system.

GlimpseConfigurationGenerator

From the highlighted sections, you can see how you can open the generator by clicking on the “Start Configuration Helper” button and then checking or unchecking the various checkboxes. What I really like about this helper is that its context sensitive to your system. As you can see, you are about to uncheck/check the tabs/policies/etc that are installed in your system.

If you have any thoughts about what else we can do here please let us know.

Host Tab Data Layout Change
Also in this release we have switched up the location of some of the data points in the Host tab. In previous releases there was some inconsistencies with where the data points where places in relation to the other HUD sections, hence the Host section has now been brought inline.

GlimpseHostTab

So don’t get caught out by these changes. Take the time to get reacquainted with the changes.

Popup Sections Spacing
The popup sections now have more space to play with. There were cases people would be caught out by having almost no width to play with and as a consequence have a very squashed data. This release sees a minimum amount allocated to each popup. This is a UX change we aren’t 100% sure about, so make sure you let us know if you have any thoughts.

Ajax Linking
This is a great pull request we accepted from Christophe Gijbels. In short it allows you to click on one of the requests in the Ajax mouseover and have Glimpse opened with that request already selected.

GlimpseAjaxLink

This is a really cool feature, so have a play around and let us know what you think. Expect to see more links like this appearing over time.

Release Notes

  • Glimpse.Core – Lightweight release 1.5.0
    • Added Configuration builder to Glimpse.axd
    • Allow HUD Ajax Request to click directly through to main HUD
    • Changed the position of data points in Host popup
    • Allow HUD mouse overs to take up more space if needed
    • Make HUD sections collapsible when mouse overs opened
    • Update to allow glimpse to work for request with a Content Type of “text/plain”
    • Minor fix where Host tab could throw exception if data was missing
    • Minor fix to remove some exceptions in the client that occurred in IE8
    • Minor fix to metadata which didn’t output in some rare cases
    • Minor optimization in the client for cases where tabs could rerender
    • Fixed UI glitch that would prevent Glimpse being opened once the HUD Ajax calls had been detected
    • Minor improvement to make tabs use explicit box-sizes
    • Minor fix to improve HUD processing of Ajax request that don’t have a Content Type
    • Minor fix for client error on Configuration Tab when connectionStrings is null
    • Update to glimpse.axd which showed Policies under the Inspectors heading
  • Glimpse.ADO – Flyweight release 1.4.1
    • Fix error calling what could occur when binding DataReader’s to controls
    • Made GlimpseDbDataAdapter public to allow it to be accessed in different cases
  • Glimpse.EF* – Middleweight release 1.3.1
    • Majorish feature where we now support DbSpatialServices (EF5 .NET45 and EF6)
  • Glimpse.ASP.NET – Bantamweight release 1.3.1
    • Remove styling from URL column in Route tab
    • Added defensive try/catch around some system call in Environment call to be more defensive

Other news
Also in this release cycle we had our Regular Town Hall where we discussed all the project happenings coming up. We try to have these once a month, so if you would like to join the next one, keep an eye out on the blog.

We also accepted Christophe Gijbels to the ranks of the Glimpse committers. If you get a chance, take a moment to congratulate him and welcome him to the team.

Special Thanks
In terms of pull requests, I have to say a big thanks to:

And for the great issue reports, I want to thank:

Release Details
Well I think that is a wrap. For a full list of changes, issues and commits see below:

Parting words
Let us know if you have any thoughts or comments and I hope you enjoy this release.