Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Wiki Markup
{remotion-mixins-disclaimer} 

_Mixin scopes_ are useful for mixing at run-time and limiting the "reach of action" for certain mixins. If nothing else, this is eminently useful for testing. This page explains how to construct and use mixin scopes. The [Hotel mixin sample]'s {{Hotel.Tests}} project makes good use of transaction scopes for testing. 

In the previous example, we have made an entirely home-grown configuration the active configuration for the entire thread. For the following example, we will introduce three modifications to that idea. 
# We will derive our custom configuration from the existing default master configuration (which in turn is a reflection of all the {{Extends}} and {{Uses}} attributes in the application).
# Instead of adding to that configuration, we will remove from it with {{SuppressMixin<>}}.
# We will make the custom configuration active within a limited _scope_, i.e. temporarily.

Since the simplistic dessert wax sample does not lend itself very well to our sophisticated endeavor, we will resort to the slightly more advanced parrot on a unicycle here. 

The following sample is based on the class hierarchy of parrots from the page 

[re-motion mixins basics -- what about sub-classes of the target class?]

[ {{Parrot}}, {{GreyParrot}} and {{Macaw}}, with {{Parrot}} being the base class for {{GreyParrot}} and {{Macaw}}. ]

The entire hierarchy is extended by the {{CircusMixin}} which gives the entire hierarchy the ability to ride a unicycle. This configuration is read in and made the master configuration, which in turn is the active configuration for the given task. 

Since we can't modify an existing configuration, we have to build a new one based upon the active configuration and make the active configuration for our thread. 

As explained on page [re-motion mixins basics -- what about sub-classes of the target class?] the entire class hierarchy is extended by a mixin if that mixin is applied to the base class. In our example, we will exempt the {{Macaw}} class from being mixed. The new methods in the following snippet are
- {{MixinConfiguration.BuildFromActive ()}} -- clones the active configuration
- {{SupressMixin<> ()}} -- removes the specified mixin from the given class

{code}
var t = typeof (IMixinTarget);

var myMixinConfiguration = MixinConfiguration.BuildFromActive () // build upon active configuration
 .ForClass<Macaw> ().SuppressMixin<CircusMixin> ()               // remove the circus mixin from the 'Macaw' class
 .BuildConfiguration ();                                         // finish construction 
{code}

At this point we have a new mixin configuration in the {{myMixinConfiguration}} variable. It is exactly like the default master configuration that has been assembled from inspection of the executable. In the default master configuration, the {{CircusMixin}} is applied to {{Parrot}}, {{GreyParrot}} and {{Macaw}}. In our improved clone, the {{CircusMixin}} is only applied to {{Parrot}} and {{GreyParrot}}. Note that we haven't made the configuration active yet. Remember that we want to do it temporarily, not for the entire life-time of the thread. For this, we have to create a _mixin configuration scope_ from {{myMixinConfiguration}}. You do this with the method {{EnterScope}}:

{code}
... some configuration is active here ...
using (myMixinConfiguration.EnterScope ())
{
  ... code code code ...
  ... where 'myMixinConfiguration' is active ...
} // end of scope

... previous mixin configuration is active again ...
{code}

The {{using}} spans the _scope_ for which the configuration is active. As soon as execution of the code passes the closing bracket {{\}}} of the {{using}}, the cloned configuration vanishes and the previous configuration becomes active again. (People who know the PhoneBook tutorial will recognize this mechanism: this is how [re-store]s _transaction scopes_ work.)

You can even nest such scopes to arbitrary depth:

{code}
... configuration A is active here ...
using (mixinConfiguration_B.EnterScope ())
{
  ... code code code ...
  ... where 'mixinConfiguration_B' is active ...

  using (mixinConfiguration_C.EnterScope ())
  {
    ... code code code ...
    where 'mixinConfiguration_C' is active ...
  } // end of scope for mixinConfiguration_C
  ... mixinConfiguration_C is active again ...

} // end of scope for mixinConfiguration_B
{code} 

The following code sample utilizes {{using}} for our special configuration that has mixing for {{Macaw}} suppressed. 

{code}
  class Program
  {
    static void Main (string[] args)
    {
      // mind the loading of 'Remotion.dll'!
      var t = typeof (IMixinTarget);

      var myMixinConfiguration = MixinConfiguration.BuildFromActive ()   // clone the currently active configuration 
                                                                         // (that happens to be the master default config 
                                                                         // at this point)
        .ForClass<Macaw> ().SuppressMixin<CircusMixin> ()                // remove the mixing of the circus mixin from the 'Macaw' class
        .BuildConfiguration ();                                          // cause the actual building of our clonee 

      // now we span a scope for our special configuration
      using (myMixinConfiguration.EnterScope ())
      {
        // The mixed parrot class works as usual, no suppression here
        var myCircusParrot = ObjectFactory.Create<Parrot> (ParamList.Empty);
        Console.WriteLine ("Parrot:");
        myCircusParrot.Whistle();
        ((ICircusMixin) myCircusParrot).RideUniCycle();
        Console.WriteLine();

        // The mixed grey parrot class works as usua, no suppression here either
        Console.WriteLine ("GreyParrot:");
        var myCircusGreyParrot = ObjectFactory.Create<GreyParrot> (ParamList.Empty);
        myCircusGreyParrot.DestroyFurniture();
        ((ICircusMixin) myCircusGreyParrot).RideUniCycle();
        Console.WriteLine();

        // The macaw can do as macaws do (crack nuts)...
        Console.WriteLine ("Macaw:");
        var myBogusCircusMacaw = ObjectFactory.Create<Macaw> (ParamList.Empty);
        myBogusCircusMacaw.CrackNut();
        try
        {
          // ... but it can't ride the unicycle, because that's been
          // suppressed
          ((ICircusMixin) myBogusCircusMacaw).RideUniCycle ();
        }
        catch (InvalidCastException)
        {
          // ... what gives us a bummer at run-time
          Console.WriteLine ("... or not. We caught an 'InvalidCastException'.");
          Console.WriteLine ("The 'Macaw' class can't be cast to 'ICircusMixin'");
          Console.WriteLine ("because we suppressed that mixin. (Remember?)");
        }
        
        Console.WriteLine();
      } // end of scope with the non-unicycle macaw

      // at this point, the original (default master) configuration
      // is active again, so the macaw CAN ride the unicycle
      Console.WriteLine ("Macaw:");
      var myWorkingCircusMacaw = ObjectFactory.Create<Macaw> (ParamList.Empty);
      myWorkingCircusMacaw.CrackNut ();
      ((ICircusMixin) myWorkingCircusMacaw).RideUniCycle ();
      Console.WriteLine ();

      Console.ReadLine ();
    }
  }
{code}

If you need the new configuration only once, you can directly call {{EnterScope}} on the builder without using {{BuildConfiguration}}. {{BuildConfiguration}} is useful when you need to store the configuration in a variable in order to use it more than once. 

h5. Sample code

You find the complete sample code for this exercise [in subversion|https://svn.re-motion.org/svn/Remotion-Contrib/Samples/Mixin/WikiSamples/trunk/WikiSamples.ParrotSuppressMacaw/]

A more elaborate, and common, application of mixin scopes can be found in the unit-tests of the [Hotel mixin sample], discussed there.