The FARM adds a nail to the coffin of IE 6

by Shannon Deminick 20. July 2009 10:19

Days until TheFARM drops support for IE6



We are happy to announce that we will cease to support IE 6 development from Oct 31 2009. We will continue to support our clients’ existing websites but any new development will target Firefox 2+, IE 7+, and Web kit browsers (e.g. Safari, Chrome).

IE 6 was released in Aug 2001, it is 8 years old and 2 more browsers have since been released from Microsoft… it’s about time people stop using this horrible, horrible browser. Other larger players including YouTube have also stated that support for IE 6 will stop. We’re hoping that more agencies will adopt this idea and push for clients to finally make the upgrade or switch to a new browser… it’s 8 years old!

Comments are always welcome!

Tags: , , ,

Testing Outgoing SMTP Emails - So Simple!

by Shannon Deminick 16. July 2009 06:29

At the Umbraco retreat before CodeGarden 09 in Denmark, Aaron had told me an extremely handy tip about testing outbound emails in your .Net applications. I'm not sure why I've never heard about this before and the funny thing is all of the .Net developers working in our office (including contractors) had never seen this before either! It's so incredibly simple and built into .Net, so if you don't know about this already you'll want to be using this in the future.

If you application needs to send emails for whatever reason and you’re testing locally, you generally have to make sure that you're only sending emails to your address(es) so you’re not spamming a bunch of random people. This is an easy way to get around that and lets you view all of the emails sent. Just change (in our case add) a deliveryMethod attribute to your smtp settings to SpecifiedPickupDirectory:

<system.net>
  <mailSettings>
    <smtp from="noreply@localhost" deliveryMethod="SpecifiedPickupDirectory">
      <specifiedPickupDirectory pickupDirectoryLocation="c:\maildrop" />
    </smtp>
  </mailSettings>
</system.net>

Now, all emails that are sent, just get saved to the specified folder and you can view them with Windows Live Mail, Outlook express, Thunderbird, or whatever.

Nice!!

Tags: , ,
Categories: .Net | Umbraco

Sodality Simple Tutorial in Flash Builder (formerly Flex)

by Tom Byrne 16. July 2009 03:14

This tutorial goes through the building of a simple demo app with Sodality. If it’s moving too slow for you I’d recommend downloading the source and only going through the checkpoint steps of the tutorial (3, 5 & 7).

  1. Project Setup
  2. Basic Sodality interactions
  3. Checkpoint 1
  4. Inserting Asychronous behaviour
  5. Checkpoint 2
  6. Intercepting before and after
  7. Checkpoint 3
  8. Download Source

1. Project Setup

  • In Flash Builder, set up a new ActionScript project (i.e. NOT a Flex Project) named ‘SodalitySimpleDemo’.
  • Download the Sodality SWC and add it to your project, making sure you add it to your source (Project Properties > ActionScript Build Path > Library path > Add SWC).
  • Open the Compiler Settings (Project Properties > ActionScript Compiler) and uncheck ‘Copy non-embedded…’, uncheck ‘Generate HTML Wrapper…’, this is not required by the framework but will help keep things clean.
  • Add the compiler argument ‘-keep-as3-metadata+=Advice,Trigger,Property’ to the ‘Additional Compiler Arguments’ field (still in the Compiler Settings area). This tells Flash Builder to keep the metatags we use to make sodality work.

2. Basic Sodality interactions

First we must add a President to the project, the president executes the core logic of Sodality.

Within the SodalitySimpleDemo class (which flash builder created in the default package), add this private variable:

private var president:President;

Add this to the SodalitySimpleDemo constructor to instantiate the president:

president = new President(this);

Passing through the root object to the president tells it how much of the display hierarchy should be managed by this president (i.e. in this case the whole hierarchy).

Now we must create an Advice class, this class represents a certain action that is taking place within your application. For simplicity’s sake we will create an Advice class representing a mouse click and we’ll create it in the default package. The MouseClickAdvice class will contain a message which will be displayed later:

package
{

import org.farmcode.sodality.advice.Advice;

public class MouseClickAdvice extends Advice
{
[Property(toString="true",clonable="true")]
public var message:String;

public function MouseClickAdvice(message:String=null){
super();
this.message = message;
}
}
}

Consider this Advice class similar to an event, we’ll use it to run other code when something gets clicked on. You’ll notice the message property is annotated with the ‘Property’ metatag, this forces the value to be copied over when the MouseClickAdvice is cloned in the dispatching process, generally all public properties of Advice classes should be annotated in this way. You’ll also note that the constructor arguments must always be optional in Advice subclasses, this is again to allow the cloning behaviour that Advice undergoes.

Next, we’ll add a button to the SodalitySimpleDemo class which we'll use to dispatch our advice, add this to the constructor:

var button:Sprite = new Sprite();
button.graphics.beginFill(0xff0000);
button.graphics.drawRect(100,100,100,100);
button.addEventListener(MouseEvent.CLICK, onMouseClick);
button.buttonMode = true;
addChild(button);

And then we’ll add the event handler which will dispatch the MouseClickAdvice:

protected function onMouseClick(e:MouseEvent):void{
dispatchEvent(new MouseClickAdvice("Button Clicked"));
}

Now the MouseClickAdvice is being dispatched but nothing is listening for it. Before we do that we'll add a text field to the SodalitySimpleDemo class that we can display the message in. Add this private property to the class:

private var resultField:TextField;

And add this to the constructor:

resultField = new TextField();
addChild(resultField);

Now we'll add a function to the SodalitySimpleDemo class which will catch the advice (using the Sodality 'Trigger' Metadata), note that the method must be public for the President to find it:

[Trigger(triggerTiming="after")]
public function afterMouseClick(cause:MouseClickAdvice):void{
resultField.text = cause.message;
}

This is all fine except that Sodality won’t trigger functions within classes unless they’re tagged with the interface IAdvisor (this only works for DisplayObjects, more on that in Step 4).

So change the SodalitySimpleDemo class definition line to:

public class SodalitySimpleDemo extends Sprite implements IAdvisor

Back to top

3. Checkpoint 1

At this point your project should run happily and when you click on the red square, the text ‘Button Clicked’ should appear in the corner of the SWF.

Your classes should look like this:

SodalitySimpleDemo.as

package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;

import org.farmcode.sodality.President;
import org.farmcode.sodality.advisors.IAdvisor;

public class SodalitySimpleDemo extends Sprite implements IAdvisor
{
private var president:President;
private var resultField:TextField;

public function SodalitySimpleDemo()
{
president = new President(this);

var button:Sprite = new Sprite();
button.graphics.beginFill(0xff0000);
button.graphics.drawRect(100,100,100,100);
button.addEventListener(MouseEvent.CLICK, onMouseClick);
button.buttonMode = true;
addChild(button);

resultField = new TextField();
addChild(resultField);
}
protected function onMouseClick(e:MouseEvent):void{
dispatchEvent(new MouseClickAdvice("Button Clicked"));
}
[Trigger(triggerTiming="after")]
public function afterMouseClick(cause:MouseClickAdvice):void{
resultField.text = cause.message;
}
}
}

MouseClickAdvice.as

package
{
import flash.display.DisplayObject;

import org.farmcode.sodality.advice.Advice;

public class MouseClickAdvice extends Advice
{
[Property(toString="true",clonable="true")]
public var message:String;

public function MouseClickAdvice(message:String=null){
super();
this.message = message;
}
}
}

Skip to Checkpoint 2

Back to top

4. Inserting Asychronous behaviour

You’ll be thinking to yourselves that this is just a convoluted event management system at this point, so now we’ll load a file in between when the button is clicked and when the handling function gets called (something that would be impossible with events).

We’ll change the demo so that when the red button is clicked another (non-visual) class loads a file before the ‘afterMouseClick’ handler function gets called.

First we'll create the file to load in, simply create a text file called 'file.txt' in the 'bin-debug' folder containing the words "File Loaded".

Now we’ll need to create a new interface representing an action (Advice) that needs to load a file. This represents all of the interactions that our file loading Advisor (which we’re about to build) will need from the advice:

package
{
import org.farmcode.sodality.advice.IAdvice;

public interface ILoadFileAdvice extends IAdvice
{
function get fileURL():String;
function set message(value:String):void;
}
}

Then we'll modify MouseClickAdvice to implement the new interface (and modify the constructor arguments to take a url):

package
{
import org.farmcode.sodality.advice.Advice;

public class MouseClickAdvice extends Advice implements ILoadFileAdvice
{
public function MouseClickAdvice(fileURL:String=null){
super();
this.fileURL = fileURL;
}

private var _message:String;
private var _fileURL:String;

[Property(toString="true",clonable="true")]
public function get fileURL():String{
return _fileURL
}
public function set fileURL(value:String):void{
_fileURL = value;
}
[Property(toString="true",clonable="true")]
public function get message():String{
return _message
}
public function set message(value:String):void{
_message = value;
}
}
}

Then we’ll need to create our new Advisor, whose responsibility it is to load a file whenever an action requires it:

package
{
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;

import org.farmcode.sodality.advice.AsyncMethodAdvice;
import org.farmcode.sodality.advisors.DynamicAdvisor;

public class FileLoaderAdvisor extends DynamicAdvisor
{
private var lastCause:ILoadFileAdvice;
private var lastAdvice:AsyncMethodAdvice;

[Trigger(triggerTiming="before")]
public function beforeFileLoad(cause:ILoadFileAdvice,
advice:AsyncMethodAdvice, time:String):void{
lastCause = cause;
lastAdvice = advice;
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest(cause.fileURL));
}
protected function onLoadComplete(e:Event):void{
lastCause.message = ((e.target as URLLoader).data as String);
lastAdvice.adviceContinue();
}
}
}

Much of this should be familiar, the class loads in a file and gets it's content. Have a look at the beforeFileLoad function’s parameters though, it is noteworthy that this class never references the MouseClickAdvice class, meaning that any other Advice class that implements ILoadFileAdvice will now be picked up by this class and load a file. Also of note is that the second parameter of the beforeFileLoad function is an AsyncMethodAdvice instance, this tells Sodality to wait until the ‘adviceContinue’ method is called on this instance before other listeners to this advice continue getting called.

The order in which the listeners get called is based on the ‘triggerTiming’ property within the ‘Trigger’ metatag. This is the reason that the 'afterMouseClick' method on the SodalitySimpleDemo will wait until after the file is loaded (and lastAdvice.adviceContinue() is called in 'onLoadComplete') before it gets called and sets the message into the TextField.

Before the new Advisor will do anything though, it must be added to the president. You can see it extends DyamicAdvisor instead of implementing IAdvisor (like SodalitySimpleDemo did), this is because non-visual Advisors need a little more logic to be added to the President. The first way a DynamicAdvisor can be added to the President is using the President's addAdvisor method, this is not the recommended way of doing it because it means references to the president must be passed around your application, tangling your logic. The preferrable way is to set the advisorDisplay property of the new Advisor to a DisplayObject which is added to the display hierarchy (beneath the original display you passed into the President when you created it).

Add this to the constructor of SodalitySimpleDemo to add the new Advisor:

var fileLoaderAdvisor:FileLoaderAdvisor = new FileLoaderAdvisor();
fileLoaderAdvisor.advisorDisplay = this;

The final touch is to modify the onMouseClick method to use the file's URL (as opposed to "Button Clicked"):

protected function onMouseClick(e:MouseEvent):void{
dispatchEvent(new MouseClickAdvice("file.txt"));
}

Back to top

5. Checkpoint 2

Now, when you run the app, click the red box, the file will be loaded in and it’s contents will be shown in the TextField.

The FileLoaderAdvisor class and the ILoadFileAdvice should be as shown in Step 4 above (they won’t change throughout the tutorial).

Your classes should look like this:

SodalitySimpleDemo.as

package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;

import org.farmcode.sodality.President;
import org.farmcode.sodality.advisors.IAdvisor;

public class SodalitySimpleDemo extends Sprite implements IAdvisor
{
private var president:President;
private var resultField:TextField;

public function SodalitySimpleDemo()
{
president = new President(this);

var button:Sprite = new Sprite();
button.graphics.beginFill(0xff0000);
button.graphics.drawRect(100,100,100,100);
button.addEventListener(MouseEvent.CLICK, onMouseClick);
button.buttonMode = true;
addChild(button);

resultField = new TextField();
addChild(resultField);

var fileLoaderAdvisor:FileLoaderAdvisor = new FileLoaderAdvisor();
fileLoaderAdvisor.advisorDisplay = this;
}
protected function onMouseClick(e:MouseEvent):void{
dispatchEvent(new MouseClickAdvice("file.txt"));
}
[Trigger(triggerTiming="after")]
public function afterMouseClick(cause:MouseClickAdvice):void{
resultField.text = cause.message;
}
}
}

MouseClickAdvice.as

package
{
import org.farmcode.sodality.advice.Advice;

public class MouseClickAdvice extends Advice implements ILoadFileAdvice
{
public function MouseClickAdvice(fileURL:String=null){
super();
this.fileURL = fileURL;
}

private var _message:String;
private var _fileURL:String;

[Property(toString="true",clonable="true")]
public function get fileURL():String{
return _fileURL
}
public function set fileURL(value:String):void{
_fileURL = value;
}
[Property(toString="true",clonable="true")]
public function get message():String{
return _message
}
public function set message(value:String):void{
_message = value;
}
}
}

Skip to CheckPoint 3

Back to top

6. Intercepting before and after

For the final step, we build an (incredibly rudimentary) load monitor which outputs the time that outputs the time before and after the file loads.

Lets create our Load Monitor class:

package
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.utils.getTimer;

import org.farmcode.sodality.advisors.IAdvisor;

public class LoadMonitor extends Sprite implements IAdvisor
{
private var textField:TextField;

public function LoadMonitor(){
textField = new TextField();
textField.y = 100;
addChild(textField);
}

[Trigger(triggerTiming="before")]
public function beforeFileLoad(cause:ILoadFileAdvice):void{
textField.text = "Load begun: "+getTimer();
}
[Trigger(triggerTiming="after")]
public function afterFileLoad(cause:ILoadFileAdvice):void{
textField.appendText("\nLoad finished: "+getTimer());
}
}
}

Notice that it has two triggered functions; one before the Advice is executed and one after the Advice executes. Also note that it implements the IAdvisor interface so that the President manages it.

Add these lines to the SodalitySimpleDemo constructor to add the LoadMonitor to our app:

var loadMonitor:LoadMonitor = new LoadMonitor();
addChild(loadMonitor);

Back to top

7. Checkpoint 3

Now when you run the app and click the red button, several things happen in this order:

  • The LoadMonitor outputs the time before any other Advisors see the advice.
  • The FileLoaderAdvisor begins loading the file, at this point the president waits for confirmation to continue.
  • The file finishes loading, the FileLoaderAdvisor calls the adviceContinue method, telling the President to continue.
  • The SodalitySimpleDemo sets the text in it’s TextField to the contents of the file.
  • The LoadMonitor outputs the time after the advice has been to all Advisors.

This process is done without the Advisors being aware of each other, allowing for a discreet relationship between code frameworks. The only thing tying the operations together is the Advice class which simply stores and transfers data.

NOTE: You may be wondering why the LoadMonitor’s beforeFileLoad function gets called before the FileLoaderAdvisor’s beforeFileLoad method, this is based on the order in which the Advisor’s are added. This is not an issue with the prebuilt Advisors and more control over ordering is coming in version 4 (see the roadmap in the repository).

Your main class should look like this (the MouseClickAdvice class didn’t change in this step):

SodalitySimpleDemo.as

package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;

import org.farmcode.sodality.President;
import org.farmcode.sodality.advisors.IAdvisor;

public class SodalitySimpleDemo extends Sprite implements IAdvisor
{
private var president:President;
private var resultField:TextField;

public function SodalitySimpleDemo()
{
president = new President(this);

var button:Sprite = new Sprite();
button.graphics.beginFill(0xff0000);
button.graphics.drawRect(100,100,100,100);
button.addEventListener(MouseEvent.CLICK, onMouseClick);
addChild(button);

resultField = new TextField();
addChild(resultField);

var fileLoaderAdvisor:FileLoaderAdvisor = new FileLoaderAdvisor();
fileLoaderAdvisor.advisorDisplay = this;

var loadMonitor:LoadMonitor = new LoadMonitor();
addChild(loadMonitor);
}
protected function onMouseClick(e:MouseEvent):void{
dispatchEvent(new MouseClickAdvice("file.txt"));
}
[Trigger(triggerTiming="after")]
public function afterMouseClick(cause:MouseClickAdvice):void{
resultField.text = cause.message;
}
}
}

 

8. Download Source

Back to top

Tags:
Categories: Flash

2 Click ASP.Net Web Application Deployment

by Shannon Deminick 10. July 2009 13:17

At CodeGarden09 I had mentioned during one of the open space sessions that we had developed a 2 click deployment strategy for some of our projects and someone asked if I could share the solution, so here it goes!

The tools we use to do this are:

Get Zip File here: TheFARMDeployUtilities.zip (513.89 kb)

We generally have 3 different environments for our websites: Development, Staging, Live (Production). For 2 click deployments to work, we then need to have CruiseControl.Net server installed on each server environment. Your development machine or the machine that is performing the 2 click deploy should have access to all of the CruiseControl.Net servers so that each project can be added to your CCTray application. We lock down our CruiseControl.Net servers via firewall and running them on non-standard ports to only allowing traffic to them from our office environment.

This strategy is 2 steps because:

  • Gives you the control over when the project is built
  • Gives you control over when the built files are deployed live
  • Though it could be done in 1 step, I feel that it’s safer to ensure that the project is built properly before it goes live

This deployment process also takes into account:

  • A rollback process which ensures any build that is deployed can be rolled back to a previous build very quickly
  • The problem of multiple application restarts (or long application restarts) when uploading files via FTP since each DLL is essentially copied up one at a time, this process ensures that all built files are deployed to the environment without delay.

Basic Workflow

In order to understand the next steps its best to get a brief overview of what steps are involved when the deployment occurs:

  1. All code is committed to the repository (to a branch, or wherever the stable code should be) that you wish to deploy

Click 1

  1. A CruiseControl ‘Force Build’ is initiated for the project (normally via CCTray)
  2. CruiseControl checks out the latest version from Subversion and puts it in it’s working folder for the project
  3. CruiseControl runs a NANT task which:
    1. Creates a build date time stamp (i.e. 20090710AM)
    2. Runs an MSBuild process for each deployment environment (dev/stage/live) to build and deploy the web application. Depending on the environment, it will build in Release vs. Debug mode.
    3. Formats configuration files to include only the relevant settings for each environment
    4. Zips the builds for each deployment environment named with the build date time stamp
    5. FTPs the Zip file to a deploy folder on the deployment servers (stage/live)

Click 2

  1. A CruiseControl ‘ForceBuild’ is initiated on the environment’s server you wish to deploy to (normally via CCTray)
  2. CruiseControl runs a NANT task which:
    1. Unzips the latest build files into the staging/live folders for IIS.

CruiseControl Server Setup

The CruiseControl (build) server requires a few bits of software installed and configured:

The CruiseControl servers on staging/live environments only require that CruiseControl & NANT is installed.

Folder Structure

On the CruiseControl build server, we create this folder structure for each project:

  • Artifact
    • Debug
      • buildlogs (stores the CruiseControl build logs for the project)
  • Deployment
    • Dev (the folder for the Dev environment builds)
    • Live (the folder for the Live environment builds)
    • Staging (the folder for the Staging environment builds)
  • Working (where all of the source control files are checked out to)

On the staging/live servers we have a folder in the root of the website labelled: “_DEPLOY” which is where the deployment zip files get FTP’d to. We ensure that this folder doesn’t have read access so public people can’t go downloading your build files! In this _DEPLOY folder we have a deploy.build NANT task (see below).

Configuration File Formatting

Everyone seems to have their own way of managing different configuration settings for different environments and so do we! I like to be able to see all of the configuration settings for each environment in one file so that i know what’s being defined without looking through different files. To do this, our configuration settings look something like this:

<!--<DEV>-->
<add key="mySetting" value="DevEnvironmentSettingValue" />
<!--</DEV>-->
<!--<STAGING>
<add key="mySetting" value="StagingEnvironmentSettingValue" />
</STAGING>-->
<!--<LIVE>
<add key="mySetting" value="LiveEnvironmentSettingValue" />
</LIVE>-->

 

With the above design, development settings are always active when developing. When CruiseControl runs it’s build one of the NANT tasks that are run is The FARM’s configuration settings parser plugin which removes any configuration settings in config files that are not required for the current build environment.

We’ve also developed a handy little Visual Studio plugin to make formatting these configuration settings very easy. All that is required is highlighting a configuration element, clicking Tools –> The Farm Config –> Format for Dev (or staging/live)

CruiseControl.Net Setup

Deployment/Build Server

I’ve put documentation about each item directly in the XML:

<project name="MYPROJECT" category="WEB" queue="MYPROJECT" queuePriority="1" >
<!-- The working folder, this is where our repository files will be checked out to -->
<workingDirectory>D:\CruiseControl\Projects\MYPROJECT\Working</workingDirectory>
<!-- This is where our log files, and debug info will be saved -->
<artifactDirectory>D:\CruiseControl\Projects\MYPROJECT\Artifact\Debug</artifactDirectory>
<sourcecontrol type="svn">
<!-- We use a file based Subversion repository... here's an example of the location -->
<trunkUrl>file:///\myserver.mydomain.local/MYPROJECT/DEV_Repository/trunk/</trunkUrl>
<!-- Our Subversion install is here -->
<executable>C:\Program Files\CollabNet Subversion\svn.exe</executable>
<autoGetSource>True</autoGetSource>
</sourcecontrol>
<!-- We always do Force Builds, instead of automatic builds based on source control so our
server doesn't die when people commit multiple times for different projects, therefore
triggers is set to nothing -->
<triggers/>
<tasks>
<nant>
<!-- We use NANT to do all of our build. NANT in turn runs MSBuild and other
methods that we require such as FTP, etc... -->
<executable>C:\Program Files\Nant\bin\nant.exe</executable>
<baseDirectory>D:\CruiseControl\Projects\MYPROJECT\Working</baseDirectory>
<!-- This is the location of the NANT build file... it is in our working folder
which means that it is committed into the repository so that our devs are free
to modify it to suit their needs -->
<buildFile>D:\CruiseControl\Projects\MYPROJECT\Working\cc.build</buildFile>
<buildTimeoutSeconds>3600</buildTimeoutSeconds>
</nant>
</tasks>
<publishers>
<xmllogger />
<!-- This ensures that our log files don't build up too big -->
<artifactcleanup cleanUpMethod="KeepLastXBuilds" cleanUpValue="50" />
</publishers>
</project>

Staging/Live Servers

For each site, just define another project which simply runs a NANT script which basically just unzips the zip build file into the working IIS site folder (see below)

<project name="_STAGING_MYSITE">
<workingDirectory>E:\Inetpub\MYSITE</workingDirectory>
<artifactDirectory>E:\Inetpub\MYSITE\_DEPLOY\Debug</artifactDirectory>
<tasks>
<nant>
<executable>C:\Nant\bin\nant.exe</executable>
<baseDirectory>E:\Inetpub\MYSITE</baseDirectory>
<buildFile>E:\Inetpub\MYSITE\_DEPLOY\deploy.build</buildFile>
</nant>
</tasks>
<publishers>
<xmllogger />
<artifactcleanup cleanUpMethod="KeepLastXBuilds" cleanUpValue="50" />
</publishers>
</project>

NANT Build File

Deployment/Build Server

I’ve put documentation about each item directly in the XML:

<!-- Ensure NO namespace is declared as FTPTask for NANT 
has a bug when namespaces are declared -->
<project name="Site" default="DeployAll">

<!-- ** PROPERTY DEFINITIONS, These are the only things you should need to change -->

<!-- define the folder containing the web application project -->
<property name="location.working.web" value="${CCNetWorkingDirectory}\_net\MySite.Web"/>

<!-- define the web applicatoin project project file -->
<property name="project.web" value="${location.working.web}\MySite.Web.csproj"/>

<!-- define the deployment folders for each environment -->
<property name="location.deploy.dev" value="${CCNetWorkingDirectory}\..\Deployment\Dev"/>
<property name="location.deploy.staging" value="${CCNetWorkingDirectory}\..\Deployment\Staging"/>
<property name="location.deploy.live" value="${CCNetWorkingDirectory}\..\Deployment\Live"/>

<!-- define the MSBuild executable -->
<property name="exe.msconfig" value="C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe"/>

<!-- define the ftp property connections for the stage/live environments -->
<property name="ftp.staging.server" value="123.123.123.123"/>
<property name="ftp.staging.user" value="admin"/>
<property name="ftp.staging.password" value="hello"/>
<property name="ftp.staging.path" value="MYSITE/FILES/_DEPLOY"/>
<property name="ftp.live.server" value="321.321.321.321"/>
<property name="ftp.live.user" value="admin"/>
<property name="ftp.live.password" value="hello"/>
<property name="ftp.live.path" value="MYSITE/FILES/_DEPLOY"/>

<!-- ** END PROPERTY DEFINITIONS -->

<!-- the primary build target which does the entire build -->
<target name="DeployAll" depends="SetBuildDate,Init,DevSite,StagingSite,LiveSite,CopyStaticFiles" />

<!-- sets some GLOBAL properties for use on all builds -->
<target name="Init" description="Sets the global working and project properties">
<property name="GLOBAL.location.working" value="${location.working.web}"/>
<property name="GLOBAL.project" value="${project.web}"/>
</target>

<!-- set a build date property to today's date with am/pm stored in ${build.date}-->
<target name="SetBuildDate" description="Creates a build date property for use in our deployment scripts">
<tstamp property="build.date" pattern="yyyyMMddtt" verbose="true" />
<echo message="Current build label: ${build.date}" level="Debug" />
</target>

<!-- Deploy Dev with Debug, this doesn't time stamp or zip the output
since it's a dev environment, the location should be set to your
IIS folder -->
<target name="DevSite" description="Compile Dev Web MySite.">
<property name="GLOBAL.location.deploy" value="${location.deploy.dev}"/>
<!-- build project in Debug mode -->
<property name="GLOBAL.project.config" value="Debug"/>
<property name="GLOBAL.deploy.type" value="DEV"/>

<!-- build the web application (see below) -->
<call target="BuildWebProject" />
</target>

<!-- Deploy Staging with Debug -->
<target name="StagingSite" description="Compile Staging Web MySite.">
<!-- build the project to the time stamped folder -->
<property name="GLOBAL.location.deploy" value="${location.deploy.staging}\${build.date}"/>
<!-- build project in Debug mode -->
<property name="GLOBAL.project.config" value="Debug"/>
<!-- define that the STAGING settings should be active on deploy -->
<property name="GLOBAL.deploy.type" value="STAGING"/>

<!-- build the web application (see below) -->
<call target="BuildWebProject" />
<!-- format the configuration files for the project to have the correct
environments settings active -->
<call target="FormatFileForDeploy" />
<!-- zip the build -->
<call target="ZipDeployFiles" />

<!-- transfer the build to the staging environment's deploy folder -->
<connection id="myconn" server="${ftp.staging.server}" username="${ftp.staging.user}" password="${ftp.staging.password}" />
<ftp connection="myconn" verbose="true" showdironconnect="true" remotedir="${ftp.staging.path}">
<put localdir="${location.deploy.staging}" type="bin">
<include name="${build.date}.zip" />
</put>
</ftp>

</target>

<!-- Deploy Live with Release -->
<target name="LiveSite" description="Compile Live Web MySite.">
<!-- build the project to the time stamped folder -->
<property name="GLOBAL.location.deploy" value="${location.deploy.live}\${build.date}"/>
<!-- build project in RELEASE mode -->
<property name="GLOBAL.project.config" value="Release"/>
<!-- define that the LIVE settings should be active on deploy -->
<property name="GLOBAL.deploy.type" value="LIVE"/>

<!-- build the web application (see below) -->
<call target="BuildWebProject" />
<!-- format the configuration files for the project to have the correct
environments settings active -->
<call target="FormatFileForDeploy" />
<!-- zip the build -->
<call target="ZipDeployFiles" />

<!-- transfer the build to the live environment's deploy folder -->
<connection id="myconn" server="${ftp.live.server}" username="${ftp.live.user}" password="${ftp.live.password}" />
<ftp connection="myconn" verbose="true" showdironconnect="true" remotedir="${ftp.live.path}">
<put localdir="${location.deploy.live}" type="bin">
<include name="${build.date}.zip" />
</put>
</ftp>
</target>

<!-- ** BUILD METHODS-->

<!-- Global method to build web applicatio projects using MSBuild -->
<target name="BuildWebProject">
<exec basedir="${GLOBAL.location.working}" program="${exe.msconfig}" workingdir="." failonerror="true">
<arg value="${GLOBAL.project}" />
<arg value="/nologo" />
<arg value="/t:Clean;Build" />
<arg value="/t:ResolveReferences;_CopyWebApplication"/>
<arg value="/p:OutDir=${GLOBAL.location.deploy}\bin\;Configuration=${GLOBAL.project.config}"/>
<arg value="/p:WebProjectOutputDir=${GLOBAL.location.deploy}"/>
</exec>
</target>

<!-- This formats any files that are included in our include property -->
<!-- to be in the correct format for dev,staging,live -->
<target name="FormatFileForDeploy">
<thefarm-process-config configtype="${GLOBAL.deploy.type}">
<fileset basedir="${GLOBAL.location.deploy}">
<include name="**/Regions.xml" />
<include name="**/EmailFormDefinitions.xml" />
<include name="**/*.config" />
</fileset>
</thefarm-process-config>
</target>

<!-- Zips all files in the current deployment folder -->
<target name="ZipDeployFiles">
<zip zipfile="${GLOBAL.location.deploy}.zip">
<fileset basedir="${GLOBAL.location.deploy}">
<include name="**/*" />
</fileset>
</zip>
<delete dir="${GLOBAL.location.deploy}" />
</target>

<!-- ** END BUILD METHODS-->

</project>

Staging/Live Servers

<project name="MyStagingSite" default="DoDeploy">

<target name="DoDeploy" depends="SetBuildDate,UnzipDeployFiles" />

<!-- set a build date property to today's date with am/pm-->
<target name="SetBuildDate">
<tstamp property="build.date" pattern="yyyyMMddtt" verbose="true" />
<echo message="Current build label: ${build.date}" level="Debug" />
</target>

<!-- unzip the build file into the IIS working folder -->
<target name="UnzipDeployFiles">
<unzip zipfile="${CCNetWorkingDirectory}\_DEPLOY\${build.date}.zip" todir="${CCNetWorkingDirectory}" />
</target>

</project>

Rollback

Since each build is contained in a date/time stamped ZIP file it is obviously fairly easy to rollback your site to a previous build, you would just have to know what date/time the last stable build was.

Conclusion

Though the above seems like a lot of work, it really isn’t since all the work is already done for you! All you really need to do once you have your CruiseControl servers setup is create your projects, modify the NANT build script properties for each project and that’s it.

This makes deployment extremely easy and safe since there’s no room for human error. All of the files will go exactly where they need to go. Developers can’t upload the wrong configuration files to the wrong servers or the wrong files to the wrong places. Another great benefit is that when the server unzips the build, the .Net application restarts quickly because it has all of the files to start compiling instantly.

Obviously to do this, you would need access to your staging and development servers, but even if you didn’t you could still get the first step done (which is the major step). The NANT scripts above are quite basic, we use them to build Sandcastle .Net documentation and all sorts of other things.

Always great to know how everyone else is doing the things described in this doc, so please let me know!

Categories: .Net | Hosting | Umbraco

Fast 2D Bezier Library for ActionScript 3

by Tom Byrne 6. July 2009 11:13

Recently we needed to do some animated curves in AS3, and after searching around for some time we found several libraries for easily drawing bezier paths. The problem was they were all very slow, this being a result of them using the lineTo method for drawing (resulting in a curve made up of many tiny straight lines).

The benefits of the lineTo method (over the curveTo method) are that the resulting drawing can be very accurate, and that the maths used is simpler, neither of these were necessary for what we were doing.

We wrote our library with some goals, firstly, that it used the curveTo for all of it’s drawing. We also wanted to be able to construct paths without specifying control points, just the main points that the path passed through and a “tension” to describe the curve that gets drawn between.

The code for the bezier library is part of our Open Source Library

Here’s an example of the kind of thing that can be done easily with the library.

 

 (Click anywhere to create a wave)

Please note that the library hasn’t undergone the rigorous optimisations that everyone is writing articles on, we’ll probably wait till we convert it to FP10.

Here's the source:

package 
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;

import org.farmcode.bezier.BezierPoint;
import org.farmcode.bezier.Path;

[SWF(backgroundColor="#dfdfdd", frameRate="25", width="700", height="400")]
public class WaveTest extends Sprite
{
//Math
private var theta:Number = 0;
private var buffer:Array = new Array();
private var renderBuffer:int = 0;
private var k1:Number;
private var k2:Number;
private var k3:Number;

private var path:Path;

//Common
private var spacing:int = 70;
private var waterSegsX:int;
private var waterSegsXreal:int;
private var waterXcenter:int;
private var isPause:Boolean = false;

public function WaveTest(){
path = new Path();
path.autoFillTension = 0.2;
//path.closed = true;

stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;

prepareMath();
populateBuffer();

addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(MouseEvent.CLICK, onMouseClick);

}
private function onMouseClick(e:Event):void{
makeSplash(((stage.stageHeight/2)-mouseY)/2, mouseX/stage.stageWidth);
}
public function makeSplash(impact:Number, fract:Number):void{
var index:int = Math.floor(fract*waterSegsXreal);
if(index==waterSegsXreal)index = waterSegsX;
buffer[renderBuffer][index].y += impact;
}
public function prepareMath():void{
waterSegsX = Math.round(stage.stageWidth/spacing);
waterSegsXreal = waterSegsX+1;
waterXcenter = waterSegsX/2;

var weight:Number = 10;
var t:Number = 0.3;
var speedPerFrequency:Number = 6;
var decreaseRate:Number = 0.1;

var f1:Number = speedPerFrequency*speedPerFrequency*t*t/(weight*weight);
var f2:Number = 1/(decreaseRate*t+2);

k1 = (4 - 8*f1)*f2;
k2 = (decreaseRate*t-2)*f2;
k3 = 2 * f1 * f2;
}

public function populateBuffer():void{
var x:int,y:int,z:int;
for(var i:int = 0 ; i < 2; i++){
var a:Array = new Array();
for(var k:int = 0 ; k < waterSegsXreal; k++)
a.push(new Point(k,0));

buffer[i] = a;
}
}

private function evaluate():void{
var crnt:Array = buffer[renderBuffer];
var prev:Array = buffer[1-renderBuffer];
for (var i:int = 0 ; i < waterSegsXreal; i++) {
var currentP:Point = (Point)(buffer[renderBuffer][i]);
var prevPoint:Point = prev[i] as Point;
var beforePoint:Point = crnt[i-1];
var afterPoint:Point = crnt[i+1];
prevPoint.y =
k1*((Point)(crnt[i])).y + k2*prevPoint.y +
k3*((afterPoint?afterPoint.y:0) + (beforePoint?beforePoint.y:0) + currentP.y);
}
renderBuffer = 1-renderBuffer;
}


private function onEnterFrame(event:Event):void{
if(!isPause){
theta += 0.05;
var verCounter:uint = 0;
graphics.clear();
graphics.beginFill(0x111120);

var points:Array = [];
graphics.moveTo(0,stage.stageHeight/2);

for(var x:int = 0 ; x < waterSegsXreal ; x++){
var y:Number = buffer[renderBuffer][x].y;
var point:BezierPoint = new BezierPoint((x/(waterSegsXreal-1))*stage.stageWidth,(stage.stageHeight/2)+y);
if(x==0){
point.backwardDistance = 0;
point.backwardAngle = 0;
}else if(x == waterSegsXreal-1){
point.forwardDistance = 0;
point.forwardAngle = 0;
}
points.push(point);
}

path.points = points;
path.drawInto(graphics);

graphics.lineTo(stage.stageWidth,stage.stageHeight);
graphics.lineTo(0,stage.stageHeight);
graphics.endFill();

evaluate();

}
}
}
}
Tags:
Categories: Flash