Skip to content
This repository has been archived by the owner on Mar 9, 2022. It is now read-only.

Commit

Permalink
Added support for Objective-C validation functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
snej committed Oct 8, 2011
1 parent d66f1a4 commit faf8808
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 60 deletions.
20 changes: 14 additions & 6 deletions Couch/CouchDesignDocument_Embedded.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@
//

#import "CouchDesignDocument.h"
#ifdef COUCHCOCOA_IMPL
#import "CouchbaseCallbacks.h"
#else
#import <Couchbase/CouchbaseCallbacks.h>
#endif


extern NSString* const kCouchLanguageObjectiveC;


typedef void (^CouchDesignDocumentMapBlock)(NSDictionary* doc, void (^emit)(id key, id value));
typedef id (^CouchDesignDocumentReduceBlock)(NSArray* keys, NSArray* values, BOOL rereduce);

#define MAPBLOCK(BLOCK) ^(NSDictionary* doc, void (^emit)(id key, id value)){BLOCK}
#define REDUCEBLOCK(BLOCK) ^id(NSArray* keys, NSArray* values, BOOL rereduce){BLOCK}
#define VALIDATIONBLOCK(BLOCK) ^BOOL(NSDictionary* doc, id<CouchbaseValidationContext> context)\
{BLOCK}


/** Optional support for native Objective-C map/reduce functions.
Expand All @@ -28,12 +33,15 @@ typedef id (^CouchDesignDocumentReduceBlock)(NSArray* keys, NSArray* values, BOO
It is very important that this block be a law-abiding map function! As in other languages, it must be a "pure" function, with no side effects, that always emits the same values given the same input document. That means that it should not access or change any external state; be careful, since blocks make that so easy that you might do it inadvertently!
The block may be called on any thread, or on multiple threads simultaneously. This won't be a problem if the code is "pure" as described above, since it will as a consequence also be thread-safe. */
- (void) defineViewNamed: (NSString*)viewName
mapBlock: (CouchDesignDocumentMapBlock)mapBlock;
mapBlock: (CouchMapBlock)mapBlock;

/** Defines or deletes a native view with both a map and a reduce function.
For details, read the documentation of the -defineViewNamed:mapBlock: method.*/
- (void) defineViewNamed: (NSString*)viewName
mapBlock: (CouchDesignDocumentMapBlock)mapBlock
reduceBlock: (CouchDesignDocumentReduceBlock)reduceBlock;
mapBlock: (CouchMapBlock)mapBlock
reduceBlock: (CouchReduceBlock)reduceBlock;

/** An Objective-C block that can validate any document being added/updated to this database. */
@property (copy) CouchValidateUpdateBlock validationBlock;

@end
60 changes: 42 additions & 18 deletions Couch/CouchDesignDocument_Embedded.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
//

#import "CouchDesignDocument_Embedded.h"
#import "CouchbaseViewRegistry.h"


NSString* const kCouchLanguageObjectiveC = @"objc";
Expand All @@ -16,48 +15,73 @@
@implementation CouchDesignDocument (Embedded)


+ (CouchbaseViewRegistry*) objCViewRegistry {
static CouchbaseViewRegistry* sRegistry;
if (!sRegistry) {
Class regClass = NSClassFromString(@"CouchbaseViewRegistry");
sRegistry = [regClass performSelector: @selector(sharedInstance)];
+ (CouchbaseCallbacks*) objCCallbacks {
// Look up the callbacks without creating a link-time dependency on the class:
static CouchbaseCallbacks* sCallbacks;
if (!sCallbacks) {
Class regClass = NSClassFromString(@"CouchbaseCallbacks");
sCallbacks = [regClass performSelector: @selector(sharedInstance)];
if (!sCallbacks)
[NSException raise: NSGenericException format: @"No Objective-C views available"];
}
return sRegistry;
return sCallbacks;
}


- (void) defineViewNamed: (NSString*)viewName
mapBlock: (CouchDesignDocumentMapBlock)mapBlock
mapBlock: (CouchMapBlock)mapBlock
{
[self defineViewNamed: viewName mapBlock: mapBlock reduceBlock: NULL];
}


- (void) defineViewNamed: (NSString*)viewName
mapBlock: (CouchDesignDocumentMapBlock)mapBlock
reduceBlock: (CouchDesignDocumentReduceBlock)reduceBlock
mapBlock: (CouchMapBlock)mapBlock
reduceBlock: (CouchReduceBlock)reduceBlock
{
NSString* mapKey = nil, *reduceKey = nil;
if (mapBlock) {
CouchbaseViewRegistry* registry = [[self class] objCViewRegistry];
if (!registry)
[NSException raise: NSGenericException format: @"No Objective-C views available"];

CouchbaseCallbacks* callbacks = [[self class] objCCallbacks];
mapKey = [self mapFunctionOfViewNamed: viewName];
if (!mapKey)
mapKey = [registry generateKey];
[registry registerMapBlock: mapBlock forKey: mapKey];
mapKey = [callbacks generateKey];
[callbacks registerMapBlock: mapBlock forKey: mapKey];
reduceKey = [self reduceFunctionOfViewNamed: viewName];
if (reduceBlock) {
if (!reduceKey)
reduceKey = [registry generateKey];
reduceKey = [callbacks generateKey];
}
if (reduceKey)
[registry registerReduceBlock: reduceBlock forKey: reduceKey];
[callbacks registerReduceBlock: reduceBlock forKey: reduceKey];
self.language = kCouchLanguageObjectiveC;
}
[self defineViewNamed: viewName map: mapKey reduce: reduceKey];
}


- (CouchValidateUpdateBlock) validationBlock {
if (![self.language isEqualToString: kCouchLanguageObjectiveC])
return nil;
NSString* validateKey = self.validation;
if (!validateKey)
return nil;
return [[[self class] objCCallbacks] validateUpdateBlockForKey: validateKey];
}

- (void) setValidationBlock: (CouchValidateUpdateBlock)validateBlock {
CouchbaseCallbacks* callbacks = [[self class] objCCallbacks];
NSString* validateKey = self.validation;
if (validateBlock) {
if (!validateKey)
validateKey = [callbacks generateKey];
[callbacks registerValidateUpdateBlock: validateBlock forKey: validateKey];
self.language = kCouchLanguageObjectiveC;
} else if (validateKey) {
[callbacks registerValidateUpdateBlock: nil forKey: validateKey];
validateKey = nil;
}
self.validation = validateKey;
}


@end
2 changes: 2 additions & 0 deletions Couch/CouchPrefix.pch
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#endif

#define COUCHCOCOA_IMPL
84 changes: 84 additions & 0 deletions Couch/CouchbaseCallbacks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// CouchbaseCallbacks.h
// iErl14
//
// Created by Jens Alfke on 10/3/11.
// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
//

#import <Foundation/Foundation.h>
@protocol CouchbaseValidationContext;


typedef void (^CouchEmitBlock)(id key, id value);

/** A "map" function called when a document is to be added to a view.
@param doc The contents of the document being analyzed.
@param emit A block to be called to add a key/value pair to the view. Your block can call zero, one or multiple times. */
typedef void (^CouchMapBlock)(NSDictionary* doc, CouchEmitBlock emit);

/** A "reduce" function called to summarize the results of a view.
@param keys An array of keys to be reduced.
@param values A parallel array of values to be reduced, corresponding one-to-one with the keys.
@param rereduce YES if the input keys and values are the results of previous reductions.
@return The reduced value; almost always a scalar or small fixed-size object. */
typedef id (^CouchReduceBlock)(NSArray* keys, NSArray* values, BOOL rereduce);

/** Called to validate a document before it's added to the database.
@param doc The submitted document contents.
@param context Lets the block access relevant information and specify an error message.
@return YES to accept the document, NO to reject it. */
typedef BOOL (^CouchValidateUpdateBlock)(NSDictionary* doc,
id<CouchbaseValidationContext> context);

/** Central per-process registry for native design-document functions.
Associates a key (a unique ID stored in the design doc as the "source" of the function)
with a C block.
This class is thread-safe. */
@interface CouchbaseCallbacks : NSObject
{
NSMutableArray* _registries;
}

+ (CouchbaseCallbacks*) sharedInstance;

- (NSString*) generateKey;

- (void) registerMapBlock: (CouchMapBlock)block forKey: (NSString*)key;
- (void) registerReduceBlock: (CouchReduceBlock)block forKey: (NSString*)key;
- (void) registerValidateUpdateBlock: (CouchValidateUpdateBlock)block forKey: (NSString*)key;

- (CouchMapBlock) mapBlockForKey: (NSString*)key;
- (CouchReduceBlock) reduceBlockForKey: (NSString*)key;
- (CouchValidateUpdateBlock) validateUpdateBlockForKey: (NSString*)key;

@end


/** Context passed into a CouchValidateUpdateBlock. */
@protocol CouchbaseValidationContext <NSObject>

/** The contents of the current revision of the document, or nil if this is a new document. */
@property (readonly) NSDictionary* currentRevision;

/** The name of the database being updated. */
@property (readonly) NSString* databaseName;

/** The name of the logged-in user, or nil if this is an anonymous request. */
@property (readonly) NSString* userName;

/** Does the user have admin privileges?
(If the database is in the default "admin party" mode, this will be YES even when the userName is nil.) */
@property (readonly) BOOL isAdmin;

/** The database's security object, which assigns roles and privileges. */
@property (readonly) NSDictionary* security;

/** The type of error to report, if the validate block returns NO.
The default value is "forbidden", which will result in an HTTP 403 status. */
@property (copy) NSString* errorType;

/** The error message to return in the HTTP response, if the validate block returns NO.
The default value is "invalid document". */
@property (copy) NSString* errorMessage;
@end
34 changes: 0 additions & 34 deletions Couch/CouchbaseViewRegistry.h

This file was deleted.

8 changes: 6 additions & 2 deletions CouchCocoa.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@
27CDEC3713C67E1600C979BB /* Test_REST.m in Sources */ = {isa = PBXBuildFile; fileRef = 272E9D9313A2EBE0009F18E9 /* Test_REST.m */; };
27CDEC3813C67E1A00C979BB /* Test_Couch.m in Sources */ = {isa = PBXBuildFile; fileRef = 270A663A13A5B36900791F4A /* Test_Couch.m */; };
27CDEC3A13C6841E00C979BB /* CouchCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27CDEBF113C67C9B00C979BB /* CouchCocoa.framework */; };
27D083B8143FBEEA0067702F /* CouchbaseCallbacks.h in Headers */ = {isa = PBXBuildFile; fileRef = 27D083B7143FBEEA0067702F /* CouchbaseCallbacks.h */; };
27D083B9143FBEEA0067702F /* CouchbaseCallbacks.h in Headers */ = {isa = PBXBuildFile; fileRef = 27D083B7143FBEEA0067702F /* CouchbaseCallbacks.h */; };
27DB821E1408202000E57444 /* CouchDynamicObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 27DB821D1408202000E57444 /* CouchDynamicObject.m */; };
27DB821F1408202000E57444 /* CouchDynamicObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 27DB821D1408202000E57444 /* CouchDynamicObject.m */; };
27DB82211408202E00E57444 /* CouchDynamicObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 27DB82201408202E00E57444 /* CouchDynamicObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -241,14 +243,14 @@
27C727FB13EB237200C7ADF5 /* CouchUITableSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CouchUITableSource.m; sourceTree = "<group>"; };
27CB654A143A746700EEA1F2 /* CouchDesignDocument_Embedded.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CouchDesignDocument_Embedded.h; sourceTree = "<group>"; };
27CB654B143A746700EEA1F2 /* CouchDesignDocument_Embedded.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CouchDesignDocument_Embedded.m; sourceTree = "<group>"; };
27CB6550143A767900EEA1F2 /* CouchbaseViewRegistry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CouchbaseViewRegistry.h; sourceTree = "<group>"; };
27CDEBF113C67C9B00C979BB /* CouchCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CouchCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; };
27CDEBF413C67C9B00C979BB /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
27CDEBF513C67C9B00C979BB /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
27CDEBF613C67C9B00C979BB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
27CDEBF913C67C9B00C979BB /* CouchCocoa-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CouchCocoa-Info.plist"; sourceTree = "<group>"; };
27CDEC0313C67C9B00C979BB /* Mac Tests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Mac Tests.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
27CDEC3913C6806400C979BB /* CouchPrefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CouchPrefix.pch; sourceTree = "<group>"; };
27D083B7143FBEEA0067702F /* CouchbaseCallbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CouchbaseCallbacks.h; sourceTree = "<group>"; };
27D5997813CE404300694B37 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.md; sourceTree = "<group>"; };
27DA430413B659A900BBADB7 /* RESTInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RESTInternal.h; sourceTree = "<group>"; };
27DA430513B659A900BBADB7 /* RESTInternal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RESTInternal.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -440,7 +442,7 @@
27333BCA13B7E61700EF5A10 /* CouchInternal.h */,
2781244413AFA6CD0051A99D /* CouchChangeTracker.h */,
2781244513AFA6CD0051A99D /* CouchChangeTracker.m */,
27CB6550143A767900EEA1F2 /* CouchbaseViewRegistry.h */,
27D083B7143FBEEA0067702F /* CouchbaseCallbacks.h */,
27CDEC3913C6806400C979BB /* CouchPrefix.pch */,
);
name = Internal;
Expand Down Expand Up @@ -570,6 +572,7 @@
27E4DD0D141921E000A3D8F6 /* CouchPersistentReplication.h in Headers */,
279276EF14215D5600002958 /* RESTBase64.h in Headers */,
27CB654D143A746700EEA1F2 /* CouchDesignDocument_Embedded.h in Headers */,
27D083B9143FBEEA0067702F /* CouchbaseCallbacks.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -597,6 +600,7 @@
27CDEC3613C67D3500C979BB /* CouchCocoa.h in Headers */,
279276EE14215D5600002958 /* RESTBase64.h in Headers */,
27CB654C143A746700EEA1F2 /* CouchDesignDocument_Embedded.h in Headers */,
27D083B8143FBEEA0067702F /* CouchbaseCallbacks.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down

0 comments on commit faf8808

Please sign in to comment.