Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial commit

  • Loading branch information...
commit 35376b69527078862fe879654a8c21635c0ea7af 0 parents
@JimRoepcke JimRoepcke authored
Showing with 3,074 additions and 0 deletions.
  1. +57 −0 Documentation/images/twisted/LICENSE.txt
  2. BIN  Documentation/images/twisted/deferred-attach.png
  3. BIN  Documentation/images/twisted/deferred-process.png
  4. BIN  Documentation/images/twisted/deferred.png
  5. +15 −0 HLDeferred/.gitignore
  6. +65 −0 HLDeferred/Classes/HLDeferred.h
  7. +266 −0 HLDeferred/Classes/HLDeferred.m
  8. +23 −0 HLDeferred/Classes/HLDeferredConcurrentDataSource.h
  9. +103 −0 HLDeferred/Classes/HLDeferredConcurrentDataSource.m
  10. +73 −0 HLDeferred/Classes/HLDeferredDataSource.h
  11. +84 −0 HLDeferred/Classes/HLDeferredDataSource.m
  12. +32 −0 HLDeferred/Classes/HLDeferredList.h
  13. +129 −0 HLDeferred/Classes/HLDeferredList.m
  14. +18 −0 HLDeferred/Classes/HLDownloadDataSource.h
  15. +110 −0 HLDeferred/Classes/HLDownloadDataSource.m
  16. +19 −0 HLDeferred/Classes/HLFailure.h
  17. +41 −0 HLDeferred/Classes/HLFailure.m
  18. +13 −0 HLDeferred/Classes/HLJSONDataSource.h
  19. +34 −0 HLDeferred/Classes/HLJSONDataSource.m
  20. +41 −0 HLDeferred/Classes/HLURLDataSource.h
  21. +164 −0 HLDeferred/Classes/HLURLDataSource.m
  22. +1 −0  HLDeferred/Frameworks/.gitignore
  23. +525 −0 HLDeferred/HLDeferred.xcodeproj/project.pbxproj
  24. +13 −0 HLDeferred/HLDeferred_Prefix.pch
  25. +82 −0 HLDeferred/Tests/GHUnitIOSTestMain.m
  26. +70 −0 HLDeferred/Tests/HLDeferredConcurrentDataSourceTest.m
  27. +68 −0 HLDeferred/Tests/HLDeferredDataSourceTest.m
  28. +268 −0 HLDeferred/Tests/HLDeferredListTest.m
  29. +233 −0 HLDeferred/Tests/HLDeferredTest.m
  30. +20 −0 HLDeferred/Tests/HLDeferredTests-Info.plist
  31. +14 −0 HLDeferred/Tests/HLDeferredTests_Prefix.pch
  32. +105 −0 HLDeferred/Tests/HLDownloadDataSourceTest.m
  33. +61 −0 HLDeferred/Tests/HLJSONDataSourceTest.m
  34. +84 −0 HLDeferred/Tests/HLURLDataSourceTest.m
  35. +18 −0 LICENSE
  36. +225 −0 README.md
57 Documentation/images/twisted/LICENSE.txt
@@ -0,0 +1,57 @@
+Copyright (c) 2001-2010
+Allen Short
+Andy Gayton
+Andrew Bennetts
+Antoine Pitrou
+Apple Computer, Inc.
+Benjamin Bruheim
+Bob Ippolito
+Canonical Limited
+Christopher Armstrong
+David Reid
+Donovan Preston
+Eric Mangold
+Eyal Lotem
+Itamar Shtull-Trauring
+James Knight
+Jason A. Mobarak
+Jean-Paul Calderone
+Jessica McKellar
+Jonathan Jacobs
+Jonathan Lange
+Jonathan D. Simms
+Jürgen Hermann
+Kevin Horn
+Kevin Turner
+Mary Gardiner
+Matthew Lefkowitz
+Massachusetts Institute of Technology
+Moshe Zadka
+Paul Swartz
+Pavel Pergamenshchik
+Ralph Meijer
+Sean Riley
+Software Freedom Conservancy
+Travis B. Hartwell
+Thijs Triemstra
+Thomas Herve
+Timothy Allen
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
BIN  Documentation/images/twisted/deferred-attach.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Documentation/images/twisted/deferred-process.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Documentation/images/twisted/deferred.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 HLDeferred/.gitignore
@@ -0,0 +1,15 @@
+build
+
+*.pbxuser
+*.perspectivev3
+*.mode1v3
+*.mode2v3
+!default.pbxuser
+!default.perspectivev3
+!default.mode1v3
+!default.mode2v3
+
+*~.nib
+*~.xib
+
+.DS_Store
65 HLDeferred/Classes/HLDeferred.h
@@ -0,0 +1,65 @@
+//
+// HLDeferred.h
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLFailure.h"
+
+extern NSString * const kHLDeferredCancelled;
+
+typedef id (^ThenBlock)(id result);
+typedef id (^FailBlock)(HLFailure *failure);
+
+@class HLDeferred;
+
+@protocol HLDeferredCancellable <NSObject>
+
+- (void) deferredWillCancel: (HLDeferred *)d;
+
+@end
+
+@interface HLDeferred : NSObject
+{
+ BOOL finalized_;
+ BOOL called_;
+ BOOL suppressAlreadyCalled_;
+ BOOL running_;
+ id result_;
+ NSInteger pauseCount_;
+ NSMutableArray *callbacks_;
+ id <HLDeferredCancellable> canceller_;
+ ThenBlock finalizer_;
+}
+
+@property (nonatomic, assign) id <HLDeferredCancellable> canceller;
+@property (nonatomic, readonly, assign, getter=isCalled) BOOL called;
+
+// designated initializer
+- (id) initWithCanceller: (id <HLDeferredCancellable>) theCanceller;
+- (id) init; // calls initWithCanceller: nil
+
++ (HLDeferred *) deferredWithResult: (id)result;
++ (HLDeferred *) deferredWithError: (id)error;
+
+- (HLDeferred *) then: (ThenBlock)cb;
+- (HLDeferred *) fail: (FailBlock)eb;
+- (HLDeferred *) both: (ThenBlock)bb;
+
+- (HLDeferred *) then: (ThenBlock)cb fail: (FailBlock)eb;
+
+- (HLDeferred *) thenReturn: (id)aResult;
+
+- (HLDeferred *) thenFinally: (ThenBlock)aThenFinalizer;
+- (HLDeferred *) failFinally: (FailBlock)aFailFinalizer;
+- (HLDeferred *) bothFinally: (ThenBlock)aBothFinalizer;
+
+- (HLDeferred *) thenFinally: (ThenBlock)atThenFinalizer failFinally: (FailBlock)aFailFinalizer;
+
+- (HLDeferred *) takeResult: (id)aResult;
+- (HLDeferred *) takeError: (id)anError;
+- (void) cancel;
+
+@end
266 HLDeferred/Classes/HLDeferred.m
@@ -0,0 +1,266 @@
+//
+// HLDeferred.m
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDeferred.h"
+
+NSString * const kHLDeferredCancelled = @"__HLDeferredCancelled__";
+
+@interface HLDeferred ()
+
+@property (nonatomic, retain) id result;
+
+- (id) _continue: (id)newResult;
+- (void) _run;
+- (void) _startRun: (id)aResult;
+
+@end
+
+@implementation HLDeferred
+
+@synthesize result=result_;
+@synthesize canceller=canceller_;
+@synthesize called=called_;
+
++ (HLDeferred *) deferredWithResult: (id)aResult { return [[[[self alloc] init] autorelease] takeResult: aResult]; }
++ (HLDeferred *) deferredWithError: (id)anError { return [[[[self alloc] init] autorelease] takeError: anError]; }
+
+- (id) initWithCanceller: (id <HLDeferredCancellable>) theCanceller
+{
+ self = [super init];
+ if (self) {
+ called_ = NO;
+ suppressAlreadyCalled_ = NO;
+ running_ = NO;
+ result_ = nil;
+ pauseCount_ = 0;
+ finalized_ = NO;
+ finalizer_ = nil;
+ callbacks_ = [[NSMutableArray alloc] init];
+ canceller_ = theCanceller;
+ }
+ return self;
+}
+
+- (id) init
+{
+ self = [self initWithCanceller: nil];
+ return self;
+}
+
+- (void) dealloc
+{
+ canceller_ = nil;
+ [result_ release]; result_ = nil;
+ [finalizer_ release];
+ [callbacks_ release]; callbacks_ = nil;
+
+ [super dealloc];
+}
+
+- (void) pause
+{
+ // NSLog(@"%@ in %@", self, NSStringFromSelector(_cmd));
+ pauseCount_++;
+}
+
+- (void) unpause
+{
+ // NSLog(@"%@ in %@", self, NSStringFromSelector(_cmd));
+ pauseCount_--;
+ if (pauseCount_ > 0) return;
+ if (called_) {
+ [self _run];
+ }
+}
+
+- (HLDeferred *) thenReturn: (id)aResult {
+ return [self then: ^(id _) { return aResult; } fail: nil];
+}
+
+- (HLDeferred *) then: (ThenBlock)cb { return [self then: cb fail: nil ]; }
+- (HLDeferred *) fail: (FailBlock)eb { return [self then: nil fail: eb ]; }
+- (HLDeferred *) both: (ThenBlock)bb { return [self then: bb fail: bb]; }
+
+- (HLDeferred *) then: (ThenBlock)cb fail: (FailBlock)eb
+{
+ // NSLog(@"%@ in %@", self, NSStringFromSelector(_cmd));
+ if (finalized_) {
+ @throw [NSException exceptionWithName: NSInternalInconsistencyException
+ reason: @"HLDeferred has been finalized"
+ userInfo: nil];
+ } else {
+ NSMutableDictionary *link = [[NSMutableDictionary alloc] initWithCapacity: 3];
+ cb = [cb copy];
+ eb = [eb copy];
+ if (cb) [link setObject: cb forKey: @"t"];
+ if (eb) [link setObject: eb forKey: @"f"];
+ [callbacks_ addObject: link];
+ [cb release];
+ [eb release];
+ [link release];
+ if (called_) {
+ [self _run];
+ }
+ }
+ return self;
+}
+
+- (HLDeferred *) thenFinally: (ThenBlock)aThenFinalizer { return [self thenFinally: aThenFinalizer failFinally: nil ]; }
+- (HLDeferred *) failFinally: (FailBlock)aFailFinalizer { return [self thenFinally: nil failFinally: aFailFinalizer]; }
+- (HLDeferred *) bothFinally: (ThenBlock)aBothFinalizer { return [self thenFinally: aBothFinalizer failFinally: aBothFinalizer]; }
+
+- (HLDeferred *) thenFinally: (ThenBlock)atThenFinalizer failFinally: (FailBlock)aFailFinalizer
+{
+ if (finalized_) {
+ @throw [NSException exceptionWithName: NSInternalInconsistencyException
+ reason: @"HLDeferred has been finalized"
+ userInfo: nil];
+ } else if (finalizer_) {
+ @throw [NSException exceptionWithName: NSInternalInconsistencyException
+ reason: @"HLDeferred already has a finalizer"
+ userInfo: nil];
+ } else {
+ finalizer_ = [[NSMutableDictionary alloc] initWithCapacity: 3];
+ atThenFinalizer = [atThenFinalizer copy];
+ aFailFinalizer = [aFailFinalizer copy];
+ if (atThenFinalizer) [finalizer_ setObject: atThenFinalizer forKey: @"t"];
+ if (aFailFinalizer) [finalizer_ setObject: aFailFinalizer forKey: @"f"];
+ [atThenFinalizer release];
+ [aFailFinalizer release];
+ if (called_) {
+ [self _run];
+ }
+ }
+ return self;
+}
+
+- (HLDeferred *) takeResult: (id)aResult
+{
+ // NSLog(@"%@ in %@", self, NSStringFromSelector(_cmd));
+ [self _startRun: aResult];
+ return self;
+}
+
+- (HLDeferred *) takeError: (id)anError
+{
+ // NSLog(@"%@ in %@", self, NSStringFromSelector(_cmd));
+ id err = anError;
+ if (![err isKindOfClass: [HLFailure class]]) {
+ err = [[[HLFailure alloc] initWithValue: anError] autorelease];
+ }
+ [self _startRun: err];
+ return self;
+}
+
+- (void) cancel
+{
+ if (! called_) {
+ if (canceller_) {
+ [canceller_ deferredWillCancel: self];
+ } else {
+ suppressAlreadyCalled_ = YES;
+ }
+ if (! called_) {
+ [self takeError: kHLDeferredCancelled];
+ }
+ } else if ([result_ isKindOfClass: [HLDeferred class]]) {
+ [result_ cancel];
+ }
+}
+
+/*
+ #pragma mark -
+ #pragma mark Private API: processing machinery
+ */
+
+- (void) _startRun: (id)aResult
+{
+ // NSLog(@"%@ in %@", self, NSStringFromSelector(_cmd));
+ if (finalized_) {
+ @throw [NSException exceptionWithName: @"HLDeferredAlreadyCalledException"
+ reason: @"cannot run an HLDeferred object more than once"
+ userInfo: [NSDictionary dictionaryWithObject: self
+ forKey: @"HLDeferred"]];
+ }
+ if (called_) {
+ if (suppressAlreadyCalled_) {
+ suppressAlreadyCalled_ = NO;
+ return;
+ }
+ @throw [NSException exceptionWithName: @"HLDeferredAlreadyCalledException"
+ reason: @"cannot run an HLDeferred object more than once"
+ userInfo: [NSDictionary dictionaryWithObject: self
+ forKey: @"HLDeferred"]];
+ }
+ called_ = YES;
+ [self setResult: aResult];
+ [self _run];
+}
+
+- (void) _run
+{
+ // NSLog(@"%@ in %@", self, NSStringFromSelector(_cmd));
+ if (running_) return;
+ if (pauseCount_ == 0) {
+ // NSLog(@"%@ in %@, not paused", self, NSStringFromSelector(_cmd));
+ NSDictionary *link;
+ while ([callbacks_ count] > 0) {
+ // NSLog(@"%@ in %@, running callback", self, NSStringFromSelector(_cmd));
+ @try {
+ link = [[callbacks_ objectAtIndex: 0] retain];
+ [callbacks_ removeObjectAtIndex: 0];
+ running_ = YES;
+ @try {
+ if ([result_ isKindOfClass: [HLFailure class]]) {
+ FailBlock failBlock = [link objectForKey: @"f"];
+ // NSLog(@"%@ in %@, calling failBlock", self, NSStringFromSelector(_cmd));
+ // NSLog(@"HLDeferred: calling errback, result: '%@'", result_);
+ [self setResult: failBlock ? failBlock((HLFailure *)result_) : result_];
+ } else {
+ ThenBlock thenBlock = [link objectForKey: @"t"];
+ // NSLog(@"%@ in %@, calling thenBlock", self, NSStringFromSelector(_cmd));
+ [self setResult: thenBlock ? thenBlock(result_) : result_];
+ }
+ } @finally {
+ // NSLog(@"%@ in %@, running=NO", self, NSStringFromSelector(_cmd));
+ running_ = NO;
+ }
+ if ([result_ isKindOfClass: [HLDeferred class]]) {
+ // NSLog(@"%@ in %@, result is HLDeferred, pausing", self, NSStringFromSelector(_cmd));
+ // CPLog("callback result is another HLDeferred, pausing: " + self);
+ [self pause];
+ [result_ then: ^(id result) { return [self _continue: result]; }
+ fail: ^(HLFailure *failure) { return [self _continue: failure]; }];
+ break;
+ }
+ } @catch (NSException *e) {
+ // NSLog(@"%@ in %@, caught exception: %@", self, NSStringFromSelector(_cmd), e);
+ [self setResult: [HLFailure wrap: e]];
+ } @finally {
+ [link release];
+ }
+ }
+ if (finalizer_ && (pauseCount_ == 0)) {
+ [callbacks_ addObject: finalizer_];
+ [finalizer_ release]; finalizer_ = nil;
+ finalized_ = YES;
+ [self _run];
+ }
+ }
+ // NSLog(@"%@ in %@, done", self, NSStringFromSelector(_cmd));
+}
+
+- (id) _continue: (id)newResult
+{
+ // NSLog(@"%@ in %@", self, NSStringFromSelector(_cmd));
+ [self setResult: newResult];
+ [self unpause];
+ return result_;
+}
+
+@end
23 HLDeferred/Classes/HLDeferredConcurrentDataSource.h
@@ -0,0 +1,23 @@
+//
+// HLDeferredConcurrentDataSource.h
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDeferredDataSource.h"
+
+@interface HLDeferredConcurrentDataSource : HLDeferredDataSource <HLDeferredCancellable>
+{
+ BOOL executing_;
+ BOOL finished_;
+}
+
+#pragma mark -
+#pragma mark Template methods for subclasses
+
+// -main will run on the main thread via dispatch_async
+- (void) execute;
+
+@end
103 HLDeferred/Classes/HLDeferredConcurrentDataSource.m
@@ -0,0 +1,103 @@
+//
+// HLDeferredConcurrentDataSource.m
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDeferredConcurrentDataSource.h"
+
+@implementation HLDeferredConcurrentDataSource
+
+- (id) init
+{
+ self = [super init];
+ if (self != nil) {
+ executing_ = NO;
+ finished_ = NO;
+ }
+ return self;
+}
+
+// override this method to perform work
+- (void) execute
+{
+ [super execute];
+}
+
+#pragma mark -
+#pragma mark HLDeferred support
+
+- (void) markOperationCompleted
+{
+ [self willChangeValueForKey: @"isFinished"];
+ [self willChangeValueForKey: @"isExecuting"];
+
+ executing_ = NO;
+ finished_ = YES;
+
+ [self didChangeValueForKey: @"isExecuting"];
+ [self didChangeValueForKey: @"isFinished"];
+}
+
+#pragma mark -
+#pragma mark Concurrent NSOperation support
+
+- (BOOL) isConcurrent { return YES; }
+- (BOOL) isExecuting { return executing_; }
+- (BOOL) isFinished { return finished_; }
+
+// DO NOT OVERRIDE THIS METHOD
+// override -execute instead
+- (void) start
+{
+ if ([self isCancelled]) {
+ [self willChangeValueForKey: @"isFinished"];
+ finished_ = YES;
+ [self didChangeValueForKey: @"isFinished"];
+ } else {
+ [self main];
+ }
+}
+
+// DO NOT OVERRIDE THIS METHOD
+// override -execute instead
+- (void) main
+{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self willChangeValueForKey: @"isExecuting"];
+ NSException *thrown = nil;
+ @try {
+ [self execute];
+ } @catch (NSException *e) {
+ thrown = e;
+ }
+ executing_ = YES;
+ [self didChangeValueForKey: @"isExecuting"];
+ // this is down here after didChangeValueForKey because
+ // we don't want to call asyncCompleteOperationError
+ // before didChangeValueForKey, because
+ // asyncCompleteOperationError calls markOperationCompleted
+ // which does its own will/didChangeValueForKey for isExecuting
+ if (thrown) {
+ [self setError: thrown];
+ [self asyncCompleteOperationError];
+ }
+ });
+}
+
+- (void) deferredWillCancel: (HLDeferred *)d
+{
+ [self willChangeValueForKey: @"isFinished"];
+ [self willChangeValueForKey: @"isExecuting"];
+
+ executing_ = NO;
+ finished_ = YES;
+
+ [self didChangeValueForKey: @"isExecuting"];
+ [self didChangeValueForKey: @"isFinished"];
+ [super deferredWillCancel: d];
+}
+
+@end
73 HLDeferred/Classes/HLDeferredDataSource.h
@@ -0,0 +1,73 @@
+//
+// HLDeferredDataSource.h
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDeferred.h"
+
+// Subclassing note:
+
+// DO NOT OVERRIDE -start OR -main unless you're SURE it's okay.
+// HLDeferredDataSource and HLDeferredConcurrentDataSource are
+// designed to do the right thing if you override -execute.
+
+@interface HLDeferredDataSource : NSOperation <HLDeferredCancellable>
+{
+ id error_;
+ id result_;
+ HLDeferred *deferred_;
+}
+
+@property (nonatomic, retain) id result;
+@property (nonatomic, retain) id error;
+
+#pragma mark -
+#pragma mark HLDeferred support
+
+// This NSOperation will be added to and retained by the queue, so it is
+// safe to release this operation after calling this method.
+// Note: the returned HLDeferred is retained by this operation, so you
+// are not required to retain the returned HLDeferred to ensure it and its
+// callbacks survive until the operation is complete.
+//
+// Note that this method returns an HLDeferred and the name of the method
+// starts with "request". This is a coding convention we suggest you follow:
+// "The name of methods returning (HLDeferred *) should start with request"
+- (HLDeferred *) requestStartOnQueue: (NSOperationQueue *)queue;
+
+// sends -takeResult: to the HLDeferred, on the main thread using dispatch_async
+// the result is from [self result], so call -setResult: before you call this
+- (void) asyncCompleteOperationResult;
+
+// sends -takeError: to the HLDeferred, on the main thread using dispatch_async
+// the error is from [self error], so call -setError: before you call this
+- (void) asyncCompleteOperationError;
+
+#pragma mark -
+#pragma mark Template methods for subclasses
+
+// Override this and do what you need to do.
+// The default implementation does nothing.
+// Your execute method should:
+// - call -setResult: then -asyncCompleteOperationResult
+// or
+// - call -setError: then -asyncCompleteOperationError
+// DO NOT CALL THIS YOURSELF
+- (void) execute;
+
+// Override this to cancel the task started by execute
+// DO NOT CALL THIS YOURSELF except to call super, which
+// you MUST do at the end of your implementation if you
+// override this
+- (void) deferredWillCancel: (HLDeferred *)d;
+
+// Optionally override this
+// Called after the HLDeferred's callback chain has run.
+// DO NOT CALL THIS YOURSELF except to call super, which
+// you MUST do if you override this
+- (void) markOperationCompleted;
+
+@end
84 HLDeferred/Classes/HLDeferredDataSource.m
@@ -0,0 +1,84 @@
+//
+// HLDeferredDataSource.m
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDeferredDataSource.h"
+
+@implementation HLDeferredDataSource
+
+@synthesize result=result_;
+@synthesize error=error_;
+
+- (id) init
+{
+ self = [super init];
+ if (self != nil) {
+ error_ = nil;
+ result_ = nil;
+ deferred_ = [[HLDeferred alloc] initWithCanceller: self];
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ [error_ release]; error_ = nil;
+ [result_ release]; result_ = nil;
+ [deferred_ setCanceller: nil];
+ [deferred_ release]; deferred_ = nil;
+ [super dealloc];
+}
+
+- (HLDeferred *) requestStartOnQueue: (NSOperationQueue *)queue
+{
+ [queue addOperation: self];
+ return [[deferred_ retain] autorelease];
+}
+
+// override this method in your subclass to perform work
+- (void) execute {}
+
+// DO NOT OVERRIDE THIS METHOD
+// DO NOT OVERRIDE -start EITHER
+// override -execute instead
+- (void) main
+{
+ @try {
+ [self execute];
+ } @catch (NSException * e) {
+ [self setError: e];
+ [self asyncCompleteOperationError];
+ }
+}
+
+#pragma mark -
+#pragma mark HLDeferred support
+
+- (void) deferredWillCancel: (HLDeferred *)d
+{
+ [self cancel];
+}
+
+- (void) markOperationCompleted {}
+
+- (void) asyncCompleteOperationResult
+{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [deferred_ takeResult: [[result_ retain] autorelease]];
+ [self markOperationCompleted];
+ });
+}
+
+- (void) asyncCompleteOperationError
+{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [deferred_ takeError: [[error_ retain] autorelease]];
+ [self markOperationCompleted];
+ });
+}
+
+@end
32 HLDeferred/Classes/HLDeferredList.h
@@ -0,0 +1,32 @@
+//
+// HLDeferredList.h
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDeferred.h"
+
+@interface HLDeferredList : HLDeferred
+{
+ NSArray *deferreds_;
+ NSMutableArray *results_;
+ BOOL fireOnFirstResult_;
+ BOOL fireOnFirstError_;
+ BOOL consumeErrors_;
+ NSUInteger finishedCount_;
+}
+
+- (id) initWithDeferreds: (NSArray *)list
+ fireOnFirstResult: (BOOL)flFireOnFirstResult
+ fireOnFirstError: (BOOL)flFireOnFirstError
+ consumeErrors: (BOOL)flConsumeErrors;
+
+- (id) initWithDeferreds: (NSArray *)list;
+- (id) initWithDeferreds: (NSArray *)list fireOnFirstResult: (BOOL)flFireOnFirstResult;
+- (id) initWithDeferreds: (NSArray *)list fireOnFirstResult: (BOOL)flFireOnFirstResult consumeErrors: (BOOL)flConsumeErrors;
+- (id) initWithDeferreds: (NSArray *)list fireOnFirstError: (BOOL)flFireOnFirstError;
+- (id) initWithDeferreds: (NSArray *)list consumeErrors: (BOOL)flConsumeErrors;
+
+@end
129 HLDeferred/Classes/HLDeferredList.m
@@ -0,0 +1,129 @@
+//
+// HLDeferredList.m
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDeferredList.h"
+
+NSString * const kHLDeferredListNilSentinel = @"__HLDeferredListNilSentinel__";
+
+@implementation HLDeferredList
+
+- (id) initWithDeferreds: (NSArray *)list
+ fireOnFirstResult: (BOOL)flFireOnFirstResult
+ fireOnFirstError: (BOOL)flFireOnFirstError
+ consumeErrors: (BOOL)flConsumeErrors
+{
+ self = [super init];
+ if (self) {
+ deferreds_ = [(list ? list : [NSArray array]) copy];
+ fireOnFirstResult_ = flFireOnFirstResult;
+ fireOnFirstError_ = flFireOnFirstError;
+ consumeErrors_ = flConsumeErrors;
+ results_ = [[NSMutableArray alloc] initWithCapacity: [list count]];
+ finishedCount_ = 0;
+
+ int i;
+ for (i = 0; i < [deferreds_ count]; i++) {
+ [results_ addObject: [NSNull null]];
+ }
+
+ if (([deferreds_ count] == 0) && (!fireOnFirstResult_)) {
+ [self takeResult: results_];
+ }
+
+ [deferreds_ enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
+ __block ThenBlock deferredCallback = ^(id result) {
+ if ([result isKindOfClass: [HLDeferred class]]) {
+ [result both: deferredCallback];
+ return result;
+ };
+ [results_ replaceObjectAtIndex: idx withObject: result ? result : [NSNull null]];
+ ++finishedCount_;
+ BOOL succeeded = ![result isKindOfClass: [HLFailure class]];
+ if (![self isCalled]) {
+ if (succeeded && fireOnFirstResult_)
+ [self takeResult: result];
+ else if (!succeeded && fireOnFirstError_)
+ [self takeError: result];
+ else if (finishedCount_ == [results_ count])
+ [self takeResult: results_];
+ }
+
+ if ((!succeeded) && consumeErrors_) {
+ result = nil;
+ }
+ return result;
+ };
+ [obj both: deferredCallback];
+ }];
+ }
+ return self;
+}
+
+- (id) initWithDeferreds: (NSArray *)list
+{
+ self = [self initWithDeferreds: list
+ fireOnFirstResult: NO
+ fireOnFirstError: NO
+ consumeErrors: NO];
+ return self;
+}
+
+- (id) initWithDeferreds: (NSArray *)list fireOnFirstResult: (BOOL)flFireOnFirstResult
+{
+ self = [self initWithDeferreds: list
+ fireOnFirstResult: flFireOnFirstResult
+ fireOnFirstError: NO
+ consumeErrors: NO];
+ return self;
+}
+
+- (id) initWithDeferreds: (NSArray *)list fireOnFirstResult: (BOOL)flFireOnFirstResult consumeErrors: (BOOL)flConsumeErrors
+{
+ self = [self initWithDeferreds: list
+ fireOnFirstResult: flFireOnFirstResult
+ fireOnFirstError: NO
+ consumeErrors: flConsumeErrors];
+ return self;
+}
+
+- (id) initWithDeferreds: (NSArray *)list fireOnFirstError: (BOOL)flFireOnFirstError
+{
+ self = [self initWithDeferreds: list
+ fireOnFirstResult: NO
+ fireOnFirstError: flFireOnFirstError
+ consumeErrors: NO];
+ return self;
+}
+
+- (id) initWithDeferreds: (NSArray *)list consumeErrors: (BOOL)flConsumeErrors
+{
+ self = [self initWithDeferreds: list
+ fireOnFirstResult: NO
+ fireOnFirstError: NO
+ consumeErrors: flConsumeErrors];
+ return self;
+}
+
+- (void) dealloc
+{
+ [deferreds_ release]; deferreds_ = nil;
+ [results_ release]; results_ = nil;
+ [super dealloc];
+}
+
+- (void) cancel
+{
+ for (HLDeferred *d in deferreds_) {
+ [d cancel];
+ }
+ [deferreds_ release]; deferreds_ = [[NSArray array] copy];
+ [results_ removeAllObjects];
+ finishedCount_ = 0;
+}
+
+@end
18 HLDeferred/Classes/HLDownloadDataSource.h
@@ -0,0 +1,18 @@
+//
+// HLDownloadDataSource.h
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLURLDataSource.h"
+
+@interface HLDownloadDataSource : HLURLDataSource
+{
+ NSFileHandle *fileHandle_;
+}
+
+- (id) initWithSourceURL: (NSURL *)sourceURL destinationPath: (NSString *)destinationPath;
+
+@end
110 HLDeferred/Classes/HLDownloadDataSource.m
@@ -0,0 +1,110 @@
+//
+// HLDownloadDataSource.m
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDownloadDataSource.h"
+
+
+@implementation HLDownloadDataSource
+
+- (id) initWithSourceURL: (NSURL *)sourceURL destinationPath: (NSString *)destinationPath
+{
+ self = [super initWithContext: [NSDictionary dictionaryWithObjectsAndKeys:
+ sourceURL, @"sourceURL",
+ destinationPath, @"destinationPath",
+ nil]];
+ if (self) {
+ fileHandle_ = nil;
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ [fileHandle_ release]; fileHandle_ = nil;
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Private API
+
+- (NSURL *) sourceURL
+{
+ return [[self context] objectForKey: @"sourceURL"];
+}
+
+- (NSString *) destinationPath
+{
+ return [[self context] objectForKey: @"destinationPath"];
+}
+
+#pragma mark -
+#pragma mark Public API: template methods
+
+- (NSURLRequest *) urlRequest
+{
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: [self sourceURL]
+ cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
+ timeoutInterval: 240.0];
+ [request setHTTPMethod: @"GET"];
+ [request setValue: @"gzip" forHTTPHeaderField: @"Accept-Encoding"];
+ return request;
+}
+
+- (void) execute
+{
+ if ([[NSFileManager defaultManager] fileExistsAtPath: [self destinationPath]]) {
+ [self setResult: [self destinationPath]];
+ [self asyncCompleteOperationResult];
+ } else {
+ [super execute];
+ }
+}
+
+- (BOOL) responseBegan
+{
+ if ([super responseBegan]) {
+ if ([[NSFileManager defaultManager] createFileAtPath: [self destinationPath]
+ contents: [NSData data]
+ attributes: nil]) {
+ fileHandle_ = [[NSFileHandle fileHandleForWritingAtPath: [self destinationPath]] retain];
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (void) responseReceivedData: (NSData *)data
+{
+ if (fileHandle_) {
+ [fileHandle_ writeData: data];
+ }
+}
+
+- (void) responseFinished
+{
+ if (fileHandle_) {
+ [fileHandle_ release]; fileHandle_ = nil;
+ [self setResult: [self destinationPath]];
+ }
+ [super responseFinished];
+}
+
+#pragma mark -
+#pragma mark NSOperation support
+
+- (void) deferredWillCancel: (HLDeferred *)d
+{
+ if (fileHandle_) {
+ [fileHandle_ release]; fileHandle_ = nil;
+ [[NSFileManager defaultManager] removeItemAtPath: [self destinationPath]
+ error: NULL];
+ }
+ [super deferredWillCancel: d];
+}
+
+@end
19 HLDeferred/Classes/HLFailure.h
@@ -0,0 +1,19 @@
+//
+// HLFailure.h
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+@interface HLFailure : NSObject
+{
+ id value_;
+}
+
++ (HLFailure *) wrap: (id)v;
+
+- (id) initWithValue: (id)v;
+- (id) value;
+
+@end
41 HLDeferred/Classes/HLFailure.m
@@ -0,0 +1,41 @@
+//
+// HLFailure.m
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLFailure.h"
+
+@implementation HLFailure
+
+- (id) initWithValue: (id)v
+{
+ self = [super init];
+ if (self) {
+ value_ = [v retain];
+ }
+ return self;
+}
+
+- (id) init
+{
+ self = [self initWithValue: nil];
+ return self;
+}
+
+- (void) dealloc
+{
+ [value_ release]; value_ = nil;
+ [super dealloc];
+}
+
++ (HLFailure *) wrap: (id)v
+{
+ return [[[[self class] alloc] initWithValue: v] autorelease];
+}
+
+- (id) value { return value_; }
+
+@end
13 HLDeferred/Classes/HLJSONDataSource.h
@@ -0,0 +1,13 @@
+//
+// HLJSONDataSource.h
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLURLDataSource.h"
+
+@interface HLJSONDataSource : HLURLDataSource
+
+@end
34 HLDeferred/Classes/HLJSONDataSource.m
@@ -0,0 +1,34 @@
+//
+// HLJSONDataSource.m
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLJSONDataSource.h"
+#import <YAJL/YAJL.h>
+
+@implementation HLJSONDataSource
+
+- (void) responseFinished
+{
+ NSError *error = nil;
+ if ([self responseData]) {
+ id result = [[self responseData] yajl_JSON: &error];
+ if (!error) {
+ [[self responseData] setLength: 0];
+ [self setResult: result];
+ [self asyncCompleteOperationResult];
+ return;
+ } else {
+ [self setError: error];
+ [self asyncCompleteOperationError];
+ }
+ } else {
+ [self setError: @"Not Found"];
+ [self asyncCompleteOperationError];
+ }
+}
+
+@end
41 HLDeferred/Classes/HLURLDataSource.h
@@ -0,0 +1,41 @@
+//
+// HLURLDataSource.h
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDeferredConcurrentDataSource.h"
+
+@interface HLURLDataSource : HLDeferredConcurrentDataSource
+{
+ NSURLConnection *conn_;
+ NSURLResponse *response_;
+ NSMutableData *responseData_;
+ NSDictionary *context_;
+}
+
+@property (nonatomic, retain) NSDictionary *context;
+@property (nonatomic, retain) NSMutableData *responseData;
+
+// designated initializer
+- (id) initWithContext: (NSDictionary *)aContext;
+
+// convenience initializers
+- (id) initWithURL: (NSURL *)url;
+- (id) initWithURLString: (NSString *)urlString;
+
+- (NSInteger) responseStatusCode;
+
+#pragma mark -
+#pragma mark Public API: template methods, override these to customize behaviour (do NOT call directly)
+
+- (NSURLRequest *) urlRequest;
+
+- (BOOL) responseBegan;
+- (void) responseReceivedData: (NSData *)data;
+- (void) responseFinished;
+- (void) responseFailed;
+
+@end
164 HLDeferred/Classes/HLURLDataSource.m
@@ -0,0 +1,164 @@
+//
+// HLURLDataSource.m
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLURLDataSource.h"
+
+@implementation HLURLDataSource
+
+@synthesize context=context_;
+@synthesize responseData=responseData_;
+
+- (id) initWithContext: (NSDictionary *)aContext
+{
+ self = [super init];
+ if (self) {
+ conn_ = nil;
+ response_ = nil;
+ responseData_ = nil;
+ context_ = [aContext retain];
+ }
+ return self;
+}
+
+- (id) initWithURL: (NSURL *)url
+{
+ self = [self initWithContext: [NSDictionary dictionaryWithObject: url forKey: @"requestURL"]];
+ return self;
+}
+
+- (id) initWithURLString: (NSString *)urlString
+{
+ self = [self initWithContext: [NSDictionary dictionaryWithObject: urlString forKey: @"requestURL"]];
+ return self;
+}
+
+- (void) dealloc
+{
+ [conn_ release]; conn_ = nil;
+ [response_ release]; response_ = nil;
+ [responseData_ release]; responseData_ = nil;
+ [context_ release]; context_ = nil;
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Public API: template methods, override these to customize behaviour
+
+- (NSURLRequest *) urlRequest
+{
+ NSMutableURLRequest *result = nil;
+ id requestURL = [context_ objectForKey: @"requestURL"];
+ if (requestURL) {
+ if ([requestURL isKindOfClass: [NSString class]]) {
+ requestURL = [NSURL URLWithString: requestURL];
+ }
+ result = [NSMutableURLRequest requestWithURL: requestURL
+ cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
+ timeoutInterval: 240.0];
+ // allow response to be gzip compressed
+ [result setValue: @"gzip" forHTTPHeaderField: @"Accept-Encoding"];
+ NSString *requestMethod = [context_ objectForKey: @"requestMethod"];
+ [result setHTTPMethod: requestMethod ? requestMethod : @"GET"];
+ NSData *requestBody = [context_ objectForKey: @"requestBody"];
+ if (requestBody) [result setHTTPBody: requestBody];
+ NSString *requestBodyContentType = [context_ objectForKey: @"requestBodyContentType"];
+ if (requestBodyContentType) [result setValue: requestBodyContentType forHTTPHeaderField: @"content-type"];
+ }
+ return result;
+}
+
+- (void) execute
+{
+ [conn_ release];
+ conn_ = [[NSURLConnection alloc] initWithRequest: [self urlRequest]
+ delegate: self];
+}
+
+- (void) deferredWillCancel: (HLDeferred *)d
+{
+ [conn_ cancel];
+ [super deferredWillCancel: d];
+}
+
+- (void) responseFailed
+{
+ [self asyncCompleteOperationError];
+}
+
+- (BOOL) responseBegan
+{
+ BOOL result = NO;
+ if ([response_ isKindOfClass: [NSHTTPURLResponse class]]) {
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response_;
+ NSInteger statusCode = [httpResponse statusCode];
+ if ((statusCode >= 200) && (statusCode < 300)) {
+ [responseData_ release];
+ responseData_ = [[NSMutableData alloc] init]; // YAY!
+ result = YES;
+ }
+ } else {
+ [responseData_ release];
+ responseData_ = [[NSMutableData alloc] init]; // YAY!
+ result = YES;
+ }
+ return result;
+}
+
+- (void) responseReceivedData: (NSData *)data
+{
+ if (responseData_) {
+ [responseData_ appendData: data];
+ }
+}
+
+- (void) responseFinished
+{
+ [self asyncCompleteOperationResult];
+}
+
+#pragma mark -
+#pragma mark Private API
+
+- (void) connection: (NSURLConnection *)connection didFailWithError: (id)anError
+{
+ [self setError: anError];
+ [self responseFailed];
+}
+
+- (void) connection: (NSURLConnection *)connection didReceiveResponse: (NSURLResponse *)aResponse
+{
+ [response_ release];
+ response_ = [aResponse retain];
+ [self responseBegan];
+}
+
+- (void) connection: (NSURLConnection *)connection didReceiveData: (NSData *)data
+{
+ [self responseReceivedData: data];
+}
+
+- (void) connectionDidFinishLoading: (NSURLConnection *)connection
+{
+ [self setResult: responseData_];
+ [self responseFinished];
+}
+
+#pragma mark -
+#pragma mark Public API
+
+- (NSInteger) responseStatusCode
+{
+ NSInteger result = 0;
+ if ([response_ isKindOfClass: [NSHTTPURLResponse class]]) {
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response_;
+ result = [httpResponse statusCode];
+ }
+ return result;
+}
+
+@end
1  HLDeferred/Frameworks/.gitignore
@@ -0,0 +1 @@
+*.framework
525 HLDeferred/HLDeferred.xcodeproj/project.pbxproj
@@ -0,0 +1,525 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 45;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 388531B51300795D00A256DB /* HLDownloadDataSourceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 388531B41300795D00A256DB /* HLDownloadDataSourceTest.m */; };
+ 388531E013007DAD00A256DB /* HLJSONDataSourceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 388531DF13007DAD00A256DB /* HLJSONDataSourceTest.m */; };
+ 388532951300859100A256DB /* YAJL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A79B87F912F9DCAD0048464F /* YAJL.framework */; };
+ 3885A63412FBC56900BA5864 /* HLDeferredDataSourceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3885A63312FBC56900BA5864 /* HLDeferredDataSourceTest.m */; };
+ 3885A67912FC783000BA5864 /* HLDeferredConcurrentDataSourceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3885A67812FC783000BA5864 /* HLDeferredConcurrentDataSourceTest.m */; };
+ 38CB29B312FDF0BD00739549 /* HLURLDataSourceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 38CB29B212FDF0BD00739549 /* HLURLDataSourceTest.m */; };
+ A7171D8112F8BBC800FF92BB /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7171D8012F8BBC800FF92BB /* CoreGraphics.framework */; };
+ A7171D8212F8BBC800FF92BB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; };
+ A7171D8412F8BBC800FF92BB /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7171D8312F8BBC800FF92BB /* UIKit.framework */; };
+ A7171D8912F8BBDB00FF92BB /* GHUnitIOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7171D5D12F8BB7C00FF92BB /* GHUnitIOS.framework */; };
+ A72B196F12F77215007685CF /* HLDeferred.h in Headers */ = {isa = PBXBuildFile; fileRef = A72B196312F77215007685CF /* HLDeferred.h */; };
+ A72B197012F77215007685CF /* HLDeferred.m in Sources */ = {isa = PBXBuildFile; fileRef = A72B196412F77215007685CF /* HLDeferred.m */; };
+ A72B197112F77215007685CF /* HLDeferredConcurrentDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A72B196512F77215007685CF /* HLDeferredConcurrentDataSource.h */; };
+ A72B197212F77215007685CF /* HLDeferredConcurrentDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A72B196612F77215007685CF /* HLDeferredConcurrentDataSource.m */; };
+ A72B197312F77215007685CF /* HLDownloadDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A72B196712F77215007685CF /* HLDownloadDataSource.h */; };
+ A72B197412F77215007685CF /* HLDownloadDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A72B196812F77215007685CF /* HLDownloadDataSource.m */; };
+ A72B197512F77215007685CF /* HLJSONDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A72B196912F77215007685CF /* HLJSONDataSource.h */; };
+ A72B197712F77215007685CF /* HLDeferredDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A72B196B12F77215007685CF /* HLDeferredDataSource.h */; };
+ A72B197812F77215007685CF /* HLDeferredDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A72B196C12F77215007685CF /* HLDeferredDataSource.m */; };
+ A72B197912F77215007685CF /* HLURLDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A72B196D12F77215007685CF /* HLURLDataSource.h */; };
+ A72B197A12F77215007685CF /* HLURLDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A72B196E12F77215007685CF /* HLURLDataSource.m */; };
+ A7578F0512FB2E4300D52C21 /* HLFailure.h in Headers */ = {isa = PBXBuildFile; fileRef = A7578F0312FB2E4300D52C21 /* HLFailure.h */; };
+ A7578F0612FB2E4300D52C21 /* HLFailure.m in Sources */ = {isa = PBXBuildFile; fileRef = A7578F0412FB2E4300D52C21 /* HLFailure.m */; };
+ A75790EE12FB7B3500D52C21 /* HLDeferredListTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A75790ED12FB7B3500D52C21 /* HLDeferredListTest.m */; };
+ A757910412FB7C4C00D52C21 /* HLDeferredList.m in Sources */ = {isa = PBXBuildFile; fileRef = A757910312FB7C4C00D52C21 /* HLDeferredList.m */; };
+ A757910612FB7C7400D52C21 /* HLDeferredList.h in Headers */ = {isa = PBXBuildFile; fileRef = A757910512FB7C5A00D52C21 /* HLDeferredList.h */; };
+ A764F5C812F8C1560002D17E /* libHLDeferred.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2AAC07E0554694100DB518D /* libHLDeferred.a */; };
+ A76B841712F8BD2200ACDD85 /* GHUnitIOSTestMain.m in Sources */ = {isa = PBXBuildFile; fileRef = A76B841612F8BD2200ACDD85 /* GHUnitIOSTestMain.m */; };
+ A76B842C12F8BE8000ACDD85 /* HLDeferredTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A76B842B12F8BE8000ACDD85 /* HLDeferredTest.m */; };
+ A79B87DD12F9DC410048464F /* HLJSONDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A72B196A12F77215007685CF /* HLJSONDataSource.m */; };
+ A79B87FB12F9DCAD0048464F /* YAJL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A79B87F912F9DCAD0048464F /* YAJL.framework */; };
+ AA747D9F0F9514B9006C5449 /* HLDeferred_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = AA747D9E0F9514B9006C5449 /* HLDeferred_Prefix.pch */; };
+ AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ A757912A12FB866C00D52C21 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = D2AAC07D0554694100DB518D;
+ remoteInfo = HLDeferred;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 388531B41300795D00A256DB /* HLDownloadDataSourceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HLDownloadDataSourceTest.m; path = Tests/HLDownloadDataSourceTest.m; sourceTree = "<group>"; };
+ 388531DF13007DAD00A256DB /* HLJSONDataSourceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HLJSONDataSourceTest.m; path = Tests/HLJSONDataSourceTest.m; sourceTree = "<group>"; };
+ 3885A63312FBC56900BA5864 /* HLDeferredDataSourceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HLDeferredDataSourceTest.m; path = Tests/HLDeferredDataSourceTest.m; sourceTree = "<group>"; };
+ 3885A67812FC783000BA5864 /* HLDeferredConcurrentDataSourceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HLDeferredConcurrentDataSourceTest.m; path = Tests/HLDeferredConcurrentDataSourceTest.m; sourceTree = "<group>"; };
+ 38CB29B212FDF0BD00739549 /* HLURLDataSourceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HLURLDataSourceTest.m; path = Tests/HLURLDataSourceTest.m; sourceTree = "<group>"; };
+ A7171D5D12F8BB7C00FF92BB /* GHUnitIOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GHUnitIOS.framework; path = Frameworks/GHUnitIOS.framework; sourceTree = "<group>"; };
+ A7171D7A12F8BBA700FF92BB /* Tests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Tests.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ A7171D7C12F8BBA700FF92BB /* HLDeferredTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "HLDeferredTests-Info.plist"; path = "Tests/HLDeferredTests-Info.plist"; sourceTree = "<group>"; };
+ A7171D8012F8BBC800FF92BB /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ A7171D8312F8BBC800FF92BB /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ A72B196312F77215007685CF /* HLDeferred.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HLDeferred.h; path = Classes/HLDeferred.h; sourceTree = "<group>"; };
+ A72B196412F77215007685CF /* HLDeferred.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLDeferred.m; sourceTree = "<group>"; };
+ A72B196512F77215007685CF /* HLDeferredConcurrentDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HLDeferredConcurrentDataSource.h; path = Classes/HLDeferredConcurrentDataSource.h; sourceTree = "<group>"; };
+ A72B196612F77215007685CF /* HLDeferredConcurrentDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLDeferredConcurrentDataSource.m; sourceTree = "<group>"; };
+ A72B196712F77215007685CF /* HLDownloadDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HLDownloadDataSource.h; path = Classes/HLDownloadDataSource.h; sourceTree = "<group>"; };
+ A72B196812F77215007685CF /* HLDownloadDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLDownloadDataSource.m; sourceTree = "<group>"; };
+ A72B196912F77215007685CF /* HLJSONDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HLJSONDataSource.h; path = Classes/HLJSONDataSource.h; sourceTree = "<group>"; };
+ A72B196A12F77215007685CF /* HLJSONDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLJSONDataSource.m; sourceTree = "<group>"; };
+ A72B196B12F77215007685CF /* HLDeferredDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HLDeferredDataSource.h; path = Classes/HLDeferredDataSource.h; sourceTree = "<group>"; };
+ A72B196C12F77215007685CF /* HLDeferredDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLDeferredDataSource.m; sourceTree = "<group>"; };
+ A72B196D12F77215007685CF /* HLURLDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HLURLDataSource.h; path = Classes/HLURLDataSource.h; sourceTree = "<group>"; };
+ A72B196E12F77215007685CF /* HLURLDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLURLDataSource.m; sourceTree = "<group>"; };
+ A7578F0312FB2E4300D52C21 /* HLFailure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HLFailure.h; path = Classes/HLFailure.h; sourceTree = "<group>"; };
+ A7578F0412FB2E4300D52C21 /* HLFailure.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLFailure.m; sourceTree = "<group>"; };
+ A75790ED12FB7B3500D52C21 /* HLDeferredListTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HLDeferredListTest.m; path = Tests/HLDeferredListTest.m; sourceTree = "<group>"; };
+ A757910312FB7C4C00D52C21 /* HLDeferredList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLDeferredList.m; sourceTree = "<group>"; };
+ A757910512FB7C5A00D52C21 /* HLDeferredList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HLDeferredList.h; path = Classes/HLDeferredList.h; sourceTree = "<group>"; };
+ A76B841612F8BD2200ACDD85 /* GHUnitIOSTestMain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GHUnitIOSTestMain.m; path = Tests/GHUnitIOSTestMain.m; sourceTree = "<group>"; };
+ A76B841F12F8BD4C00ACDD85 /* HLDeferredTests_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HLDeferredTests_Prefix.pch; path = Tests/HLDeferredTests_Prefix.pch; sourceTree = "<group>"; };
+ A76B842B12F8BE8000ACDD85 /* HLDeferredTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HLDeferredTest.m; path = Tests/HLDeferredTest.m; sourceTree = "<group>"; };
+ A79B87F912F9DCAD0048464F /* YAJL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = YAJL.framework; path = Frameworks/YAJL.framework; sourceTree = "<group>"; };
+ AA747D9E0F9514B9006C5449 /* HLDeferred_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HLDeferred_Prefix.pch; sourceTree = SOURCE_ROOT; };
+ AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ D2AAC07E0554694100DB518D /* libHLDeferred.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libHLDeferred.a; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ A7171D7812F8BBA700FF92BB /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A7171D8912F8BBDB00FF92BB /* GHUnitIOS.framework in Frameworks */,
+ A7171D8112F8BBC800FF92BB /* CoreGraphics.framework in Frameworks */,
+ A7171D8212F8BBC800FF92BB /* Foundation.framework in Frameworks */,
+ A7171D8412F8BBC800FF92BB /* UIKit.framework in Frameworks */,
+ A764F5C812F8C1560002D17E /* libHLDeferred.a in Frameworks */,
+ 388532951300859100A256DB /* YAJL.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ D2AAC07C0554694100DB518D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */,
+ A79B87FB12F9DCAD0048464F /* YAJL.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 034768DFFF38A50411DB9C8B /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ D2AAC07E0554694100DB518D /* libHLDeferred.a */,
+ A7171D7A12F8BBA700FF92BB /* Tests.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 0867D691FE84028FC02AAC07 /* HLDeferred */ = {
+ isa = PBXGroup;
+ children = (
+ A79B88AD12FA0D190048464F /* HLDeferred Headers */,
+ A72B196212F77215007685CF /* Classes */,
+ 32C88DFF0371C24200C91783 /* Other Sources */,
+ A76B841512F8BCE500ACDD85 /* Tests */,
+ 0867D69AFE84028FC02AAC07 /* Frameworks */,
+ 034768DFFF38A50411DB9C8B /* Products */,
+ );
+ name = HLDeferred;
+ sourceTree = "<group>";
+ };
+ 0867D69AFE84028FC02AAC07 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ A79B87F912F9DCAD0048464F /* YAJL.framework */,
+ A7171D8312F8BBC800FF92BB /* UIKit.framework */,
+ A7171D8012F8BBC800FF92BB /* CoreGraphics.framework */,
+ A7171D5D12F8BB7C00FF92BB /* GHUnitIOS.framework */,
+ AACBBE490F95108600F1A2B1 /* Foundation.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 32C88DFF0371C24200C91783 /* Other Sources */ = {
+ isa = PBXGroup;
+ children = (
+ AA747D9E0F9514B9006C5449 /* HLDeferred_Prefix.pch */,
+ );
+ name = "Other Sources";
+ sourceTree = "<group>";
+ };
+ A72B196212F77215007685CF /* Classes */ = {
+ isa = PBXGroup;
+ children = (
+ A79B87F812F9DC610048464F /* Data Sources */,
+ A72B196412F77215007685CF /* HLDeferred.m */,
+ A757910312FB7C4C00D52C21 /* HLDeferredList.m */,
+ A72B196C12F77215007685CF /* HLDeferredDataSource.m */,
+ A72B196612F77215007685CF /* HLDeferredConcurrentDataSource.m */,
+ A7578F0412FB2E4300D52C21 /* HLFailure.m */,
+ );
+ path = Classes;
+ sourceTree = "<group>";
+ };
+ A76B841512F8BCE500ACDD85 /* Tests */ = {
+ isa = PBXGroup;
+ children = (
+ A7171D7C12F8BBA700FF92BB /* HLDeferredTests-Info.plist */,
+ A76B841F12F8BD4C00ACDD85 /* HLDeferredTests_Prefix.pch */,
+ A76B841612F8BD2200ACDD85 /* GHUnitIOSTestMain.m */,
+ A76B842B12F8BE8000ACDD85 /* HLDeferredTest.m */,
+ A75790ED12FB7B3500D52C21 /* HLDeferredListTest.m */,
+ 3885A63312FBC56900BA5864 /* HLDeferredDataSourceTest.m */,
+ 3885A67812FC783000BA5864 /* HLDeferredConcurrentDataSourceTest.m */,
+ 38CB29B212FDF0BD00739549 /* HLURLDataSourceTest.m */,
+ 388531B41300795D00A256DB /* HLDownloadDataSourceTest.m */,
+ 388531DF13007DAD00A256DB /* HLJSONDataSourceTest.m */,
+ );
+ name = Tests;
+ sourceTree = "<group>";
+ };
+ A79B87F812F9DC610048464F /* Data Sources */ = {
+ isa = PBXGroup;
+ children = (
+ A72B196812F77215007685CF /* HLDownloadDataSource.m */,
+ A72B196A12F77215007685CF /* HLJSONDataSource.m */,
+ A72B196E12F77215007685CF /* HLURLDataSource.m */,
+ );
+ name = "Data Sources";
+ sourceTree = "<group>";
+ };
+ A79B88AD12FA0D190048464F /* HLDeferred Headers */ = {
+ isa = PBXGroup;
+ children = (
+ A72B196712F77215007685CF /* HLDownloadDataSource.h */,
+ A72B196912F77215007685CF /* HLJSONDataSource.h */,
+ A72B196D12F77215007685CF /* HLURLDataSource.h */,
+ A72B196312F77215007685CF /* HLDeferred.h */,
+ A757910512FB7C5A00D52C21 /* HLDeferredList.h */,
+ A72B196B12F77215007685CF /* HLDeferredDataSource.h */,
+ A72B196512F77215007685CF /* HLDeferredConcurrentDataSource.h */,
+ A7578F0312FB2E4300D52C21 /* HLFailure.h */,
+ );
+ name = "HLDeferred Headers";
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ D2AAC07A0554694100DB518D /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AA747D9F0F9514B9006C5449 /* HLDeferred_Prefix.pch in Headers */,
+ A72B196F12F77215007685CF /* HLDeferred.h in Headers */,
+ A72B197112F77215007685CF /* HLDeferredConcurrentDataSource.h in Headers */,
+ A72B197312F77215007685CF /* HLDownloadDataSource.h in Headers */,
+ A72B197512F77215007685CF /* HLJSONDataSource.h in Headers */,
+ A72B197712F77215007685CF /* HLDeferredDataSource.h in Headers */,
+ A72B197912F77215007685CF /* HLURLDataSource.h in Headers */,
+ A7578F0512FB2E4300D52C21 /* HLFailure.h in Headers */,
+ A757910612FB7C7400D52C21 /* HLDeferredList.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ A7171D7912F8BBA700FF92BB /* HLDeferredTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = A7171D7F12F8BBA800FF92BB /* Build configuration list for PBXNativeTarget "HLDeferredTests" */;
+ buildPhases = (
+ A7171D7612F8BBA700FF92BB /* Resources */,
+ A7171D7712F8BBA700FF92BB /* Sources */,
+ A7171D7812F8BBA700FF92BB /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ A757912B12FB866C00D52C21 /* PBXTargetDependency */,
+ );
+ name = HLDeferredTests;
+ productName = Tests;
+ productReference = A7171D7A12F8BBA700FF92BB /* Tests.app */;
+ productType = "com.apple.product-type.application";
+ };
+ D2AAC07D0554694100DB518D /* HLDeferred */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "HLDeferred" */;
+ buildPhases = (
+ D2AAC07A0554694100DB518D /* Headers */,
+ D2AAC07B0554694100DB518D /* Sources */,
+ D2AAC07C0554694100DB518D /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = HLDeferred;
+ productName = HLDeferred;
+ productReference = D2AAC07E0554694100DB518D /* libHLDeferred.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 0867D690FE84028FC02AAC07 /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "HLDeferred" */;
+ compatibilityVersion = "Xcode 3.1";
+ developmentRegion = English;
+ hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ );
+ mainGroup = 0867D691FE84028FC02AAC07 /* HLDeferred */;
+ productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ D2AAC07D0554694100DB518D /* HLDeferred */,
+ A7171D7912F8BBA700FF92BB /* HLDeferredTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ A7171D7612F8BBA700FF92BB /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ A7171D7712F8BBA700FF92BB /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A76B841712F8BD2200ACDD85 /* GHUnitIOSTestMain.m in Sources */,
+ A76B842C12F8BE8000ACDD85 /* HLDeferredTest.m in Sources */,
+ A75790EE12FB7B3500D52C21 /* HLDeferredListTest.m in Sources */,
+ 3885A63412FBC56900BA5864 /* HLDeferredDataSourceTest.m in Sources */,
+ 3885A67912FC783000BA5864 /* HLDeferredConcurrentDataSourceTest.m in Sources */,
+ 38CB29B312FDF0BD00739549 /* HLURLDataSourceTest.m in Sources */,
+ 388531B51300795D00A256DB /* HLDownloadDataSourceTest.m in Sources */,
+ 388531E013007DAD00A256DB /* HLJSONDataSourceTest.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ D2AAC07B0554694100DB518D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A72B197012F77215007685CF /* HLDeferred.m in Sources */,
+ A757910412FB7C4C00D52C21 /* HLDeferredList.m in Sources */,
+ A72B197212F77215007685CF /* HLDeferredConcurrentDataSource.m in Sources */,
+ A72B197412F77215007685CF /* HLDownloadDataSource.m in Sources */,
+ A72B197812F77215007685CF /* HLDeferredDataSource.m in Sources */,
+ A72B197A12F77215007685CF /* HLURLDataSource.m in Sources */,
+ A79B87DD12F9DC410048464F /* HLJSONDataSource.m in Sources */,
+ A7578F0612FB2E4300D52C21 /* HLFailure.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ A757912B12FB866C00D52C21 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = D2AAC07D0554694100DB518D /* HLDeferred */;
+ targetProxy = A757912A12FB866C00D52C21 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 1DEB921F08733DC00010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ COPY_PHASE_STRIP = NO;
+ DSTROOT = /tmp/HLDeferred.dst;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/Frameworks\"",
+ );
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = HLDeferred_Prefix.pch;
+ INSTALL_PATH = /usr/local/lib;
+ PRODUCT_NAME = HLDeferred;
+ };
+ name = Debug;
+ };
+ 1DEB922008733DC00010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ DSTROOT = /tmp/HLDeferred.dst;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/Frameworks\"",
+ );
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = HLDeferred_Prefix.pch;
+ INSTALL_PATH = /usr/local/lib;
+ PRODUCT_NAME = HLDeferred;
+ };
+ name = Release;
+ };
+ 1DEB922308733DC00010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ FRAMEWORK_SEARCH_PATHS = "";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.0;
+ OTHER_LDFLAGS = (
+ "-ObjC",
+ "-all_load",
+ );
+ PREBINDING = NO;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 1DEB922408733DC00010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ FRAMEWORK_SEARCH_PATHS = "";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.0;
+ OTHER_LDFLAGS = (
+ "-ObjC",
+ "-all_load",
+ );
+ PREBINDING = NO;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ A7171D7D12F8BBA800FF92BB /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/Frameworks\"",
+ );
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = Tests/HLDeferredTests_Prefix.pch;
+ INFOPLIST_FILE = "Tests/HLDeferredTests-Info.plist";
+ INSTALL_PATH = "$(HOME)/Applications";
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ UIKit,
+ "-ObjC",
+ "-all_load",
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = Tests;
+ SDKROOT = iphoneos;
+ };
+ name = Debug;
+ };
+ A7171D7E12F8BBA800FF92BB /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/Frameworks\"",
+ );
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = Tests/HLDeferredTests_Prefix.pch;
+ INFOPLIST_FILE = "Tests/HLDeferredTests-Info.plist";
+ INSTALL_PATH = "$(HOME)/Applications";
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ UIKit,
+ "-ObjC",
+ "-all_load",
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = Tests;
+ SDKROOT = iphoneos;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "HLDeferred" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB921F08733DC00010E9CD /* Debug */,
+ 1DEB922008733DC00010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "HLDeferred" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB922308733DC00010E9CD /* Debug */,
+ 1DEB922408733DC00010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ A7171D7F12F8BBA800FF92BB /* Build configuration list for PBXNativeTarget "HLDeferredTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ A7171D7D12F8BBA800FF92BB /* Debug */,
+ A7171D7E12F8BBA800FF92BB /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
+}
13 HLDeferred/HLDeferred_Prefix.pch
@@ -0,0 +1,13 @@
+//
+// HLDeferred_Prefix.pch
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+// Prefix header for all source files of the 'CocoaTouchStaticLibrary' target in the 'CocoaTouchStaticLibrary' project.
+//
+
+#ifdef __OBJC__
+ #import <Foundation/Foundation.h>
+#endif
82 HLDeferred/Tests/GHUnitIOSTestMain.m
@@ -0,0 +1,82 @@
+//
+// GHUnitIOSTestMain.m
+// GHUnitIPhone
+//
+// Created by Gabriel Handford on 1/25/09.
+// Copyright 2009. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <UIKit/UIKit.h>
+
+// If you are using the framework
+#import <GHUnitIOS/GHUnit.h>
+// If you are using the static library and importing header files manually
+//#import "GHUnit.h"
+
+// Default exception handler
+void exceptionHandler(NSException *exception) {
+ NSLog(@"%@\n%@", [exception reason], GHUStackTraceFromException(exception));
+}
+
+int main(int argc, char *argv[]) {
+
+ /*!
+ For debugging:
+ Go into the "Get Info" contextual menu of your (test) executable (inside the "Executables" group in the left panel of XCode).
+ Then go in the "Arguments" tab. You can add the following environment variables:
+
+ Default: Set to:
+ NSDebugEnabled NO "YES"
+ NSZombieEnabled NO "YES"
+ NSDeallocateZombies NO "YES"
+ NSHangOnUncaughtException NO "YES"
+
+ NSEnableAutoreleasePool YES "NO"
+ NSAutoreleaseFreedObjectCheckEnabled NO "YES"
+ NSAutoreleaseHighWaterMark 0 non-negative integer
+ NSAutoreleaseHighWaterResolution 0 non-negative integer
+
+ For info on these varaiables see NSDebug.h; http://theshadow.uw.hu/iPhoneSDKdoc/Foundation.framework/NSDebug.h.html
+
+ For malloc debugging see: http://developer.apple.com/mac/library/documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html
+ */
+
+ NSSetUncaughtExceptionHandler(&exceptionHandler);
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ // Register any special test case classes
+ //[[GHTesting sharedInstance] registerClassName:@"GHSpecialTestCase"];
+
+ int retVal = 0;
+ // If GHUNIT_CLI is set we are using the command line interface and run the tests
+ // Otherwise load the GUI app
+ if (getenv("GHUNIT_CLI")) {
+ retVal = [GHTestRunner run];
+ } else {
+ retVal = UIApplicationMain(argc, argv, nil, @"GHUnitIPhoneAppDelegate");
+ }
+ [pool release];
+ return retVal;
+}
70 HLDeferred/Tests/HLDeferredConcurrentDataSourceTest.m
@@ -0,0 +1,70 @@
+//
+// HLDeferredConcurrentDataSourceTest.m
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDeferredConcurrentDataSource.h"
+
+@interface HLDeferredConcurrentDataSourceTestDataSource : HLDeferredConcurrentDataSource
+{
+ BOOL success;
+}
+
+- (BOOL) succeeded;
+
+@end
+
+@interface HLDeferredConcurrentDataSourceTest : GHAsyncTestCase
+@end
+
+@implementation HLDeferredConcurrentDataSourceTest
+
+- (void) testStart
+{
+ [self prepare];
+
+ HLDeferredConcurrentDataSourceTestDataSource *ds = [[HLDeferredConcurrentDataSourceTestDataSource alloc] init];
+ HLDeferred *d = [ds requestStartOnQueue: [NSOperationQueue mainQueue]];
+
+ __block BOOL success = NO;
+
+ [d then: ^(id result) {
+ success = YES;
+ GHAssertEqualStrings(@"ok", result, @"unexpected result");
+ [self notify: kGHUnitWaitStatusSuccess forSelector: @selector(testStart)];
+ return result;
+ }];
+
+ [self waitForStatus: kGHUnitWaitStatusSuccess timeout: 5.0];
+ GHAssertTrue([ds succeeded], @"concurrent data source wasn't executed");
+ GHAssertTrue(success, @"callback didn't run");
+}
+
+@end
+
+@implementation HLDeferredConcurrentDataSourceTestDataSource
+
+- (id) init
+{
+ self = [super init];
+ return self;
+}
+
+- (void) execute
+{
+ success = YES;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self setResult: @"ok"];
+ [self asyncCompleteOperationResult];
+ });
+}
+
+- (BOOL) succeeded
+{
+ return success;
+}
+
+@end
68 HLDeferred/Tests/HLDeferredDataSourceTest.m
@@ -0,0 +1,68 @@
+//
+// HLDeferredDataSourceTest.m
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDeferredDataSource.h"
+
+@interface HLDeferredDataSourceTestDataSource : HLDeferredDataSource
+{
+ BOOL success;
+}
+
+- (BOOL) succeeded;
+
+@end
+
+@interface HLDeferredDataSourceTest : GHAsyncTestCase
+@end
+
+@implementation HLDeferredDataSourceTest
+
+- (void) testStart
+{
+ [self prepare];
+
+ HLDeferredDataSourceTestDataSource *ds = [[HLDeferredDataSourceTestDataSource alloc] init];
+ HLDeferred *d = [ds requestStartOnQueue: [NSOperationQueue mainQueue]];
+
+ __block BOOL success = NO;
+
+ [d then: ^(id result) {
+ success = YES;
+ GHAssertEqualStrings(@"ok", result, @"unexpected result");
+ [self notify: kGHUnitWaitStatusSuccess forSelector: @selector(testStart)];
+ return result;
+ }];
+
+ [self waitForStatus: kGHUnitWaitStatusSuccess timeout: 5.0];
+ GHAssertTrue([ds succeeded], @"data source wasn't executed");
+ GHAssertTrue(success, @"callback didn't run");
+}
+
+@end
+
+@implementation HLDeferredDataSourceTestDataSource
+
+- (id) init
+{
+ self = [super init];
+ return self;
+}
+
+- (void) execute
+{
+ success = YES;
+ [self setResult: @"ok"];
+ [self asyncCompleteOperationResult];
+}
+
+- (BOOL) succeeded
+{
+ return success;
+}
+
+@end
268 HLDeferred/Tests/HLDeferredListTest.m
@@ -0,0 +1,268 @@
+//
+// HLDeferredListTest.m
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDeferredList.h"
+
+@interface HLDeferredListTest : GHTestCase
+@end
+
+@implementation HLDeferredListTest
+
+- (void) testAllocInitDealloc
+{
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray array]];
+ GHAssertNotNULL(d, nil);
+ [d release];
+}
+
+- (void) testFireOnFirstResultEmptyList
+{
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray array]
+ fireOnFirstResult: YES];
+ GHAssertFalse([d isCalled], @"empty HLDeferredList shouldn't immediately resolve if fireOnFirstResult is YES");
+ [d release];
+}
+
+- (void) testFireOnFirstResultOnCreation
+{
+ HLDeferred *d1 = [HLDeferred deferredWithResult: @"ok"];
+ HLDeferred *d2 = [[HLDeferred alloc] init];
+
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray arrayWithObjects: d1, d2, nil]
+ fireOnFirstResult: YES];
+ GHAssertTrue([d isCalled], @"HLDeferredList with results should immediately resolve if fireOnFirstResult is YES");
+ [d release];
+ // d1 is autoreleased
+ [d2 release];
+}
+
+- (void) testFireOnFirstResultAfterCreation
+{
+ HLDeferred *d1 = [[HLDeferred alloc] init];
+ HLDeferred *d2 = [[HLDeferred alloc] init];
+
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray arrayWithObjects: d1, d2, nil]
+ fireOnFirstResult: YES];
+ GHAssertFalse([d isCalled], @"HLDeferredList shouldn't immediately resolve, no results yet");
+ [d1 takeResult: @"ok"];
+ GHAssertTrue([d isCalled], @"HLDeferredList should resolve with 1 result if fireOnFirstResult is YES");
+
+ __block BOOL success = NO;
+
+ [d1 then: ^(id result) {
+ GHAssertEqualStrings(result, @"ok", @"first result not received");
+ success = YES;
+ return result;
+ }];
+
+ GHAssertTrue(success, @"callback wasn't called on resolved HLDeferredList");
+
+ [d release];
+ [d1 release];
+ [d2 release];
+}
+
+- (void) testEmptyList
+{
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray array]];
+ GHAssertTrue([d isCalled], @"empty HLDeferredList didn't immediately resolve");
+ [d release];
+}
+
+- (void) testOneResult
+{
+ HLDeferred *d1 = [[HLDeferred alloc] init];
+
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray arrayWithObjects: d1, nil]];
+ GHAssertFalse([d isCalled], @"HLDeferredList shouldn't immediately resolve, no results yet");
+ [d1 takeResult: @"ok"];
+ GHAssertTrue([d isCalled], @"HLDeferredList should be resolved");
+
+ __block BOOL success = NO;
+
+ [d then: ^(id result) {
+ GHAssertEqualStrings([result objectAtIndex: 0], @"ok", @"expected result not received");
+ success = YES;
+ return result;
+ }];
+
+ GHAssertTrue(success, @"callback wasn't called on resolved HLDeferredList");
+
+ [d release];
+ [d1 release];
+}
+
+- (void) testOneError
+{
+ HLDeferred *d1 = [[HLDeferred alloc] init];
+
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray arrayWithObjects: d1, nil]];
+ GHAssertFalse([d isCalled], @"HLDeferredList shouldn't immediately resolve, no results yet");
+ [d1 takeError: @"ok"];
+ GHAssertTrue([d isCalled], @"HLDeferredList should be resolved");
+
+ __block BOOL success = NO;
+
+ [d then: ^(id result) {
+ GHAssertEquals((int)[result count], 1, @"expected one result");
+ GHAssertTrue([[result objectAtIndex: 0] isKindOfClass: [HLFailure class]], @"first result should be HLFailure");
+ GHAssertEqualStrings([[result objectAtIndex: 0] value], @"ok", @"expected first result value not received");
+ success = YES;
+ return result;
+ }];
+
+ GHAssertTrue(success, @"callback wasn't called on resolved HLDeferredList");
+
+ [d release];
+ [d1 release];
+}
+
+- (void) testOneResultCallbackBeforeResolution
+{
+ HLDeferred *d1 = [[HLDeferred alloc] init];
+
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray arrayWithObjects: d1, nil]];
+ GHAssertFalse([d isCalled], @"HLDeferredList shouldn't immediately resolve, no results yet");
+
+ __block BOOL success = NO;
+
+ [d then: ^(id result) {
+ GHAssertEqualStrings([result objectAtIndex: 0], @"ok", @"expected result not received");
+ success = YES;
+ return result;
+ }];
+
+ [d1 takeResult: @"ok"];
+ GHAssertTrue([d isCalled], @"HLDeferredList should be resolved");
+
+ GHAssertTrue(success, @"callback wasn't called on resolved HLDeferredList");
+
+ [d release];
+ [d1 release];
+}
+
+- (void) testTwoResults
+{
+ HLDeferred *d1 = [[HLDeferred alloc] init];
+ HLDeferred *d2 = [[HLDeferred alloc] init];
+
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray arrayWithObjects: d1, d2, nil]];
+ GHAssertFalse([d isCalled], @"HLDeferredList shouldn't immediately resolve, no results yet");
+ [d1 takeResult: @"ok1"];
+ GHAssertFalse([d isCalled], @"HLDeferredList shouldn't immediately resolve, results incomplete");
+ [d2 takeResult: @"ok2"];
+ GHAssertTrue([d isCalled], @"HLDeferredList should be resolved");
+
+ __block BOOL success = NO;
+
+ [d then: ^(id result) {
+ GHAssertTrue([result isKindOfClass: [NSArray class]], @"callback result should be NSArray");
+ GHAssertEquals((int)[result count], 2, @"expected two results");
+ GHAssertEqualStrings([result objectAtIndex: 0], @"ok1", @"expected first result not received");
+ GHAssertEqualStrings([result objectAtIndex: 1], @"ok2", @"expected second result not received");
+ success = YES;
+ return result;
+ }];
+
+ GHAssertTrue(success, @"callback wasn't called on resolved HLDeferredList");
+
+ [d release];
+ [d1 release];
+ [d2 release];
+}
+
+- (void) testFireOnFirstErrorEmptyList
+{
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray array]
+ fireOnFirstError: YES];
+ GHAssertTrue([d isCalled], @"empty HLDeferredList should immediately resolve, even when fireOnFirstError is YES");
+ [d release];
+}
+
+- (void) testFireOnFirstErrorOnCreation
+{
+ HLDeferred *d1 = [HLDeferred deferredWithError: @"ok"];
+ HLDeferred *d2 = [[HLDeferred alloc] init];
+
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray arrayWithObjects: d1, d2, nil]
+ fireOnFirstError: YES];
+ GHAssertTrue([d isCalled], @"HLDeferredList with errors should immediately resolve if fireOnFirstError is YES");
+ [d release];
+ // d1 is autoreleased
+ [d2 release];
+}
+
+- (void) testFireOnFirstErrorAfterCreation
+{
+ HLDeferred *d1 = [[HLDeferred alloc] init];
+ HLDeferred *d2 = [[HLDeferred alloc] init];
+
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray arrayWithObjects: d1, d2, nil]
+ fireOnFirstError: YES];
+ GHAssertFalse([d isCalled], @"HLDeferredList shouldn't immediately resolve, no results yet");
+ [d1 takeError: @"ok"];
+ GHAssertTrue([d isCalled], @"HLDeferredList should resolve with 1 error if fireOnFirstError is YES");
+
+ __block BOOL success = NO;
+
+ [d1 fail: ^(HLFailure *failure) {
+ GHAssertEqualStrings([failure value], @"ok", @"error not received");
+ success = YES;
+ return failure;
+ }];
+
+ GHAssertTrue(success, @"errback wasn't called on resolved HLDeferred");
+
+ success = NO;
+
+ [d fail: ^(HLFailure *failure) {
+ GHAssertEqualStrings([failure value], @"ok", @"first error not received");
+ success = YES;
+ return failure;
+ }];
+
+ GHAssertTrue(success, @"errback wasn't called on resolved HLDeferredList");
+
+ [d release];
+ [d1 release];
+ [d2 release];
+}
+
+- (void) testConsumeErrors
+{
+ HLDeferred *d1 = [[HLDeferred alloc] init];
+
+ HLDeferredList *d = [[HLDeferredList alloc] initWithDeferreds: [NSArray arrayWithObjects: d1, nil]
+ consumeErrors: YES];
+
+ [d1 then: ^(id result) {
+ GHAssertNil(result, @"callback result should be nil (consumed by HLDeferredList)");
+ return result;
+ } fail: ^(HLFailure *failure) {
+ GHFail(@"errback was called but the error should have been consumed by the HLDeferredList");
+ return failure;
+ }];
+
+ GHAssertFalse([d isCalled], @"HLDeferredList shouldn't immediately resolve, no results yet");
+ [d1 takeError: @"ok"];
+ GHAssertTrue([d1 isCalled], @"Deferred with error should resolve");
+ GHAssertTrue([d isCalled], @"HLDeferredList should resolve with 1 error if fireOnFirstError is YES");
+
+ [d then: ^(id result) {
+ GHAssertTrue([result isKindOfClass: [NSArray class]], @"callback result should be NSArray");
+ GHAssertEquals((int)[result count], 1, @"expected one result");
+ GHAssertTrue([[result objectAtIndex: 0] isKindOfClass: [HLFailure class]], @"callback result first element should be HLFailure");
+ GHAssertEqualStrings([[result objectAtIndex: 0] value], @"ok", @"expected error not received");
+ return result;
+ }];
+
+ [d release];
+ [d1 release];
+}
+
+@end
233 HLDeferred/Tests/HLDeferredTest.m
@@ -0,0 +1,233 @@
+//
+// HLDeferredTest.m
+// HLDeferred
+//
+// Copyright 2011 HeavyLifters Network Ltd.. All rights reserved.
+// See included LICENSE file (MIT) for licensing information.
+//
+
+#import "HLDeferred.h"
+
+@interface HLDeferredTest : GHTestCase
+@end
+
+@interface HLDeferredTestCanceller : NSObject <HLDeferredCancellable>
+{
+ BOOL success;
+}
+
+- (BOOL) succeeded;
+
+@end
+
+@implementation HLDeferredTest
+
+- (void) testAllocInitDealloc
+{
+ HLDeferred *d = [[HLDeferred alloc] init];
+ GHAssertNotNULL(d, nil);
+ [d release];
+}
+
+- (void) testOneCallback
+{
+ HLDeferred *d = [[HLDeferred alloc] init];
+ __block BOOL success = NO;
+
+ [d then: ^(id result) {
+ success = YES;
+ GHAssertEqualStrings(result, @"success", @"unexpected callback result");
+ return result;
+ }];
+
+ [d takeResult: @"success"];
+ GHAssertTrue(success, @"callback did not run");
+ [d release];
+}
+
+- (void) testMultipleCallbacks
+{
+ HLDeferred *d = [[HLDeferred alloc] init];
+ __block int callbackCount = 0;
+ int count = 0;
+ [d then: ^(id result) {
+ callbackCount++;
+ GHAssertEqualStrings(result, @"starting", @"unexpected callback result");
+ return @"first";
+ }];
+ count++;
+
+ [d then: ^(id result) {
+ callbackCount++;
+ GHAssertEqualStrings(result, @"first", @"unexpected callback result");
+ return @"second";
+ }];
+ count++;
+
+ [d then: ^(id result) {
+ callbackCount++;
+ GHAssertEqualStrings(result, @"second", @"unexpected callback result");
+ return @"blah";
+ }];
+ count++;
+
+ [d takeResult: @"starting"];
+ GHAssertEquals(count, callbackCount, @"count doesn't equal callbackCount");
+ [d release];
+}
+
+- (void) testOneErrback
+{
+ HLDeferred *d = [[HLDeferred alloc] init];
+ __block BOOL success = NO;
+
+ [d fail: ^(HLFailure *failure) {
+ success = YES;
+ GHAssertEqualStrings([failure value], @"success", @"unexpected errback result");
+ return failure;
+ }];
+
+ [d takeError: @"success"];
+ GHAssertTrue(success, @"errback did not run");
+ [d release];
+}
+
+- (void) testMultipleErrbacks
+{
+ HLDeferred *d = [[HLDeferred alloc] init];
+ __block int errbackCount = 0;
+ int count = 0;
+ [d fail: ^(HLFailure *failure) {
+ errbackCount++;