Useful Moq Extension Method
24 November 09 04:03 PM | MattManela | 0 Comments   

I have been working with ASP .NET MVC and I use the Moq mocking library to help test the code I write.   Often in ASP MVC anonymous objects are passed around as function arguments.  This is especially common in calls to RouteUrl.  Since I want to be able to test this and verify that it is called correctly I wrote a handy extension method to make it easier called AsMatch.

 

Given a call to generate a Url like this:

var url = Url.RouteUrl("Show", new { projectName="matt" });

 

You can write a setup on your Mock of the Url helper using a similar syntax:

UrlHelperMock
.Setup(x => x.RouteUrl("Show",new{ projectName="matt"}.AsMatch()))
.Return("someUrlToTest");

 

Here is the code that defines the AsMatch extension method:

    public static class ObjectMoqExtensions
    {
        public static object AsMatch(this object @object)
        {
            return Match<object>.Create(testObject => DoObjectsMatch(@object, testObject));
        }
        private static bool DoObjectsMatch(object object1, object object2)
        {
            var props1 = ToDictionary(object1);
            var props2 = ToDictionary(object2);
            var query = from prop1 in props1
                        join prop2 in props2 on prop1.Key equals prop2.Key
                        select prop1.Value.Equals(prop2.Value);
            return query.Count(x => x) == Math.Max(props1.Count(), props2.Count());
        }
        public static Dictionary<string, object> ToDictionary(object @object)
        {
            var dictionary = new Dictionary<string, object>();
            var properties = TypeDescriptor.GetProperties(@object);
            foreach (PropertyDescriptor property in properties)
                dictionary.Add(property.Name, property.GetValue(@object));
            return dictionary;
        }
    }
Filed under: , ,
Converting RTF to HTML
28 September 09 11:28 AM | MattManela | 0 Comments   

Have you ever had the desire to convert some RTF text into HTML? Probably not. But if you do, then you are in luck! I recently had the need to do this conversion and after some searching found out a way to do it by enhancing a sample distributed in the MSDN library.  The sample is called: XAML to HTML Conversion Demo

The sample has code which converts HTML to and from a XAML Flow Document.  But this doesn’t make things easier until you realize that there is a way to convert RTF to XAML easily. The key is to use System.Windows.Controls.RichTextBox which can load RTF from a stream and save it as XAML.  This conversion is shown below:

        private static string ConvertRtfToXaml(string rtfText)
        {
            var richTextBox = new RichTextBox();
            if (string.IsNullOrEmpty(rtfText)) return "";
            var textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
            using (var rtfMemoryStream = new MemoryStream())
            {
                using (var rtfStreamWriter = new StreamWriter(rtfMemoryStream))
                {
                    rtfStreamWriter.Write(rtfText);
                    rtfStreamWriter.Flush();
                    rtfMemoryStream.Seek(0, SeekOrigin.Begin);
                    textRange.Load(rtfMemoryStream, DataFormats.Rtf);
                }
            }
            using (var rtfMemoryStream = new MemoryStream())
            {
                textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
                textRange.Save(rtfMemoryStream, DataFormats.Xaml);
                rtfMemoryStream.Seek(0, SeekOrigin.Begin);
                using (var rtfStreamReader = new StreamReader(rtfMemoryStream))
                {
                    return rtfStreamReader.ReadToEnd();
                }
            }
        }

With this code we have all we need to convert RTF to HTML. I modified the sample to add this RTF To XAML conversation and then I run that XAML through HTML converter which results in the HTML text. I added an interface to these conversion utilities and converted the sample into a library so that I would be able to use it from other projects.  Here is the interface:

 public interface IMarkupConverter
    {
        string ConvertXamlToHtml(string xamlText);
        string ConvertHtmlToXaml(string htmlText);
        string ConvertRtfToHtml(string rtfText);
    }
    public class MarkupConverter : IMarkupConverter
    {
        public string ConvertXamlToHtml(string xamlText)
        {
            return HtmlFromXamlConverter.ConvertXamlToHtml(xamlText, false);
        }
        public string ConvertHtmlToXaml(string htmlText)
        {
            return HtmlToXamlConverter.ConvertHtmlToXaml(htmlText, true);
        }
        public string ConvertRtfToHtml(string rtfText)
        {
            return RtfToHtmlConverter.ConvertRtfToHtml(rtfText);
        }
    }

With this I am now able to convert from RTF to HTML.  However, there is one catch - the conversion uses the RichTextBox WPF control which requires a single threaded apartment (STA).  Therefore in order to run your code that calls the ConvertRtfToHtml function, it must also be running in a STA.  If you can’t have your program run in a STA then you must create a new STA thread to run the conversion. Like this:

MarkupConverter markupConverter = new MarkupConverter();
private string ConvertRtfToHtml(string rtfText)
{
   var thread = new Thread(ConvertRtfInSTAThread);
   var threadData = new ConvertRtfThreadData { RtfText = rtfText };
   thread.SetApartmentState(ApartmentState.STA);
   thread.Start(threadData);
   thread.Join();
   return threadData.HtmlText;
}
private void ConvertRtfInSTAThread(object rtf)
{
   var threadData = rtf as ConvertRtfThreadData;
   threadData.HtmlText = markupConverter.ConvertRtfToHtml(threadData.RtfText);
}
        
private class ConvertRtfThreadData
{
   public string RtfText { get; set; }
   public string HtmlText { get; set; }
}

Here is the zip contain the code for the Markup converter: MarkupConverter.zip

Filed under: , , , ,
Attachment(s): MarkupConverter.zip
I finally got fed up with Enum.Parse
24 July 09 09:02 AM | MattManela | 3 Comments   

I don’t know why I didn’t do this long ago, but I am done writing this:

var val = (SomeEnum)Enum.Parse(typeof(SomeEnum),”someString”);

I have typed this too many times and it annoys me. 

I wrote a small extension method on the string type to make this better:

public static class StringExtensions
{
    public static T ToEnum<T>(this string @string)
    {
        return (T)Enum.Parse(typeof (T), @string);
    }
}

With this I can now write the previous line as:

var val = "someString".ToEnum<SomeEnum>()

It is a bit shorter and I think much more readable.
Filed under: ,
DRY and Unit Tests don’t mix well
12 July 09 08:08 PM | MattManela | 0 Comments   

When reading source code, I sometimes come across unappealing code(sometimes even my own).  However, there is one kind of “bad code” I see quite frequently.  It is a set of unit tests which have had the DRY (Don't Repeat Yourself) principle unduly forced upon them.  DRY is the idea that you shouldn’t have to write the same code over and over; abstract it in a function or a class and just call the abstraction.  This is all well and good in most cases, but I find it misguided when applied to a test case. 

A test case should be like a simple short story.  The characters are introduced, action/conflict occurs and then resolution takes place (sometimes with a moral).  This (kind of) corresponds to the 3 steps of a unit test: arrange, act and assert.  You arrange and setup what you need for your test to run, you perform the action that you are trying to test and then you assert the results. The issue I find is that a coder, in attempting to apply DRY to his test cases, will abstract away all of the arrange step into a function often with a name like SetupExpectations or just Setup.  This goes against the point of a test case. A test case needs to be concise and tell me everything I need to know about how that one bit of functionality works. I don’t want to jump around the test class trying to read one test case. To me, this is like reading a book that says, “If you want to learn about the characters in this book please open this other book.”  This doesn’t stop you from understanding the test, but it slows you down…and is just annoying.

 

That is why I will come out and say do not apply DRY haphazardly to test cases.

Filed under: ,
How to teach your girlfriend Hexadecimal?
06 May 09 04:49 PM | MattManela | 1 Comments   

It is an age old question: How do you teach your girlfriend hex?

I encountered this problem when I was a web developer in college for a late night student activities program aptly named  Late Nite Binghamton.  My co-worker and girlfriend Mallory was a graduate assistant for the program and had many ideas for how to make the website better and more engaging.  While working with her she would often see me typing in the hexadecimal color codes in css such as:

color: #F24A7D

As I am inept with what color look good (I often say that I never learnt colors) I would often ask her for help with picking out colors and I decided that if she was able to give me these colors in their hex color codes it would make us more efficient :) I sat her down and described to her how they work but after talking for a bit I realized that although she understood everything that I said she wouldn’t gain a feel for what hex numbers related to what colors just from hearing me talk.

In order to help her gain this feel I created a simple game called The Mallory Color Guessing Game.  The game is a webpage which shows you a large colored box and it is your job to guess what the hex color code is. 

image

 

After you guess it tells you how close you were to the actual color:

image

 

This game was a hit! Mallory loved it and she would compete with me to see who could guess the color the best.  Pretty quickly she gained a feel for what the hex digits meant and what color they represented. And that is how I taught my girlfriend hex!

 

NOTE: Mallory consistently gets over 80% at the Msllory Color Guessing Game. 

Filed under:
Snippet Designer in April’s MSDN Magazine!
14 April 09 04:33 PM | MattManela | 1 Comments   

I am excited to announce that the Snippet Designer is featured in the April issue of MSDN Magazine. 

msdnMag

 

 

 

 

 

 

 

 

 

It is featured in the Toolbox column where they highlight useful tools and blogs. Here is a snippet of what it says:

Creating Code Snippets is a lot easier when using Snippet Designer (version 1.1), a free, open-source Add-In for Visual Studio 2008 for creating and editing Code Snippets directly within the IDE. Once you install it, creating a new Code Snippet is as easy as going to the File menu and creating a new Code Snippet File.

 

That is so cool! If you are interested go and download the Snippet Designer from the Codeplex website and give me more feedback.

 

Also, in the same article my friend Sara Ford had her blog featured.  When I found out the Snippet Designer and Sara’s blog were in the magazine I quickly emailed to ask Sara if she knew about it.  She already did and also had an extra copy of the magazine for me!

A functional take on console program loop in F#
13 April 09 09:51 PM | MattManela | 1 Comments   

Often when learning a new technology I start with a simple console application in which the program is run in a loop it continues to prompt you for more input until you give some command like quit or exit or whatever you choose:

Enter input: someInput
someOutput
Enter input: otherInput
someoutPut
Enter input: quit
Thanks! Come again!

The code for this is in an imperative language is usually something like:

   1: while(true)
   2: {
   3:     Console.Write("\nEnter input:");
   4:     var line = Console.ReadLine();
   5:     if(line == "quit") break;
   6:     
   7:     doSomething(line);
   8: }
   9:  
  10: Console.WriteLine("Thanks! Come Again");

 

While reading some F# samples, I saw some code doing essentially the same thing which looked something like:

   1: Console.Write "\nEnter input:"
   2: let mutable input = Console.ReadLine()
   3: while input <> "quit"
   4:     do
   5:     
   6:     if input <> "quit" 
   7:     then
   8:         doSomething input
   9:         Console.Write "\nEnter input:" 
  10:         input <- Console.ReadLine()

 

Now this just feels dirty! In a functional language explicit loop constructs like a while loop feel wrong.  I different way of doing this which is more functional is to create an infinite list of actions.  Where each action represents one iteration of any of the above loops. Then you just execute each action until some condition is met (like seeing the input “quit”).

   1: let action = fun _ ->
   2:     Console.Write "\nEnter input: "
   3:     Console.ReadLine()
   4:  
   5: let readlines = Seq.init_infinite (fun _ -> action())
   6:     
   7: let run item = if item = "quit" 
   8:                 then Some(item) 
   9:                 else
  10:                     parse item 
  11:                     None
  12:  
  13: Seq.first run readlines |> ignore
  14: Console.WriteLine "Thanks! Come Again"

This code makes use of the Seq.init_infinite which create a infinite sequence and Seq.first which enumerates the sequence until the passed in function returns Some.

Filed under: ,
Synchronizing Scrollbars using JQuery
19 March 09 01:29 PM | MattManela | 1 Comments   

I just wrote this simple plugin for JQuery which lets you synchronize the scroll bars of any collection of elements.  This lets you move the scrollbar of one div it have the scrollbars’ of the rest of the divs move the same exact amount.

Here is the code:

   1: jQuery.fn.synchronizeScroll = function() {
   2:  
   3:            var elements = this;
   4:            if (elements.length <= 1) return;
   5:  
   6:            elements.scroll(
   7:            function() {
   8:                var left = $(this).scrollLeft();
   9:                var top = $(this).scrollTop();
  10:                elements.each(
  11:                function() {
  12:                    if ($(this).scrollLeft() != left) $(this).scrollLeft(left);
  13:                    if ($(this).scrollTop() != top) $(this).scrollTop(top);
  14:                }
  15:                );
  16:            });
  17:        }

 

Using this is SUPER simple.  Lets say you have several divs defined as:

<div class=”scrollDiv” style=”overflow:auto;”> .. some large content</div>

To synchronize the scrollbars’ on all divs with the class scrollDiv all you need to write is:

$(“.scrollDiv”).synchronizeScroll();

Prime Factorization using Unfold in Haskell
17 March 09 06:53 AM | MattManela | 1 Comments   

I randomly yesterday started thinking about the unfoldr function in Haskell while working out at the gym (how nerdy is that, I am lifting iron but thinking of functional programming). Unfoldr take a single and an unfolding function and turns it into a list (the opposite of fold).  At the gym I was thinking about an application where I can use this and I decided that when I got home I would use it to write a prime factorization function.  This is a method that when given a number returns the list of its prime factors.

It was easy to write the only part I am not pleased about is the code I used to deal with tuples.  It seems clumsy and I am still looking for a way to clean that up.

One note: The code below references a list of prime numbers called primes , which is not shown.

Here is the code:

   1: primeFactors x = unfoldr findFactor x
   2:                  where
   3:                    first (a,b,c) = a
   4:                    findFactor 1 = Nothing
   5:                    findFactor b = (\(_,d,p)-> Just (p, d))
   6:                                   $ head $ filter ((==0).first) 
   7:                                   $  map (\p -> (b `mod` p, b `div` p, p))  primes

This function will take any number which is greater than 1 and return a list of its prime factors.  But don’t take my word for it, I wrote a quickcheck property to ensure the prime factors multiply back to the original number:

   1: prop_factors num =  num > 1 ==> num == (foldr1 (*) $ primeFactors num)

When running quickcheck on this property you see the following: 

quickCheck prop_factors
OK, passed 100 tests.

 

Filed under: ,
My xUnit.net Visual Studio Code Snippets
02 March 09 03:58 PM | MattManela | 1 Comments   

The xUnit .Net codeplex page lists one useful Visual Studio code snippet for creating a Fact.  As you can tell I am fairly fond of code snippets so I created a few more which I use when writing xUnit.net facts. These are one line snippets that I find very convenient when writing my assertions.

Below is a table which shows the shortcut you use to access the snippet and the code the snippet generates

Shortcut Snippet
ae Assert.Equal($expected$,$actual$)
ane Assert.NotEqual($expected$,$actual$)
an Assert.Null($actual$)
ann Assert.NotNull($actual$)
at Assert.True($actual$)
af Assert.False($actual$)

I have included a zip containing these snippets.   Enjoy!

Filed under: , ,
Attachment(s): xUnitSnippets.zip
Code Assumptions
01 February 09 08:13 PM | MattManela | 1 Comments   

My co-workers and I recently came across a piece of code which exposed some assumptions we had about the “correct” behavior of two functions; these assumptions turned out to be false.  The code dealt with determining if the IP of a  request coming into to a website matches a certain range of IP address. The range of IP address was defined in a config file and could look something like this:

156.27.1.2, 156.*.*.*, 127.0.0.*

Where the * acts as a wild card and each comma-separated IP format is matched against the incoming IP address. The code’s intended behavior is that specifying no IP address formats in the config would mean that no IP addresses would match the format.  We noticed that this wasn’t holding true.  We went to examine the code that was performing the matching (which none of us had written and sadly has NO test cases around it).  The code boiled down to something like this:

 

   1: bool DoesIPMatchIPFormat(string incomingIpAddress, string ipFormatsFromConfig)
   2: {
   3:     foreach(var ipFormat in ipFormatsFromConfig.Split(','))
   4:     {
   5:         Regex re = ConvertFormatIntoRegex(ipFormat);
   6:         if(rs.Match(incomingIpAddress).Success) return true;
   7:     }
   8: }

 

Where ipFormatsFromConfig is the ip range of formats shown above separated by “,” or an empty string if no formats are given. On first glance we all were confused by why this didn’t work and then as we looked into it we realized we had made some incorrect assumptions.

First, we assumed that “”.Split(‘,’) would return a zero element array.  This assumption is false. It returns a 1 element array containing the empty string. 

After we learned this we encountered our second assumption.  We assumed that a regular expression whose pattern was an empty string would not match anything. (ConvertFormatIntoRegex returned just that).  This assumption is also false.  After learning the true behavior, it makes sense why it behaves this way.  (A DFA with only one state must accept every input. )

Our assumptions about the “correct” behavior of functions reflect similar mistakes made by all programmers; assuming that you don’t make these kinds of assumptions is a very dangerous way to program and reinforces the importance of unit testing.

Filed under: ,
Snippet Designer 1.1 Released
05 January 09 06:11 PM | MattManela | 3 Comments   

I just released Snippet Designer 1.1.  This is not a major release but just some bug fixes and often requested changes to make the snippet designer more useful.

 

Some of the most notable changes are:

  1. Languages Service are turned OFF by default now.  Since I was unable to figure out a way to host the C# and VB language services in the snippet editor without causing fake errors in projects I decided to give the users an option to turn them on or off.  If you are ok with the fake error you can turn this back on but since it really annoyed me I have it off.  I plan in a future release to provide some basic color coding outside of the language service so you can at least have color coded code.
  2. Many more aspects of the plug in are now configurable through the options menu under Snippet Designer.  you can now set the location of the snippet index, set preferences for which snippet languages you would like to appear in the snippet explorer.
  3. Several bug fixes to how the highlighting of replacements works and performance improvements with it also.

 

Hopefully these changes will make it much more useful so please download it and try it out here

 

 

The future…

My plans for the future are still up in the air but some of the things I would like to add are:

  1. More bug fixes and more feature enhancements suggested by many helpful users on the Snippet Designer Codeplex page.
  2. Ability to color code snippets without using the language service.  I have a few options here I have been looking at.
  3. Create a website to host snippets and allow you to consume and publish from Visual Studio to this website.

I don’t know how much time I will have to commit to any of these but hopefully I will be able to get more work done on this.

 

Thanks!

Inline Regular Expression Options
01 January 09 12:10 PM | MattManela | 1 Comments   

I was using attributes from the System.ComponentModel.DataAnnotations   namespace for model validation.  This namespace includes a few very useful validation attributes such as

    1. Required Attribute – Validates the field has a value
    2. Range Attribute – Validates the field is within a given range
    3. RegularExpression Attribute – Validates the field matches a given regular expression.

 

The regular expression attribute is very useful since you can describe exactly what format you want a string property to be in.  While using this though I ran into a problem.  The attribute doesn’t let you specify RegexOptions.  This was an issue for me since I wanted to use the regex to validate that the users input was between 5 and 200 characters long , so I had attribute that property as such:

   1: [RegularExpression(@".{5,200}")]
   2: public string Text{get;set;}

 

However this doesn’t work since by default the wildcard . does not match new lines (which I am allowing in the input).  The way to fix this is to specify the RegexOptions.SingleLine option to either the Regex constructor or Match function.  The problem is I have no way of doing that here, and there is no argument on the attribute constructor to specify those options.  I was considering overriding the attribute to create one that allows specifying the attribute but then I stumbled upon this:

Regular Expression Options

You are able to specify the regex option inside of the regular expression text! (which I thought was a huge discovery until my co-worker said he knew this all along but never let me know!). 

So I just changed the expression to look like this:

   1: [RegularExpression(@"(?s).{5,200}")]
   2: public string Text{get; set;}

The (?s) is the inline regex option definition so say I want this in SingleLine mode! And now the validation works the way I wanted!

Filed under: ,
Model Binder
15 December 08 10:45 PM | MattManela | 0 Comments   

When working with a multi-tier application I often find myself converting from one of the tiers object models to my own similar (but often different) model.  I often write code that would set one by one each property from a web tier object to my object.  In order to make this easier I wrote this object binder.  It is a fake deep copy in a way.  You tell it what destination type you want to build and give it a source object and it will go through each property (matching by names) and checks if it can copy from that property from the source  to the destination.  If it can’t it will call itself again on that object to try to copy its properties.

 

Here is the code:

   1: using System;
   2: using System.Reflection;
   3: using System.Collections;
   4:  
   5: namespace Binder
   6: {
   7:     public class ObjectBinder
   8:     {
   9:         /// <summary>
  10:         /// Copies one object to an instance of a different type.
  11:         /// 
  12:         /// Assumptions:
  13:         /// Object which implements IList'1 also implements IList 
  14:         /// Object which implements IDictionary'2 also implements IDictionary 
  15:         /// </summary>
  16:         /// <typeparam name="T"></typeparam>
  17:         /// <param name="source">The source.</param>
  18:         /// <returns></returns>
  19:         public static T Copy<T>(object source) where T : new()
  20:         {
  21:             return (T)Copy(source, typeof(T));
  22:         }
  23:  
  24:  
  25:         private static object Copy(object source, Type destinationType)
  26:         {
  27:             if (source == null) throw new ArgumentNullException("source", "source must not be null");
  28:  
  29:             var destination = Activator.CreateInstance(destinationType);
  30:  
  31:             var sourceProperties = source.GetType().GetProperties();
  32:             foreach (var sourceProp in sourceProperties)
  33:             {
  34:                 var sourcePropType = sourceProp.PropertyType;
  35:                 var destinationProp = destination.GetType().GetProperty(sourceProp.Name);
  36:  
  37:                 if (sourceProp.CanRead && destinationProp != null && destinationProp.CanWrite)
  38:                 {
  39:                     var destinationPropType = destinationProp.PropertyType;
  40:                     Type sourcePropInterfaceType = null;
  41:                     Type destinationPropInterfaceType = null;
  42:  
  43:                     // Skip indexer properties
  44:                     if (sourceProp.GetIndexParameters().Length > 0) continue;
  45:                     
  46:                     var sourceValue = sourceProp.GetValue(source, null);
  47:                     if (sourceValue == null) continue;
  48:  
  49:                     object destinationValue = null;
  50:                     if (destinationPropType.IsAssignableFrom(sourcePropType))
  51:                     {
  52:                         destinationValue = sourceValue;
  53:                     }
  54:                     else if (
  55:                         (sourcePropInterfaceType = sourcePropType.GetInterface("IList`1", true)) != null &&
  56:                         (destinationPropInterfaceType = destinationPropType.GetInterface("IList`1", true)) != null)
  57:                     {
  58:  
  59:                         var sourceArgType = sourcePropInterfaceType.GetGenericArguments()[0];
  60:                         var destArgType = destinationPropInterfaceType.GetGenericArguments()[0];
  61:                         bool isAssignable = destArgType.IsAssignableFrom(sourceArgType);
  62:  
  63:                         IList listCopy = null;
  64:  
  65:                         if (destinationPropType.IsArray)
  66:                         {
  67:                             // If array there must be a one argument constructor
  68:                             listCopy = (IList)Activator.CreateInstance(destinationPropType, ((IList)sourceValue).Count);
  69:                         }
  70:                         else
  71:                         {
  72:                             // If not an array require a parameterless constructor
  73:                             if (destinationPropType.GetConstructor(Type.EmptyTypes) == null) continue;
  74:                             listCopy = (IList)Activator.CreateInstance(destinationPropType);
  75:                         }
  76:  
  77:                         int index = 0;
  78:                         foreach (var item in ((IList)sourceValue))
  79:                         {
  80:                             var itemCopy = item;
  81:                             if (!isAssignable)
  82:                                 itemCopy = Copy(item, destArgType);
  83:  
  84:                             if (destinationPropType.IsArray)
  85:                                 listCopy[index] = itemCopy;
  86:                             else
  87:                                 listCopy.Add(itemCopy);
  88:  
  89:                             index++;
  90:                         }
  91:  
  92:                         destinationValue = listCopy;
  93:                     }
  94:                     else if (
  95:                         (sourcePropInterfaceType = sourcePropType.GetInterface("IDictionary`2", true)) != null &&
  96:                         (destinationPropInterfaceType = destinationPropType.GetInterface("IDictionary`2", true)) != null)
  97:                     {
  98:  
  99:                         var sourceArgTypes = sourcePropInterfaceType.GetGenericArguments();
 100:                         var destArgTypes = destinationPropInterfaceType.GetGenericArguments();
 101:                         bool isKeyAssignable = destArgTypes[0].IsAssignableFrom(sourceArgTypes[0]);
 102:                         bool isValueAssignable = destArgTypes[1].IsAssignableFrom(sourceArgTypes[1]);
 103:  
 104:                         var defaultConstructor = destinationPropType.GetConstructor(Type.EmptyTypes);
 105:                         if (defaultConstructor == null) continue;
 106:                         var dictionaryCopy = Activator.CreateInstance(destinationPropType) as IDictionary;
 107:  
 108:                         foreach (DictionaryEntry pair in ((IDictionary)sourceValue))
 109:                         {
 110:                             var keyCopy = pair.Key;
 111:                             var valueCopy = pair.Value;
 112:                             if (!isKeyAssignable)
 113:                                 keyCopy = Copy(keyCopy, destArgTypes[0]);
 114:                             if (!isValueAssignable)
 115:                                 valueCopy = Copy(valueCopy, destArgTypes[1]);
 116:  
 117:                             dictionaryCopy.Add(keyCopy, valueCopy);
 118:                         }
 119:  
 120:                         destinationValue = dictionaryCopy;
 121:                     }
 122:                     else
 123:                     {
 124:                         destinationValue = Copy(sourceValue, destinationPropType);
 125:                     }
 126:  
 127:  
 128:                     destinationProp.SetValue(destination, destinationValue, null);
 129:  
 130:                 }
 131:             }
 132:  
 133:  
 134:             return destination;
 135:         }
 136:     }
 137: }

And the tests using XUnit.Net:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using Xunit;
   6: using System.Collections.ObjectModel;
   7:  
   8: namespace Binder
   9: {
  10:     public class ObjectBinderFacts
  11:     {
  12:         public class The_Copy_Method
  13:         {
  14:  
  15:             [Fact]
  16:             public void throws_if_source_is_null()
  17:             {
  18:  
  19:                 Exception ex = Record.Exception(() => ObjectBinder.Copy<Destination>(null));
  20:  
  21:                 Assert.IsType<ArgumentNullException>(ex);
  22:                 Assert.Equal("source", ((ArgumentNullException)ex).ParamName);
  23:             }
  24:  
  25:             [Fact]
  26:             public void will_copy_assignable_scalar_property()
  27:             {
  28:                 var source = new Source();
  29:  
  30:                 var res = ObjectBinder.Copy<Destination>(source);
  31:  
  32:                 Assert.Equal(source.A, res.A);
  33:                 Assert.Equal(source.B, res.B);
  34:                 Assert.Equal(source.C, res.C);
  35:                 Assert.Equal(source.D, res.D);
  36:                 Assert.Equal(source.E, res.E);
  37:  
  38:             }
  39:  
  40:             [Fact]
  41:             public void will_copy_assignable_generic_list_property()
  42:             {
  43:                 var source = new Source();
  44:  
  45:                 var res = ObjectBinder.Copy<Destination>(source);
  46:  
  47:                 Assert.Equal(source.F.Count, res.F.Count);
  48:                 Assert.Equal(source.F[0], res.F[0]);
  49:                 Assert.Equal(source.F[1], res.F[1]);
  50:  
  51:             }
  52:  
  53:             [Fact]
  54:             public void will_copy_assignable_array_property()
  55:             {
  56:                 var source = new Source();
  57:  
  58:                 var res = ObjectBinder.Copy<Destination>(source);
  59:  
  60:                 Assert.Equal(source.N.Length, res.N.Length);
  61:                 Assert.Equal(source.N[0], res.N[0]);
  62:                 Assert.Equal(source.N[1], res.N[1]);
  63:  
  64:             }
  65:  
  66:             [Fact]
  67:             public void will_copy_unassignable_array_property()
  68:             {
  69:                 var source = new Source();
  70:  
  71:                 var res = ObjectBinder.Copy<Destination>(source);
  72:  
  73:                 Assert.Equal(source.O.Length, res.O.Length);
  74:                 Assert.Equal(source.O[0].Value, res.O[0].Value);
  75:                 Assert.Equal(source.O[1].Value, res.O[1].Value);
  76:  
  77:             }
  78:  
  79:             [Fact]
  80:             public void will_copy_assignable_generic_dictionary_property()
  81:             {
  82:                 var source = new Source();
  83:  
  84:                 var res = ObjectBinder.Copy<Destination>(source);
  85:  
  86:                 Assert.Equal(source.J.Count, res.J.Count);
  87:                 Assert.Equal(source.J["a"], res.J["a"]);
  88:                 Assert.Equal(source.J["b"], res.J["b"]);
  89:  
  90:             }
  91:  
  92:             [Fact]
  93:             public void will_copy_unassignable_generic_dictionary_with_assignable_keys_and_values()
  94:             {
  95:                 var source = new Source();
  96:  
  97:                 var res = ObjectBinder.Copy<Destination>(source);
  98:  
  99:                 Assert.Equal(source.K.Count, res.K.Count);
 100:                 Assert.Equal(source.K["a"], res.K["a"]);
 101:                 Assert.Equal(source.K["b"], res.K["b"]);
 102:  
 103:             }
 104:  
 105:             [Fact]
 106:             public void will_copy_unassignable_generic_dictionary_with_assignable_keys_and_unassignable_values()
 107:             {
 108:                 var source = new Source();
 109:  
 110:                 var res = ObjectBinder.Copy<Destination>(source);
 111:  
 112:                 Assert.Equal(source.L.Count, res.L.Count);
 113:                 Assert.Equal(source.L["a"].Value, res.L["a"].Value);
 114:                 Assert.Equal(source.L["b"].Value, res.L["b"].Value);
 115:  
 116:             }
 117:  
 118:             [Fact]
 119:             public void will_copy_unassignable_generic_dictionary_with_unassignable_keys_and_unassignable_values()
 120:             {
 121:                 var source = new Source();
 122:  
 123:                 var res = ObjectBinder.Copy<Destination>(source);
 124:  
 125:                 Assert.Equal(source.M.Count, res.M.Count);
 126:                 Assert.Equal(source.M[new SomeObject { Value = 1 }].Value, res.M[new OtherObject { Value = 1 }].Value);
 127:                 Assert.Equal(source.M[new SomeObject { Value = 2 }].Value, res.M[new OtherObject { Value = 2 }].Value);
 128:  
 129:             }
 130:  
 131:             [Fact]
 132:             public void will_copy_unassignable_generic_list_with_assignable_values()
 133:             {
 134:                 var source = new Source();
 135:  
 136:                 var res = ObjectBinder.Copy<Destination>(source);
 137:  
 138:                 Assert.Equal(source.G.Count, res.G.Count);
 139:                 Assert.Equal(source.G[0], res.G[0]);
 140:                 Assert.Equal(source.G[1], res.G[1]);
 141:  
 142:             }
 143:  
 144:             [Fact]
 145:             public void will_copy_unassignable_object_property()
 146:             {
 147:                 var source = new Source();
 148:  
 149:                 var res = ObjectBinder.Copy<Destination>(source);
 150:  
 151:                 Assert.Equal(source.H.Value, res.H.Value);
 152:  
 153:             }
 154:  
 155:             [Fact]
 156:             public void will_copy_unassignable_generic_list_properties()
 157:             {
 158:                 var source = new Source();
 159:  
 160:                 var res = ObjectBinder.Copy<Destination>(source);
 161:  
 162:                 Assert.Equal(source.I.Count, res.I.Count);
 163:                 Assert.Equal(source.I[0].Value, res.I[0].Value);
 164:                 Assert.Equal(source.I[1].Value, res.I[1].Value);
 165:  
 166:             }
 167:  
 168:             [Fact]
 169:             public void will_return_object_with_default_values_if_no_properties_match()
 170:             {
 171:                 var source = new Source();
 172:  
 173:                 var res = ObjectBinder.Copy<SomeObject>(source);
 174:  
 175:                 Assert.Equal(0, res.Value);
 176:             }
 177:  
 178:             class SomeObject
 179:             {
 180:                 public int Value { get; set; }
 181:  
 182:                 public override int GetHashCode()
 183:                 {
 184:                     return Value;
 185:                 }
 186:  
 187:                 public override bool Equals(object obj)
 188:                 {
 189:                     var some = obj as SomeObject;
 190:                     if (obj == null) return false;
 191:                     return some.Value == Value;
 192:                 }
 193:             }
 194:  
 195:             class OtherObject
 196:             {
 197:                 public int Value { get; set; }
 198:  
 199:                 public override int GetHashCode()
 200:                 {
 201:                     return Value;
 202:                 }
 203:  
 204:                 public override bool Equals(object obj)
 205:                 {
 206:                     var some = obj as OtherObject;
 207:                     if (obj == null) return false;
 208:                     return some.Value == Value;
 209:                 }
 210:             }
 211:  
 212:             class OtherDictionary<TKey, TValue> : Dictionary<TKey, TValue>
 213:             {
 214:  
 215:             }
 216:  
 217:             class Source
 218:             {
 219:                 public Source()
 220:                 {
 221:                     A = "String";
 222:                     B = 5;
 223:                     C = Guid.NewGuid();
 224:                     D = 5.5;
 225:                     E = 'a';
 226:                     F = new List<int> { 1, 2 };
 227:                     G = new List<string> { "a", "b" };
 228:                     H = new SomeObject();
 229:                     I = new List<SomeObject> { new SomeObject { Value = 1 }, new SomeObject { Value = 2 } };
 230:                     J = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } };
 231:                     K = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } };
 232:                     L = new Dictionary<string, SomeObject> { { "a", new SomeObject { Value = 1 } }, { "b", new SomeObject { Value = 2 } } };
 233:                     M = new Dictionary<SomeObject, SomeObject> {
 234:                         { new SomeObject { Value = 1 }, new SomeObject { Value = 10 } }, 
 235:                         { new SomeObject { Value = 2 }, new SomeObject { Value = 20 } } 
 236:                     };
 237:                     N = new[] { 1, 2 };
 238:                     O = new[] { new SomeObject { Value = 1 }, new SomeObject { Value = 2 } };
 239:                 }
 240:  
 241:                 // Assignable
 242:                 public string A { get; set; }
 243:                 public int B { get; set; }
 244:                 public Guid C { get; set; }
 245:                 public double D { get; set; }
 246:                 public char E { get; set; }
 247:                 public List<int> F { get; set; }
 248:                 public Dictionary<string, int> J { get; set; }
 249:                 public int[] N { get; set; }
 250:  
 251:                 // Not assignable
 252:                 public List<string> G { get; set; }
 253:                 public SomeObject H { get; set; }
 254:                 public List<SomeObject> I { get; set; }
 255:                 public Dictionary<string, int> K { get; set; }
 256:                 public Dictionary<string, SomeObject> L { get; set; }
 257:                 public Dictionary<SomeObject, SomeObject> M { get; set; }
 258:                 public SomeObject[] O { get; set; }
 259:             }
 260:  
 261:  
 262:             class Destination
 263:             {
 264:                 public string A { get; set; }
 265:                 public int B { get; set; }
 266:                 public Guid C { get; set; }
 267:                 public double D { get; set; }
 268:                 public char E { get; set; }
 269:                 public List<int> F { get; set; }
 270:                 public Dictionary<string, int> J { get; set; }
 271:                 public int[] N { get; set; }
 272:  
 273:                 public Collection<string> G { get; set; }
 274:                 public OtherObject H { get; set; }
 275:                 public List<OtherObject> I { get; set; }
 276:                 public OtherDictionary<string, int> K { get; set; }
 277:                 public Dictionary<string, OtherObject> L { get; set; }
 278:                 public Dictionary<OtherObject, OtherObject> M { get; set; }
 279:                 public OtherObject[] O { get; set; }
 280:             }
 281:         }
 282:     }
 283: }

 

One possible enhancement I thought about was to add a attribute to indicate on the destination model that is maps to a property of a different name.  I haven’t had a need for it yet but I will surely add it when I do.

Filed under: ,
Updated JQuery ResizeComplete method
02 December 08 06:51 AM | MattManela | 1 Comments   

I made 2 slight changes and I think it works pretty well now.  I now detect if the browser is Firefox and use the regular resize event since its resize behaves like a resizeComplete.  I also modified the timeout a bit since I think 100 ms might be too small.

Here is the updated version:

   1: jQuery.fn.resizeComplete = function(callback)
   2: {
   3:  
   4:   var element = this;
   5:   var height = element.height();
   6:   var width = element.width();
   7:   var monitoring = false;
   8:   var timer;
   9:   
  10:   function monitorResizing()
  11:   {
  12:     monitoring = true;
  13:     
  14:     var newHeight = element.height();
  15:     var newWidth = element.width();
  16:     
  17:     if(newHeight != height || newWidth != width)
  18:     {
  19:       height = newHeight;
  20:       width = newWidth;
  21:       timer = setTimeout(function() { monitorResizing() },200);
  22:     }
  23:     else
  24:     {
  25:       monitoring = false;
  26:       clearTimeout(timer);
  27:       callback();
  28:     }
  29:   }
  30:   
  31:   function onResize()
  32:   {
  33:     if(monitoring) return;
  34:     monitorResizing();
  35:   }
  36:   
  37:   if($.browser.mozilla)
  38:   {
  39:     element.resize(callback);
  40:   }
  41:   else
  42:   {
  43:     element.resize(onResize);
  44:   }
  45:   
  46:   
  47: }
More Posts Next page »
Page view tracker