After Thought

Include vendor java scripts and style sheets in Rails 3.1

with 2 comments

In rails 3.1 any external javascript/stylesheet plugins (like tablesorter.js etc) that you use in your project are placed in vendor/assets directory. To include it in your rails application, add a vendor.js and vendor.css.scss file as shown below in your assets/javascripts and assets/stylesheets directory.

vendor.js:

// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require_tree .

vendor.css.scss:

/*
 * This is a manifest file that'll automatically include all the stylesheets available in this directory
 * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
 * the top of the compiled file, but it's generally better to create a new file per style scope.
 *= require_self
 *= require_tree .
*/

Now update your application.js and application.css.scss files to include vendor.js and vendor.css.scss respectively.

application.js:

// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require jquery
//= require jquery_ujs
//= require jquery-ui
//= require vendor
//= require_tree .

application.css.scss:

/*
 * This is a manifest file that'll automatically include all the stylesheets available in this directory
 * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
 * the top of the compiled file, but it's generally better to create a new file per style scope.
 *= require_self
 *= require lib
 *= require vendor
 *= require_tree .
*/

Now you can add files to your vendor assets directory and it will be picked up your rails app. Being a beginner in rails, I found this way very effective as I don’t have to update my application.js file once set. I would be interested to know if there is a better way of handling this.

Written by shashankshetty

November 6, 2011 at 8:20 pm

Track changes if form fields are changed on a web page

leave a comment »

A very useful feature to have on a web page is to remind the user of unsaved changes when he/she navigates away from the page without saving. A easy way to implement this is to set a global flag when a form is changed. Based on the flag value, pop a dialog when the user tries to navigate away from the web page without saving. This approach has several flaws, the obvious one being the usage of a global variable. It also fails if the user reverts his changes.

A better solution is to keep track of all the fields on the page and display the dialog only when there is a change. If we were to implement such a tracker, it  has to do the following.

Step 1: Scan all the form fields like textbox, textarea, checkbox etc to and store the existing values when the page loads.

Step 2: When the user makes a change, attach an event on all the links to pop a dialog when the user clicks on it without saving.

Step 3: Remove the attached events, if the user reverts those changes.

Here is an example web page.

If the user edits Name field and clicks on My Blog link without saving, it should pop a dialog as shown below:

Dialog in the example is dialog from jqueryui .

track.js keeps track of changes on the form and makes it possible to hook before change and after change events.  Assuming the form id is editForm, call trackchanges (with events you want to associate with) in your document ready function. Hook a dialog open event in the afterChangeAction and remove it in beforeChangeAction.

Below is the code listing.

jQuery(document).ready(function($) {
	var beforeChangeAction = function () {
        $("a[id^='lnk']").each(function() {
            $("#" + this.id).die('click');
        });

    };

    var afterChangeAction = function () {
        $("a[id^='lnk']").each(function() {
            var id = this.id;
            $("#" + id).live('click', function (e) {
                bindModalConfirmationDialogFor(id, redirectAction);
                openConfirmationDialog(e, "Any changes made will be lost. Are you sure you want to continue?");
            });
        });
    };

	$("#editForm").trackchanges({events: "change blur keyup mousedown", exclude: "exclude", beforeChangeAction: beforeChangeAction, afterChangeAction: afterChangeAction});
});

function bindModalConfirmationDialogFor(fld, action) {
    var dialogOpts = {
        modal: true,
        autoOpen: false,
        buttons: {
            'No': function() {
                $(this).dialog('close');
            },
            'Yes': function() {
                action(fld);
                $(this).dialog('close');
            }
        }
    };
    $("#dialog-confirm").dialog(dialogOpts);
}

function openConfirmationDialog(e, message) {
    e.preventDefault();
    $("#dialog-confirm").html("<p><span class='ui-icon ui-icon-alert' style='float:left; margin: 0 7px 20px 0;'></span>" + message + "</p>");
    $("#dialog-confirm").dialog("open");
}

function redirectAction(fld) {
    window.location.href = $("#"+fld).attr("href");
}

The problem with this approach is, it is obtrusive. The user is forced to click twice to navigate without saving. What would be really cool is if we can change the color of the banner on the page as the user edits any form field. Further, we can change the color back to the original state when the user reverts those changes without ever refreshing the page. It would be a seamless unobtrusive experience to users reminding them to save the page before navigating to a different page (Some of the websites out there already have this nifty feature). As an example let us change the color of the Banner shown below from blue to yellow when a user edits any form field.

If we were to implement such a tracker, it  has to do the following.

Step 1: Scan all the form fields like textbox, textarea, checkbox etc to and store the existing values when the page loads.

Step 2: When the user edits any field check if the new value of that field is different from the old value; change the color of the Banner from blue to yellow.

Step 3: Revert the color back to blue, if the user reverts those changes.

Below is the code listing:

jQuery(document).ready(function($) {
	var beforeChangeAction = function () {
        $("#buttons").css("background-color", "#bcd3e9");
        if ($("#changed-text").length > 0) {
            $("#changed-text").remove();
        }
    };

    var afterChangeAction = function () {
        $("#buttons").css("background-color", "#fbec88");
        if ($("#changed-text").length === 0) {
            $("#buttons").prepend("<span id='changed-text'>Changes not saved</span>");
        }
    };

	$("#editForm").trackchanges({events: "change blur keyup mousedown", exclude: "exclude", beforeChangeAction: beforeChangeAction, afterChangeAction: afterChangeAction});
});

You can choose to exclude a field or a form with a class. In the example above we excluded Exclude Changes field by adding class “exclude”.

<input type='text' id='exclude_changes' name='exclude_changes' class='exclude' value=''/>

FormChangeTracker project is available on github. You can find code for both the above examples along with track.js.

Written by shashankshetty

November 4, 2011 at 5:16 pm

Posted in javascript, Uncategorized

Tagged with ,

Continuous build and one click deployment

with 2 comments

Wouldn’t it be great if we deployed every time we checked in our code to reduce the stress associated with deployment. With the goal of making deployment as easy as possible, we at MVBA follow a process that seem to work really well for us. We tend to deploy every two two weeks to production and numerous times on test servers. Even though, we had one click deployment process, it was becoming tedious to manage multiple rake scripts for build and deployments. So we decided to clean up the process. After couple iterations we had a script in our hands that did exactly what we were looking for at the moment. We use albacore for our Rake tasks. If you are not using albacore for Rake tasks, you should definitely check it out.  It has a number of predefined tasks and it also makes adding generic or custom tasks easier. We use TeamCity as our continuous integration server. settings.yaml is used to maintain settings for different environments (developer, build, test and prod). Before compiling the solution, a task replaces all the config files with the current environment settings. For example, rake compile[‘developer’] will update the web.config and App.config files with developer.yaml file. You can also add settings related to deployment like dist_directory (directory where distributable is available), website path (like C:\WebSite) etc to the yaml file that the rake file can pick up when deploying. We divided our Build configuration into three categories -

  • Compile, run unit tests, run integration tests, run environment tests, create a temp distributable with version number that includes Web build, Windows Service build (we also deploy windows services as part of our deployment), TestBuild, DatabaseRebuild zip (scripts to rebuild the database), DatabaseUpdate zip (scripts to update the database) and Deployment Scripts zip (rake scripts that are needed to deploy the app).

  • Set up deployment files: Unzips the latest deployment zip file into a folder from where you can deploy

  • Deploy and run UI tests: Deploy the latest files onto web server, update your windows services, rebuild database (only on build and developer machines) and run the update scripts. Below is a rough draft of our deploy task. It can be re-factored to make it much cleaner.
class Deploy
include Albacore::Task

attr_accessor :website_base, :website_dir, :processor_dir, :tests_dir, :dist_directory, :web_dist, :service_dists, :tests_dist, :messages_dist, :messages_dir, :env, :database_build_type, :url, :rebuild_dir, :update_dir, :rebuild_dist, :update_dist

def initialize
@service_dists = []
@iis = "w3svc"
end

def execute
deploy
end

def deploy
puts "Deploying #{@web_dist}"

# stop iis
manage(@iis, 'stop', 1)

puts "Waiting for all the messages to be processed before stopping the service"
while(Dir["#{@messages_dir}/*.request"].count > 0)
sleep(2)
puts "."
end

# stop services
manage_windows_services(@service_dists, 'stop', 1)

# deploy website
deploy_app "#{@website_base}/#{@website_dir}", "#{@dist_directory}/#{@web_dist}"

# deploy services
@service_dists.each do |svc_name, svc_dist|
puts "Deploying #{svc_dist[:dist]}"
deploy_app "#{@website_base}/#{svc_dist[:dir]}", "#{@dist_directory}/#{svc_dist[:dist]}"
end

#deploy tests, messages and rebuild database
if (@database_build_type == "rebuild")
# deploy tests
puts "Deploying #{@tests_dist}"
deploy_app "#{@website_base}/#{@tests_dir}", "#{@dist_directory}/#{@tests_dist}"

# deploy rebuild
puts "Deploying #{@rebuild_dist}"
deploy_app "#{@website_base}/#{@rebuild_dir}", "#{@dist_directory}/#{@rebuild_dist}"

if File.exists? "#{@website_base}/#{@rebuild_dir}/rebuild_database.json"
puts "Rebuilding Database"
run_tests "#{@website_base}/#{@rebuild_dir}/#{@rebuild_dir}.dll"

# deploy messages
puts "Deploying #{@messages_dist}"
Dir.mkdir(@messages_dir) unless Dir.exists?(@messages_dir)
unzip("#{@website_base}/#{@rebuild_dir}/#{@messages_dist}", @messages_dir)
else
puts "There is no need to rebuild the database as there are no changes in the mapping files"
end
end
# deploy update
puts "Deploying #{@update_dist}"
deploy_app "#{@website_base}/#{@update_dir}", "#{@dist_directory}/#{@update_dist}"
puts "Updating Database"
run_tests "#{@website_base}/#{@update_dir}/#{@update_dir}.dll"

# start services
manage_windows_services(@service_dists, 'start', 1)

# start iis
manage(@iis, 'start', 1)
end

def deploy_app(working_dir, zip_file)
Dir.mkdir(working_dir) unless Dir.exists?(working_dir)
# unzip the files to working directory
unzip(zip_file, working_dir)
t = Dir.entries(working_dir).grep(/\.config\.#{@env}$/)
config_with_env = Dir.entries(working_dir).grep(/\.config\.#{@env}$/).first
return if (config_with_env.nil?)
config = File.basename(config_with_env, ".#{@env}")
FileUtils.mv "#{working_dir}/#{config_with_env}", "#{working_dir}/#{config}"
end

def manage(service, action, total_tries = 1, tries=0)
begin
sh "net #{action} #{service}"
rescue
tries +=1
puts "Caught error while performing #{action} on #{service}"
return if (tries == total_tries)
puts "Next try to #{action} the #{service} in 30 seconds"
sleep(30)
manage(action, tries) unless (tries >= total_tries)
end
end

def manage_windows_service(service_name, action, exe, tries)
if (action == 'start' && (`sc query #{service_name}`=~ /.*does not exist.*/) != nil)
sh "#{@website_base}/#{exe} install" unless (exe == nil)
end
manage(service_name, action, tries)
end

def manage_windows_services(services, action, tries)
services.each do |svc_name, svc_dist|
manage_windows_service(svc_name, action, "#{svc_dist[:dir]}/#{svc_dist[:exe]}", tries)
end
end

def unzip(file, dest)
unzip = Unzip.new
unzip.destination = dest
unzip.file = file
unzip.force = true
unzip.execute
end

def run_tests(assemblies)
nunit = NUnitTestRunner.new
nunit.command = get_nunit_x86
nunit.assemblies assemblies
nunit.execute
end
end

Before we deploy, we stop IIS, wait till all the services have finished processing before stopping all the windows services. We then deploy web and services. Tests are deployed only if the database_build_type is “rebuild” which is decided by :database_build_type in the settings file. :database_build_type is set to “rebuild” in developer and build, but it is set to “update” in test and prod (You don’t want to rebuild your production). We rebuild the database only if any of the fluent nHibernate mappings are changed in the latest check in. We then run the update scripts irrespective of what environment you are running on (We have an internal check that makes sure that a script is not run if it has already been run before). We then restart the windows services and finally IIS is restarted to complete the deployment process. Once the website is deployed, we run the UI tests and if all the UI tests are successful web dist, services dist, database update dist and deployment scripts dist are moved to its final destination folder from where you can deploy to test or prod. In our test and production servers we have a single batch script that calls 3 tasks

call rake set_up_deploy_files['test']
call rake copy_dist['test']
rake deploy['test']

The first line gets the latest deployment scripts or the deployment version you are looking for (you can pass in the version number as part of arguments to deploy a specific version like rake set_up_deploy_files[‘test’,’1.0.25.9217′]). Second line gets Web dist, services dist and database update dist. Third line deploys web, services and updates the database structure if needed. This has reduced considerable overhead in maintaining scripts for different needs ensuring that we are always using the right deployment script. Moreover, we now have just one RakeFile that is used to build and deploy making it easier to maintain. As a result, we are now more confident of our deployment process.

Written by shashankshetty

July 25, 2011 at 1:00 pm

Posted in ASP.net, ASP.net MVC, C#, Rake, Uncategorized

Tagged with , ,

Setting up Elmah in your application to log into FogBugz (Bug tracking system)

with 2 comments

ELMAH (Error Logging Modules and Handlers)  is a pluggable error logging system created by Atif Aziz that can be hooked onto an ASP.Net application. It is very straight forward to set up and gives options to log errors to several back-end storages including databases, in-memory, XML files and send out email notification. Scott Hanselman blogged about setting up ELMAH a while back. This is just an extension to that post to log the error to bug tracking system like FogBugz if you are using one.

Here is how to set up ELMAH on your ASP.Net MVC that logs an entry in FogBugz when there is an error. Fogbugz allows to log an error just by sending an email to the FogBugz connected mailbox. So we will set up ELMAH to send an email when there is an error.

Download ELMAH Core Binary and add it your library. Add a reference to Elmah.dll in your web project.

Add following entries to your web.config

<httpModules>
 ...
 <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah"/>
</httpModules>

<modules runAllManagedModulesForAllRequests="true">
 ...
 <add name="Elmah.ErrorMail" type="Elmah.ErrorMailModule" preCondition="managedHandler" />
 <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
 <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah"/>
</modules>
<handlers>
 <add name="Elmah" verb="POST,GET,HEAD" path="elmah.axd" preCondition="integratedMode" type="Elmah.ErrorLogPageFactory, Elmah"/>
</handlers>

<configSections>
 <sectionGroup name="elmah">
 <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
 </sectionGroup>
</configSections>
<elmah>
 <errorMail
 from="from@domain.com"
 to="to@domain.com"
 subject="<Application Name> Error"
 async="false"
 useSsl="true|false"
 smtpPort="60"
 userName="username"
 password="password"
 smtpServer="smtp.domain.com" />
</elmah>

That is it. ELMAH is set up and your application is ready to log any errors to FogBugz.

Before you proceed any further check if ELMAH is properly set up by going to http://localhost/elmah.axd. You should get the following screen.

You will still encounter yellow screen when there is an error. You can always redirect users to a nice looking page if there is an error by configuring your customErrors section as follows.

<customErrors mode="On" defaultRedirect="~/Error/SomeError">
 <error statusCode="404" redirect="~/Error/NotFound"/>
</customErrors>

Written by shashankshetty

January 13, 2011 at 11:23 am

Posted in ASP.net, ASP.net MVC, C#, Uncategorized

Tagged with ,

Eliminate if else using Smart Enums

with 4 comments

Open/Close principle (SOLID) states that class should be Open for extension and closed for modification. One way to achieve this is to eliminate conditional statements (if…else) in our code. Continuing our example of PlasticType from the Smart Enums post, let us say we have to write a method that has to decide the recycling program based on the plastic type.

public class Controller
{
	private readonly IRecycleManager _recycleManager;

	public Controller(IRecycleManager recycleManager)
	{
		_recycleManager = recycleManager;
	}

	public void Recycle(PlasticData plasticData)
	{
		if (plasticData.PlasticType == "pet")
		{
			_recycleManager.RecycleType1(plasticData);
		}
		if (plasticData.PlasticType == "hdpe")
		{
			_recycleManager.RecycleType2(plasticData);
		}
		if (plasticData.PlasticType == "pvc")
		{
			_recycleManager.RecycleType3(plasticData);
		}
	}
}

In the above example, we inject RecycleManager in to our Controller class that is responsible for recycling the plastic. The Recycle method calls the appropriate recycling program based on the plastic type supplied in the PlasticData model.

public class PlasticData
{
	public string PlasticType { get; set; }
	public decimal Weight { get; set; }
	public string Color { get; set; }
}

 

 

public interface IRecycleManager
{
	void RecycleType1(PlasticData plasticData);
	void RecycleType2(PlasticData plasticData);
	void RecycleType3(PlasticData plasticData);
}

 

 

public class RecycleManager : IRecycleManager
{
	public void RecycleType1(PlasticData plasticData)
	{
		// code here
	}

	public void RecycleType2(PlasticData plasticData)
	{
		// code here
	}

	public void RecycleType3(PlasticData plasticData)
	{
		// code here
	}
}

 

We see that Recycle method is filled with multiple conditional statements making it open to modification violating the Open/Close principle. The reason I say it is open to modification is because if we want to add recycling program to another type of plastic in future, we have to add another conditional statement and add another method to RecycleManager class. It also makes our tests less maintainable and brittle. If we can eliminate the conditional statements in the Recycle method, we have a good chance of making this code adhere to Open/Close principle. One effective way of doing this is to use Smart Enums. Read more about about Smart Enums here. Let us go ahead and add a behavior to PlasticType from the previous post as shown below.

public class PlasticType
{
	public static PlasticType PET = new PlasticType("pet", "Polyethylene Terephthalate",
													(recycleManager, plasticData) =>
													recycleManager.RecycleType1(plasticData));

	public static PlasticType HDPE = new PlasticType("hdpe", "High-density Polyethylene",
													 (recycleManager, plasticData) =>
													 recycleManager.RecycleType2(plasticData));

	public static PlasticType PVC = new PlasticType("pvc", "Polyvinyl Chloride", 
													(recycleManager, plasticData) =>
													recycleManager.RecycleType3(plasticData));

	private PlasticType(string key, string description, Action<IRecycleManager, PlasticData> recycle)
	{
		Key = key;
		Description = description;
		Recycle = recycle;
		NamedConstants.Add(key, this);
	}


	public static PlasticType GetFor(string key)
	{
		if (key == null)
		{
			return null;
		}

		PlasticType plasticType;
		NamedConstants.TryGetValue(key, out plasticType);
		return plasticType;
	}

	private static readonly Dictionary<string, PlasticType> NamedConstants = new Dictionary<string, PlasticType>();

	public string Key { get; set; }
	public string Description { get; set; }
	public Action<IRecycleManager, PlasticData> Recycle;
}

Now the plastic type also defines the recycling program that it is associated with. Now let us go ahead an modify our Recycle method in the Controller class.

public class Controller
{
	private readonly IRecycleManager _recycleManager;

	public Controller(IRecycleManager recycleManager)
	{
		_recycleManager = recycleManager;
	}

	public void Recycle(PlasticData plasticData)
	{
		PlasticType.GetFor(plasticData.PlasticType).Recycle(_recycleManager, plasticData);
	}
}

We see that the Recycle method is reduced to just one line and we got rid of all the conditional statements in the method. Now our Controller is open to extension and closed to modification. If were to add a new recycling program for another type of plastic in the future, we just have to add another method to RecycleManager class.

Written by shashankshetty

July 18, 2010 at 10:39 pm

Posted in C#, Uncategorized

Tagged with

Smart Enums

with 2 comments

Our team was introduced to Smart Enums by J.P. Boodhoo in one of his Nothin’ but .NET classes in Austin. An example of a simple Smart Enum is shown below.

public class PlasticType
{
	public static PlasticType PET = new PlasticType("pet");
	public static PlasticType HDPE = new PlasticType("hdpe");
	public static PlasticType PVC = new PlasticType("pvc");
	public static PlasticType LDPE = new PlasticType("ldpe");
	public static PlasticType PP = new PlasticType("pp");
	public static PlasticType PS = new PlasticType("ps");
	public static PlasticType Other = new PlasticType("other");

	private PlasticType(string key)
	{
		Key = key;
	}
	public string Key { get; set; }
}

PlasticType is a Smart Enum that is just a collection of static classes that can be used instead of a regular Enum in our code.

The example above describes different types of plastic  (source: Wikipedia) that are in use today. The numbers were devised by Plastic Bottle Institute of the Society of the Plastics Industry to make recycling easier (On a separate note, please recycle plastic after use. Let us make the world a better place to live for the next generation). You may ask what are PET, HDPE or PVC. To make the output more readable if the PlasticTypes were to be printed, let us add Description property as shown below.

public class PlasticType
{
	public static PlasticType PET = new PlasticType("pet", "Polyethylene Terephthalate");
	public static PlasticType HDPE = new PlasticType("hdpe", "High-density Polyethylene");
	public static PlasticType PVC = new PlasticType("pvc", "Polyvinyl Chloride");
	public static PlasticType LDPE = new PlasticType("ldpe", "Low-density Polyethylene");
	public static PlasticType PP = new PlasticType("pp", "Polypropylene");
	public static PlasticType PS = new PlasticType("ps", "Polystyrene");
	public static PlasticType Other = new PlasticType("other", "Others");

	private PlasticType(string key, string description)
	{
		Key = key;
		Description = description;
	}

	public string Key { get; set; }
	public string Description { get; set; }	
}

Smart Enum is already looking better than our regular Enum with the addition of Description property. We (myself and Clinton Sheppard) decided to take it a step further to make the usage of PlasticType easier in our code. So we added a GetFor(key) method as shown below.

public class PlasticType
{
	public static PlasticType PET = new PlasticType("pet", "Polyethylene Terephthalate");
	public static PlasticType HDPE = new PlasticType("hdpe", "High-density Polyethylene");
	public static PlasticType PVC = new PlasticType("pvc", "Polyvinyl Chloride");
	public static PlasticType LDPE = new PlasticType("ldpe", "Low-density Polyethylene");
	public static PlasticType PP = new PlasticType("pp", "Polypropylene");
	public static PlasticType PS = new PlasticType("ps", "Polystyrene");
	public static PlasticType Other = new PlasticType("other", "Others");

	private PlasticType(string key, string description)
	{
		Key = key;
		Description = description;
        NamedConstants.Add(key, this);
	}

	public static PlasticType GetFor(string key)
	{
		if (key == null)
		{
			return null;
		}

		PlasticType plasticType;
		NamedConstants.TryGetValue(key, out plasticType);
		return plasticType;
	}

	private static readonly Dictionary<string, PlasticType> NamedConstants = new Dictionary<string, PlasticType>();

	public string Key { get; set; }
	public string Description { get; set; }
}

GetFor(key) method converts a given key to the corresponding Smart Enum that can be used within our code. This limits the usage of strings only within the Smart Enum making renaming easier across the project. We have created a NamedConstant class (our name for Smart Enum) that is part of MvbaCore project that provides other helper methods like Equals, Values (enumerates over all the available PlasticTypes). Inheriting PlasticType from NamedConstant class would add all the helper methods automatically as shown below.

public class PlasticType : NamedConstant&lt;PlasticType&gt;
{
	public static PlasticType PET = new PlasticType("pet", "Polyethylene Terephthalate");
	public static PlasticType HDPE = new PlasticType("hdpe", "High-density Polyethylene");
	public static PlasticType PVC = new PlasticType("pvc", "Polyvinyl Chloride");
	public static PlasticType LDPE = new PlasticType("ldpe", "Low-density Polyethylene");
	public static PlasticType PP = new PlasticType("pp", "Polypropylene");
	public static PlasticType PS = new PlasticType("ps", "Polystyrene");
	public static PlasticType Other = new PlasticType("other", "Others");

	private PlasticType(string key, string description)
	{
		Key = key;
		Description = description;
		NamedConstants.Add(key, this);
	}

	public string Description { get; set; }
}

NamedConstant<T> makes creating Smart Enums easier, so we can focus on solving a business problem rather than building helper methods around it.

We can use Smart Enums not only to compare types like any other regular Enum but also add behavior to them making it smarter as described in this post.

You can create a Smart Enum as described above without a key or description. It depends on the problem you are trying to solve.

Written by shashankshetty

July 18, 2010 at 9:51 pm

Posted in C#, Uncategorized

Tagged with

Separation of concerns in Input Builders

with 2 comments

Note: We have moved on to use UIMaps as described in this blog post.

In our ASP.Net MVC application we want to separate the presentation code associated with HTML views from the act of binding data to HTML controls. We also want to get more of the UI control generation under test. This is our latest refinement towards those goals.

Html page for Address:

<div>
	<%= Model.Street
	.WithLabel("Street:")
	.Width("400px") %>
</div>
<div>
	<%= Model.City
	.WithLabel("City:") %>
</div>
<div>
	<%= Model.States
	.WithDefault("Select", "")
	.WithLabel("State:") %>
</div>
<div>
	<%= Model.ZipCode
	.WithLabel("Zip:")
	.Width("50px") %>-<%=
	Model.ZipPlus
	.Width("50px")%>
</div>

Here we only set css and other view related attributes. Now our HTML views are more concerned with how to display the data rather than what to display. Our model uses FluentWebControls to build the HTML inputs:

public class AddressInputProvider
{
	private readonly IRepository _repository;
	private Address _address;
	public AddressInputProvider(IRepository repository)
	{
		_repository = repository;
	}
	public void SetAddress(Address address)
	{
		_address = address;
	}
	public TextBoxData Street
	{
		get
		{
			return Fluent.TextBoxFor(_address, x => x.Street)
				.WithId((AddressModel address) => address.Street);
		}
	}
	public TextBoxData City
	{
		get
		{
			return Fluent.TextBoxFor(_address, x => x.City)
				.WithId((AddressModel address) => address.City);
		}
	}
	public DropDownListData States
	{
		get
		{
			return Fluent.DropDownListFor<State, AddressModel, string>(
				_repository.GetAll<State>(),
				state => state.Name,
				state => state.StateCode,
				x => x.State)
				.WithSelectedValue(() => _address.State);
		}
	}
	public TextBoxData ZipCode
	{
		get
		{
			return Fluent.TextBoxFor(_address, x => x.ZipCode)
				.WithId((AddressModel address) => address.ZipCode);
		}
	}

	public TextBoxData ZipPlus
	{
		get
		{
			return Fluent.TextBoxFor(_address, x => x.ZipPlus)
				.WithId((AddressModel address) => address.ZipPlus);
		}
	}
}

Now we can test the AddressInputBuilder, including the HTML control types and their properties in unit tests. We can eliminate mapping Domain objects to a Model before building the view. As FluentWebControls can bind against the model to get validation information, we only have to define the validation in one place – just in our Domain object or however we choose to do that. Other benefit is that it reduces our reliance on UI tests.

co-authored with Clinton Sheppard

Written by shashankshetty

March 5, 2010 at 4:56 pm

Posted in ASP.net MVC, C#, Uncategorized

Tagged with

Follow

Get every new post delivered to your Inbox.