"Working on the complexity that is always indirectly implied by simpilicity."

Simple Log in Amplify

As many people know rails has a simple logger inside the framework which really only outputs to the console. This comes in handy to see SQL output of your models, benchmarks and other cool little things inside the rails framework. 

Of course people in the .Net realm tend to want things that scale and yet simplified for them.  So output only to the console won't fly in larger projects.  There is log4net, but you don't want to have it tightly coupled to the system, this caused an issue to one project that I worked on while still working for Vivus Software before I came OSC. Where the framework and project both expected a certain version of Log4Net and there was no wrapper to put something else in place if need be. 

So the solution was to Build small static Log class, that takes a list of an interface ILog (different than log4net's) and then use that interface to build a wrapper around using the root Logger for log4net. This way people can still build their own loggers for amplify or build their own Appenders for Log4Net and amplify will still be able to leverage this. Of course there is also conditional symbol LOG4NET in amplify if you wish to build the amplify framework without any dependency on the log4net library.  

To me this is more like the Console.WriteLine that is needed for a class library vs what log4net is typically used for in an application where it also spits out the type information of a class. To help with the testing using mock object is the which uses the Lambda expression and gets rid of all that crazy record/playback junk to which I loathe. 

		public void Test()
		{
			var id = 0;
			Log.Sql("SELECT * FROM test");
			Log.Debug("The id is  {0} ", id);
		}		


		[It(Order = 2), Should("have a debug method that logs debug statements")]
		public void Debug()
		{
			var calls = new List<string>();
			var log = CreateMockLog();

			log.Expect(o => o.Debug(Moq.It.IsAny<string>(), null))
				.Callback((string s, object[] args) => { calls.Add(s); });

			calls.Count.ShouldBe(0);

			// simple and easily called from anywhere... and lets you 
			// format messages like 
			// Log.Debug("item id: {0} could not be saved", id);
			Log.Debug("debug", null);
			calls.Count.ShouldBe(1);
			calls.Contains("debug").ShouldBeTrue();

			Log.IsDebug = false;

			// its turned off so it won't output anything
			Log.Debug("debug", null);
			calls.Count.ShouldBe(1);

			Log.IsDebug = true;
		}

		private static Mock<ILog> CreateMockLog()
		{
			var log = new Mock<ILog>();
			Log.Loggers.Clear();
			Log.Loggers.Add(log.Object);
			return log;
		}

Though keep in mind, one of the more trickier things to mock is the use of a method with the "params" keyword. I had to used a callback that you pass in an object array (object[] args) and I passed in null, otherwise the callback would not fire, and Lambdas don't allow the "params" keyword. Once that was figured out, I was able to use moq successfully to test the Log class successfully.

Labels: , , , , , , ,

Gallio and Mb-Unit release v3.0.4

You can find many of the new features over at Jeff Brown's blog.  Some of the cooler features of note is the integration with the visual studio testing system, wrapping testing for exceptions into a delegate, and data store called Ambient, in which you can store state for your tests.  I've integrated this into amplify which is again, now on GitHub. I did run into a few lil issues when setting this up though....

The biggest issue was finding out how to turn a project into a test project in order to get the visual studio integration working for Gallio. Basically you need to make modifications to the .csproj file and add an XML element of <ProjectTypeGuids> into the first <PropertyGroup> of the file. 

<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};
{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 
</ProjectTypeGuids>

After this, I could get the Gallio tests showing up in visual studio.

Gallio-Test-View

Now this coupled with really helps with the testing process especially since with certain versions of Visual Studio, Test Driven .Net will let you run visual studio's code coverage with Gallio.

CodeCoverage

Again, one of the cooler features was the improvements to Mb-Unit's Asserts (which did change the API, but its all good, cause I wrap the Asserts that I use in BDD style mixin Methods, so I just need to change them in one place). The one really change of note would be adding Assert.Throw and Assert.Throw<T>, to which you can wrap throwing an exception into a delegate.

WrappingExceptions

All in all nice improvements to both Gallio and Mb-Unit, which are now incorporated into amplify.

Labels: , , , , , , , , ,

Moving Amplify To Git Hub...

After getting updated, and getting much of my dev stuff on a USB drive for many good reasons and exploring using on windows.  I've decided git is stable enough to use, a good reason for me to use the command line more often and git fits more of my development needs. 

The biggest factor is that it is its own repo on my local, so when I'm working disconnected, i can still make commits to my local drive.  The second biggest reason is the amazing merging, branching and speed at which it works. 

Also with the code move, I'm starting over per-say, by slowly adding code only after tests are written for them, this process will put me back some, but when down, I should have a decent size test suite for amplify which is sorely needed. 

Of course another while doing all of this I also moved a huge portion of my dev environment to my USB drive to recently having to develop on a desktop not provided for.  I was even able to get e-texteditor on a USB drive without using something like thinstall/thinapp

As for moving to GitHub, well there are not that many hosted repositories for git at this point and you really can't beat free.

Getting the |DataDirectory| folder in C sharp

One of the cool things about sql server express's connection strings is the ability to relatively path database files for sql express databases. Often one might see something in the connection string that looks like " AttachDbFilename=|DataDirectory|\file.mdf ". The pipes and DataDirectory is parsed to equate to a relative data directory file path that changes depending on the application type (i.e. an application deployed by click once, vs an application that is installed to a folder).

However sometimes you might want to know where that data directory is, like in my case, I want to take a look at a connection string and attempt to create a database based off the connection string in order to speed up development using database migrations. This could also come in handy deploying small applications.

So after some research via google, I found this gem on msdn about where the DataDirectory is, though not guaranteed to be accurate. It basically follows 3 simple hierarchal rules. If the AppDomain.SetData method has been used, then the directory is set there. Otherwise if the application is a click once application the data directory is set or its in the application root folder.

So below is what the code might possibly look like in order to get the applications data directory no matter what type of application it is and it gives you ability to set the DataDirectory as well.

namespace Amplify
{
 using System;
 using System.Configuration;
 using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.Deployment;
 using System.Deployment.Application;
 using System.IO;
 using System.Text;
 using System.Reflection;
 using System.Web;

 public class ApplicationContext {

  private static NameValueContext s_properties;


  public static bool IsWebsite { get; internal set; }

  static ApplicationContext()
  {
   IsWebsite = (System.Web.HttpContext.Current != null);
   
  }

  public static string DataDirectory
  {
   get
   {
    string dataDirectory = GetProperty("DataDirectory") as string;

    if (string.IsNullOrEmpty(dataDirectory))
    {
     dataDirectory = AppDomain
      .CurrentDomain.GetData("DataDirectory") as string;

     if (dataDirectory == null)
     {
      if (ApplicationDeployment.IsNetworkDeployed)
       dataDirectory = ApplicationDeployment
        .CurrentDeployment.DataDirectory;
      else
       dataDirectory = Path.GetDirectoryName(
        Assembly.GetExecutingAssembly()
         .GetName().CodeBase);
     }
     dataDirectory = dataDirectory.Replace(@"file:\", "");
     SetProperty("DataDirectory", dataDirectory);
    }
    return dataDirectory;
   }
   set
   {
    string dataDirectory = GetProperty("DataDirectory") as string;
    value = value.Replace(@"file:\", value);

    if (!System.IO.Directory.Exists(dataDirectory))
     System.IO.Directory.CreateDirectory(dataDirectory);

    SetProperty("DataDirectory", value);
    AppDomain.CurrentDomain.SetData("DataDirectory", value);
   }
  }


  public static object GetProperty(string propertyName)
  {
   if (IsWebsite)
    return HttpContext.Current.Application[propertyName];
   else
    return Context[propertyName];
  }

  
  public static void SetProperty(string propertyName, object value)
  {
   if (IsWebsite)
    HttpContext.Current.Application[propertyName] = value;
   else
    Context[propertyName] = value;
  }

  private static NameValueContext Context
  {
   get
   {
    if (s_properties == null)
     s_properties = new NameValueContext();
    return s_properties;
   }
  }
 }
}

Labels: , , ,

Amplify-Fusion, JavaScript libraries compatibility layer

As one might start guessing, I tend to jump around languages, projects, and clients (web/windows client not people).  After writing custom code for , , , , , it becomes a pain to remember which library has what and to port work you've previously done into another library.  Its one thing if it porting code from one language to another, but to have port code cause of the library/framework.... its a pain that isn't needed. 

Granted the downside of writing a compatibility layer is extra code that could be duplicated else where or writing a smaller set of routines that are less in what other full blown libraries. However most people have broadband these days and the browsers have this cool thing called caching. Not to mention YUI has a great compressor and there are ways to include multiple files on the server rather than on the browser. 

So I've begun to embark on a long journey of making JavaScript widgets build on a compatibility layer of different libraries. The layer it self will take some time, testing, and constant refinement while trying to keep the layer small.  However I believe this is needed, esp as a consultant that works on various projects and everyone has their favorite library to use... 

you can find the beginnings of the code at http://code.google.com/p/amplify-fusion/.

Labels: , , ,

Amplify's WCF Twitter API Client Library v0.2

After some playing around with WCF and Flickr, I decided to move everything internally for the twitter library to use WCF with it's ServiceContract.  It was more of a pain that initially thought it would be. WCF is amazingly customizable but still has a couple of downfalls thanks to REST and people not writing their API for all technologies. 

The biggest issue is that Twitter throws an Http 403 unknown exception when you call a page and something is incorrect in the url.  Well with WCF, it throws a System.ServiceModel.Security.MessageSecurityException, due to the url returning a status code other than 200, and it wraps the inner exceptions inside of that.

I'm sure there is a better way to get around this, but each proxy call is now wrapped by a try/catch in the client class methods in order to check for the MessageSecurityException, then it checks the inner exception and determines if twitter sent back a response error message. If it does the library now throws a twitter exception with error information, otherwise you would only see the WCF exception that would mask the real issue. 

Also I renamed the "Service" classes to "Client" seeing that is the proper term for them.  The tests are now updated as well to reflect the changes.  The tests are a good way of seeing of how to use the library. Last but not least, methods now return arrays instead of lists in order to be more REST friendly.

using Amplify.Twitter;
using System.Security;

// ... stuff

public void Test() 
{
	try {
		string temp = "password";
	
		Twitter.SetCredentials("Username", temp);

		StatusClient client = Twitter.CreateStatusClient();
 		// or StatusClient client = new StatusClient("Username", temp);

        	Status[] tweets = service.GetUserTimeline();
	} catch (TwitterException ex) {
		Log.Write(ex.ToString()); // write the twitter rest error. 
	}

}

Labels: , , , , , , , ,

Use C# to determine where and what version of java is installed

While writing an MsBuild Task that is calling a jar file, there was a need to find out information on the java runtime on the local machine, otherwise when I do publish the task, the developer would be required to install a specific version of java in a specific location.  That kind of thing just doesn't fly if you're writing on a 64 bit machine due to having dual "Program Files" folders, one for 64 bit programs, the other for x86.  Not only that, it really increases complexity to the end user/developer who might want to use the MsBuild Task.

So after poking around in the registry, I found out there was information about the Java Runtime, including which version is currently the default, and where the file path is. I wrote a singleton class that looks at the registry and grabs that information.  Though usually singleton generally only checks to see if the instance is null and then instantiates it, the code below checks the registry during the "Get" method.

namespace Amplify.Tasks
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Text;

	using Microsoft.Win32;

	public class JavaInfo
	{
		private static JavaInfo instance = null;

		protected JavaInfo()
		{
			this.CurrentVersion = "";
			this.Installed = false;
			this.Path = "";
		}

		public string CurrentVersion { get; set; }
		
		public bool Installed { get; set; }
		
		public string Path { get; set; }


		public static JavaInfo Get()
		{
			if (instance == null)
			{
				RegistryKey key = Registry.LocalMachine;
				JavaInfo info = new JavaInfo();

				string location = @"Software\JavaSoft\Java Runtime Environment";
				RegistryKey environment = key.OpenSubKey(location);
				if (environment != null)
				{

					info.Installed = true;
					object value = environment.GetValue("CurrentVersion");
					if (value != null)
					{
						info.CurrentVersion = value.ToString();
						RegistryKey currentVersion = environment.OpenSubKey(info.CurrentVersion);
						if (currentVersion != null)
						{
							value = currentVersion.GetValue("JavaHome");

							if (value != null)
								info.Path = value.ToString();
						}
					}
				}

				instance = info;
			}

			return instance;
		}
	}
}

Labels: , , ,