AARON SMITH’S CODE ENDEAVOR

Gity 0.1.8 - Initial Diff Support

I just released Gity 0.1.8 with diff support. This is my first round with diff support; I intentionally left line numbers out of the diffs. I have line numbers implemented in a couple different ways and couldn’t make up my mind about which was better. Line numbers will be in the next release.

For now, if anyone has any problems with the diff view, or if it doesn’t render correctly. Please put a post with screenshot on the mac endeavor forum and I’ll take a look. I’ll have another error reporting mechanism to report wrongful diff’s in the next version.

I’ll have another update out later this week with those two additions (line numbers, diff error reporting). Then I’ll be back in hiding for a couple more weeks adding more features.

Here’s another screenshot.

Enjoy

No comments  

Gity Diffing Preview

It’s been a little over a week since I released Gity 0.1.7. I’m working on the next set of features. Most notably diffing. It was definitely the number one requested feature. Here’s a preview screenshot.

To begin with, diffing will be context sensitive - the diff view will update based on what is selected (or not) in the file list view, as well as updating based on what status buttons are toggled. It’s coming along really well; I’m trying to get a base version with diffing in it released in at least a couple weeks. Might be sooner than that. We’ll see ;).

Also, I should definitely say thanks to anyone who has submitted a bug, or used the bug reporter UI in the application to send me errors. Definitely helps a lot.

Enjoy!

No comments  

Alpha Release For Gity - A New Git App For Mac

I’m really excited to share this news. I’ve been working on a git app for OS X for about 3 months. And this is to announce the first alpha release; it’ll be free during alpha and beta. It requires Mac OS X Snow Leopard - it uses Grand Central Dispatch, and some other new API’s that are only available on 10.6.

My schedule is tentative right now. The bug and features list before a final release is very small, so I’m crossing my fingers for two months for a final release. My timeline will also expand or contract based off of feedback and bug reports I get. So make sure to try it, break it, and ask about features. Just don’t swear at me please.

You’ll notice that there are three menu items that are always disabled, and the submodule feature is completely taken out. I had to disable those because I couldn’t complete them in time. I needed a break, and am really excited to get this out. It’s one of those situations that I’m sure some of you have run into - I could have continued working on this for three more months before showing anyone. But it’s time to let it out and start getting feedback.

I should also note that some other features you won’t see in Gity yet are “inspecting” features; most notably diff, or even looking at what the contents of the file is. I purposefully left this out for now. Gity will have two “final” releases. The first one mentioned above, the second sometime in the future. It will include diff tools, and hunk/line tools. But hold on to your panties that release is further away.

You should be pretty familiar with using git from command-line; Gity uses the same terminology - so the more familiar you are the better. If you have questions about what certain things do, the best reference is the man pages.

I just put up the new site here. And I’ve also got a forum in place for feedback and communication.

Please do check it out!

15 comments  

Archiving Framework Versions In Xcode

Here’s a quick script you can use in Xcode to archive framework versions. This is specifically for getting around the problem when you clean the build target. When you clean the target, it removes everything and re-builds it - this removes all of the versions that were there before. So if you had version A, and B, and B is the latest version. Cleaning the target will delete version A. In my case, I want to make sure these versions are kept intact.

OUTPUT=archive/${FULL_PRODUCT_NAME}
FV=$FRAMEWORK_VERSION
VERSIONED_OUTPUT=${OUTPUT}/Versions/${FRAMEWORK_VERSION}
mkdir -p $VERSIONED_OUTPUT
cp -Rf ${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}/Versions/${FRAMEWORK_VERSION}/* ${VERSIONED_OUTPUT}
cd ${OUTPUT}
rm -f $EXECUTABLE_NAME
ln -sf Versions/${FV}/${EXECUTABLE_NAME} $EXECUTABLE_NAME
rm -f Headers
ln -sf Versions/${FV}/Headers Headers
rm -f Resources
ln -sf Versions/${FV}/Resources Resources
cd Versions
rm -f Current
ln -sf ${FV} Current

After you change a Framework Version in xcode, you should clean the project, otherwise Xcode leaves around some dangling symlinks. Which will get copied into this archive. Not a big deal, but I don’t want any stragglers.

No comments  

Doxygen on Snow Leopard

If installing doxygen from macports doesn’t work for you, you’ll have to build doxygen from source. Unfortunately you’ll get an error when building doxygen as well: “error This version of Mac OS X is unsupported”.

The fix is fairly straightforward:

-Download doxygen src
-Edit the file src/qglobal.h
-Find line 73 (we’re changing a chunk from this line)

Change this:

#if defined(__APPLE__) || defined(macintosh)
#define _OS_MAC_
#  ifdef MAC_OS_X_VERSION_MIN_REQUIRED
#    undef MAC_OS_X_VERSION_MIN_REQUIRED
#  endif
#  define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_3
#  include <AvailabilityMacros.h>
#  if !defined(MAC_OS_X_VERSION_10_3)
#     define MAC_OS_X_VERSION_10_3 MAC_OS_X_VERSION_10_2 + 1
#  endif
#  if !defined(MAC_OS_X_VERSION_10_4)
#       define MAC_OS_X_VERSION_10_4 MAC_OS_X_VERSION_10_3 + 1
#  endif
#  if !defined(MAC_OS_X_VERSION_10_5)
#       define MAC_OS_X_VERSION_10_5 MAC_OS_X_VERSION_10_4 + 1
#  endif
#  if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5)
#    error "This version of Mac OS X is unsupported"
#  endif
#elif defined(MSDOS) || defined(_MSDOS) || defined(__MSDOS__)

To this:

#if defined(__APPLE__) || defined(macintosh)
#define _OS_MAC_
#  ifdef MAC_OS_X_VERSION_MIN_REQUIRED
#    undef MAC_OS_X_VERSION_MIN_REQUIRED
#  endif
#  define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_3
#  include <AvailabilityMacros.h>
#  if !defined(MAC_OS_X_VERSION_10_3)
#     define MAC_OS_X_VERSION_10_3 MAC_OS_X_VERSION_10_2 + 1
#  endif
#  if !defined(MAC_OS_X_VERSION_10_4)
#       define MAC_OS_X_VERSION_10_4 MAC_OS_X_VERSION_10_3 + 1
#  endif
#  if !defined(MAC_OS_X_VERSION_10_5)
#       define MAC_OS_X_VERSION_10_5 MAC_OS_X_VERSION_10_4 + 1
#  endif
#  if !defined(MAC_OS_X_VERSION_10_6)
#       define MAC_OS_X_VERSION_10_6 MAC_OS_X_VERSION_10_5 + 1
#  endif
#  if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_6)
#    error "This version of Mac OS X is unsupported"
#  endif
#elif defined(MSDOS) || defined(_MSDOS) || defined(__MSDOS__)

All we did was add in a definition for 10.6.

Now run the install:

./configure
make
sudo make install
1 comment  

Evironment Variables in Xcode

When you’re writing scripts in xcode you’ll need to know what environment variables xcode exposes.

Here’s how you can find them all:

-make a new empty xcode cocoa project
-add a new “run script build phase phase” to the app target
-add this as the script:

env > ENV

-build the project.

There will be file in the project directory called “ENV”, which lists all environment variables. Which comes in really handy as a reference.

1 comment  

Snow Leopard Clean Install - Disk Utility Error: Could not unmount disk

I was doing a clean install of Snow Leopard on an older MBP, and got the message “Error: Could not unmount disk.” Somewhat confused at first - I tried it a couple times, restarted the machine, etc. Still no go.

For some reason the hard drive had some open files or applications - which would cause the error. The solution is not obvious, and I just happened to randomly try this and it worked!

-Boot to your into the Snow Leopard installer.
-Open Disk Utilities
-Select the “Macintosh HD” partition, and press the “Unmount” button. (You’ll get an error asking you to make sure all files and applications are closed)
-Select the DVD-Drive (not the disk) (mine was MATSHITA DVD-RXXXXX), now press the “Eject” button in the toolbar. (You’ll get the same error as above)
-Select the “Macintosh HD” partition again, and press the “Unmount” button. This time, it will successfully unmount.

Now you can continue to erase the Macintosh HD partition, and do a clean install of Snow.

Hopefully this works for more people than just me.

3 comments  

XCode and Clang Static Analyzer

First, check out clang static analyzer.

To get it running form Xcode make a new “Shell Script Target,” call it “Clang,” and for it’s “script build phase” put this in it:

/usr/bin/scan-build -V xcodebuild -configuration $CONFIGURATION

This is assuming the scan-build tool is in /usr/bin/, change the path if you need to. Now, change your build target to Clang, and build it. All it does is run the script, which will in turn build the project for your configuration (usually debug or release). And, if clang finds any problems, it will automatically open your browser to show you the output, if no problems are present, it’s just a successful build.

No comments  

Guttershark Service Manager: SOAP

Ok folks, here’s how you can hook up soap with guttershark.

Here’s a main class: (there are a couple things I explain next)

package
{
 
	public class Main extends Sprite
	{
 
		private var sm:ServiceManager;
 
		public function Main()
		{
			sm=ServiceManager.gi();
			sm.createSOAPService("myWSDL","http://www.example.com/wsdl",3,5000);
			callSoap();
		}
 
		public function callSoap()
		{
			sm.myWSDL.someMethod({
				params:{someParameter:"hello world"},
				resultHandlerClass:MySoapResultHandler,
				onResult:onSoapResult,
				onFault:onSoapFault,
				onFirstCall:onFirstCall,
				onMethodNotAvailable:onMethodNotAvailable,
			});
		}
 
		public function onFirstCall():void
		{
			trace("trying soap service");
			showLoader();
		}
 
		public function onSoapFault(sf:SoapFault):void
		{
			trace("fault");
		}
 
		public function onSoapResult(sr:SoapResult):void
		{
			trace("result");
			trace(sr.raw);
			trace(sr.handler); //instance of resultHandlerClass.
		}
 
		public function onMethodNotAvailable():void
		{
			trace("method was not available on wsdl");
		}
	}
}

When you create a soap service (createSOAPService), the service manager downloads your WSDL and parses it out. It stores some information that you can get at runtime. For example, if you wanted to list out the supported methods on this wsdl:

sm.myWSDL.listMethods();

That’s generally all that you need, and that’s only needed in support of debugging.

Generating the Soap Message

The service manager generates the soap message to send to the server based off of the params object you give it. In the above code example, it’s going to generate a soap message that looks like this:

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <someMethod xmlns="http://webservicelocation/">
      <someParameter>hello world</someParameter>
    </someMethod>
  </soap:Body>
</soap:Envelope>

Handling the Result

The service manager requires you to give it a “result handler class.” This handler class gives you a hook to handle the soap result, before it’s handed off to your callbacks.

In the above example, there is this line (part of call props):

resultHandlerClass:MySoapResultHandler,

“MySoapResultHandler” needs to be a class that you create, to handle the result. Here’s that class:

package
{
 
	import net.guttershark.support.servicemanager.soap.SoapFaultError;
	import net.guttershark.support.servicemanager.soap.SoapResultHandler;
 
	public class MySoapResultHandler extends SoapResultHandler
	{
 
		public var somePropertyOnTheResult;
 
		override protected function process():void
		{
			super.process();
			super.setNamespace();
			super.setBody();
			super.setFault();
 
			//if it get's passed this its an ok result.
			if(soapFault) throw new SoapFaultError(soapFault);
 
			somePropertyOnTheResult = soapBody.someResult.toString();
		}
	}
}

The reason we create this soap result handler class is to delegate processing of the result XML into this class, and abstract it out of your application and the service manager.

There’s one thing to note that’s special about the SoapResultHandler, if you throw a “SoapFaultErorr”, anywhere from the process method, it will be raised back up through the service manager, and trigger your “onFault” callback.

So, in the above handler class, if “soapFault” is set, it throws an error - as mentioned this will ultimately trigger your onFault callback.

Now let’s take a look at an updated “onResult” callback function from the Main class, to do something with our handler:

public function onSoapResult(sr:SoapResult):void
{
    trace("result");
    trace(sr.raw); //raw soap xml response.
    trace(sr.handler); //this is the instance of your result handler - an instance of MySoapResultHandler.
    trace(sr.handler.somePropertyOnTheResult);
}

After the service manager processes your soap result handler, it calls your onResult handler. There’s a property on the SoapResult class called “handler”, which will be an instance of your handler class.

The service manager uses your handler class as a middle man so you can do what you want with the result - before it’s passed on. Hopefully you can see how nice this is. It keeps everything abstract from your application, and allows you to just focus on business logic. And also not clutter the methods that do all the work with a result.

Generally, for every soap method you’re going to call, you create a soap handler class. Each soap handler class can process the result specifically for that method call.

Other things to note

There’s one additional property you can set on a service call “showSoapRequest”:

sm.myWSDL.someMethod({
  params:{someParameter:"hello world"},
  resultHandlerClass:MySoapResultHandler,
  onResult:onSoapResult,
  onFault:onSoapFault,
  onFirstCall:onFirstCall,
  onMethodNotAvailable:onMethodNotAvailable,
  showSoapRequest:true, //
});

This option will trace the generated soap request that’s being sent to the server.

Integrating Soap With The Model

Here’s a model file:

<xml version="1.0" encoding="utf-8">
<model>
	<services>
		<service id="mySoapService" wsdl="mySoapServiceWSDL" attempts="3" timeout="5000" />
		<wsdl id="mySoapServiceWSDL" endpoint="http://www.example.com/wsdl" />
	</services>
</model>

And here’s the update class file:

package
{
 
	import net.guttershark.control.DocumentController;
 
	public class Main extends DocumentController
	{
 
		override protected function flashvarsForStandalone():Object
		{
			return {
				model:"model.xml",
				initServices:true,
			}
		}
 
		override protected function setupComplete():void
		{
			callSoap();
		}
 
		public function callSoap()
		{
			sm.myWSDL.someMethod({
				params:{someParameter:"hello world"},
				resultHandlerClass:MySoapResultHandler,
				onResult:onSoapResult,
				onFault:onSoapFault,
				onFirstCall:onFirstCall,
				onMethodNotAvailable:onMethodNotAvailable,
				showSoapRequest:true,
			});
		}
 
		public function onFirstCall():void
		{
			trace("trying soap service");
			showLoader();
		}
 
		public function onSoapFault(sf:SoapFault):void
		{
			trace("fault");
		}
 
		public function onSoapResult(sr:SoapResult):void
		{
			trace("result");
			trace(sr.raw);
			trace(sr.handler); //instance of resultHandlerClass.
			trace(sr.handler.somePropertyOnTheResult);
		}
 
		public function onMethodNotAvailable():void
		{
			trace("method was not available on wsdl");
		}
	}
}

Crossdomain Issues

With SOAP, Flash has to send over a header called “SOAPAction”, so you’re crossdomains need to allow headers for this.

Conclusion

Soap services are greatly simplified with guttershark - which allows you to focus on business logic of you’re application. Additionally, I hate wasting time on code that’s already been written, don’t you?

Additionally, you should read through the source of the classes that support SOAP functionality. They are in “net.guttershark.support.servicemanager.soap.”

6 comments  

Guttershark Service Manager: HTTP Calls

A key thing in flash that has to be done all too often is making some sort of service call. If you’re not using an AS3 api for a social service, like (cough twitter), it tends to be annoying to implement - because you always write the same code to accomplish some http call, but it’s just sending different parameters.

Guttershark has a service manager that handles doing all this for you, with built-in options for retries, timeouts, maximum attempts, and a bunch of other stuff. And it’s all integrated with the Model, or you can use it by itself. Let’s take a look.

Manually setting up the service manager:

package
{
 
	import net.guttershark.managers.ServiceManager;
	import net.guttershark.support.servicemanager.shared.CallResult;
 
	public class Main extends Sprite
	{
 
		private var sm:ServiceManager;
 
		public function Main()
		{
			sm = ServiceManager.gi();
			sm.createHTTPService("searchGoogle","http://www.google.com/",3,5000);
			var data:Object = {};
			data.q = "Guttershark";
			sm.searchGoogle({
				method:"get",
				data:data,
				responseFormat:"text",
				onResult:onGoogleResult,
				onFault:onGoogleFault,
				onRetry:onGoogleRetry,
				onTimeout:onGoogleTimedOut,
				onFirstCall:onGoogleFirstCall,
			});
		}
 
		public function onGoogleResult(cr:CallResult):void
		{
			trace(cr.result);
			hideStatusIndicator();
		}
 
		public function onGoogleFault(cf:CallFault):void
		{
			trace("FAULT");
			hideStatusIndicator();
		}
 
		public function onGoogleFirstCall():void
		{
			trace("first call to google service.");
			statusIndicator.show();
		}
 
		public function onGoogleRetry():void
		{
			trace("retrying google service");
		}
 
		public function onGoogleTimedOut():void
		{
			trace("timedout");
			hideStatusIndicator();
		}
 
		private function hideStatusIndicator():void
		{
			statusIndicator.hide();
		}
	}
}

Quick side note, please bare with me - some of this is a mix of psuedo code just to illustrate how the callbacks can be used to control different elements.

The above code is somewhat obvious, except for one line:

sm.createHTTPService("searchGoogle","http://www.google.com/",3,5000);

This creates a service called “searchGoogle”, who’s endpoint is that URL.. 3 is the number of attempts to allow, and 5000 is the timeout for each attempt. So in this case, it will call the service once, if a result or fault doesn’t happen within 5000 milliseconds, another attempt will be fired, so on and so fourth.

The service manager, like a couple other classes in Guttershark is a dynamic classes. So, when you create the http service, you can access it like a property of the service manager (sm.searchGoogle).

Easy though right? Let’s look at how it’s integrated with the model:

Model XML:

<?xml version="1.0" encoding="utf-8">
<model>
	<services>
		<service id="searchGoogle" url="http://www.google.com/" attempts="3" timeout="5000" />
	<services>
</model>

Here’s an update Main class file that extends doc controller to integrate the model:

package
{
 
	import net.guttershark.control.DocumentController;
	import net.guttershark.managers.ServiceManager;
	import net.guttershark.support.servicemanager.shared.CallResult;
 
	public class Main extends DocumentController
	{
 
		override protected function flashvarsForStandalone():Object
		{
			return {
				model:"model.xml",
				initServices:true
			}
		}
 
		override protected function setupComplete():void
		{
			var data:Object = {};
			data.q = "Guttershark";
			sm.searchGoogle({
				method:"get",
				data:data,
				responseFormat:"text",
				onResult:onGoogleResult,
				onFault:onGoogleFault,
				onRetry:onGoogleRetry,
				onTimeout:onGoogleTimedOut,
				onFirstCall:onGoogleFirstCall,
			});
		}
 
		public function onGoogleResult(cr:CallResult):void
		{
			trace(cr.result);
			hideStatusIndicator();
		}
 
		public function onGoogleFault(cf:CallFault):void
		{
			trace("FAULT");
			hideStatusIndicator();
		}
 
		public function onGoogleFirstCall():void
		{
			trace("first call to google service.");
			statusIndicator.show();
		}
 
		public function onGoogleRetry():void
		{
			trace("retrying google service");
		}
 
		public function onGoogleTimedOut():void
		{
			trace("timedout");
			hideStatusIndicator();
		}
 
		private function hideStatusIndicator():void
		{
			statusIndicator.hide();
		}
	}
}

Response Formats

The service manager, handles a few different types of response formats: text, binary, xml, and variables. The service manager will intelligently handle results, based off of those types.

For example, let’s say you make a service call to get some XML data. Let’s assume the request completed with a 200 OK. But, there was an error on the server side that caused a result to be returned that isn’t the expected data. As long as structure the XML a certain way, the service manager will call you onFault callback. Here’s an example:

XML Response Format

The service call:

sm.createHTTPService("myXMLService","http://example.com/xmlService",3,5000);
sm.myXMLService({method:"post",responseFormat:"xml",...});

Successful result (will call the onResult callback):

<?xml version="1.0" encoding="utf-8">
<response>
	<someData>Hello World</someData>
</response>

Trigger a fault:

<?xml version="1.0" encoding="utf-8">
<response>
	<fault>There was an error</fault>
</response>

Variables Response Format

The service call:

sm.createHTTPService("myXMLService","http://example.com/xmlService",3,5000);
sm.myXMLService({method:"post",responseFormat:"variables",...});

Successful result (will call the onResult callback):

&name=Aaron&lname=Smith&site=codeendeavor.com

Trigger a fault:

&fault=There was an error

You can access the result variables as an object on the CallResult, for example:

function onResult(cr:CallResult):void
{
    trace(cr.result.lname);
}

The only response formats that are intelligently handled are variables and xml. The other formats are raw data so you’d have to handle it yourself in the result callback.

Routes with Web Frameworks

The service manager can also handle routes for http services. The general ideas is that the “service” is the base URL to the site that exposes routes, then you tell the service manager which routes to append. For example:

sm.createHTTPService("myRestfulSite","http://www.example.com/",3,5000);
var username:String = "b00g3r";
sm.myRestfulService({method:"GET", routes:["user",username,"friends"], responseFormat:"xml"});

This example will end up calling “http://www.example.com/user/b00g3r/friends”.

Conclusion

That’s all there is for HTTP services with the service manager. The service manager also exposes remoting, and SOAP - in the same dynamic/simplistic manner.

I would recommend reading the source that supports HTTP services:

net.guttershark.managers.ServiceManager;
net.guttershark.support.servicemanager.shared.BaseCall
net.guttershark.support.servicemanager.shared.CallResult
net.guttershark.support.servicemanager.shared.CallFault
net.guttershark.support.servicemanager.http.Service
net.guttershark.support.servicemanager.http.ServiceCall

I’ll show you the remoting, and SOAP support next.

No comments  

« Previous PageNext Page »