Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added support for Objective-C validation functions.

  • Loading branch information...
commit faf88085f9d68f2bcddf07e5936fa7f75cf7bae6 1 parent d66f1a4
@snej snej authored
View
20 Couch/CouchDesignDocument_Embedded.h
@@ -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.
@@ -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
View
60 Couch/CouchDesignDocument_Embedded.m
@@ -7,7 +7,6 @@
//
#import "CouchDesignDocument_Embedded.h"
-#import "CouchbaseViewRegistry.h"
NSString* const kCouchLanguageObjectiveC = @"objc";
@@ -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
View
2  Couch/CouchPrefix.pch
@@ -5,3 +5,5 @@
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#endif
+
+#define COUCHCOCOA_IMPL
View
84 Couch/CouchbaseCallbacks.h
@@ -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
View
34 Couch/CouchbaseViewRegistry.h
@@ -1,34 +0,0 @@
-//
-// CouchViewRegistry.h
-// iErl14
-//
-// Created by Jens Alfke on 10/3/11.
-// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
-//
-
-#import <Foundation/Foundation.h>
-
-typedef void (^CouchEmitBlock)(id key, id value);
-typedef void (^CouchMapBlock)(NSDictionary* doc, CouchEmitBlock emit);
-typedef id (^CouchReduceBlock)(NSArray* keys, NSArray* values, BOOL rereduce);
-
-/** Central per-process registry for native map/reduce 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 CouchbaseViewRegistry : NSObject
-{
- NSMutableDictionary* _mapBlocks, *_reduceBlocks;
-}
-
-+ (CouchbaseViewRegistry*) sharedInstance;
-
-- (NSString*) generateKey;
-
-- (void) registerMapBlock: (CouchMapBlock)block forKey: (NSString*)key;
-- (void) registerReduceBlock: (CouchReduceBlock)block forKey: (NSString*)key;
-
-- (CouchMapBlock) mapBlockForKey: (NSString*)key;
-- (CouchReduceBlock) reduceBlockForKey: (NSString*)key;
-
-@end
View
8 CouchCocoa.xcodeproj/project.pbxproj
@@ -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, ); }; };
@@ -241,7 +243,6 @@
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; };
@@ -249,6 +250,7 @@
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>"; };
@@ -440,7 +442,7 @@
27333BCA13B7E61700EF5A10 /* CouchInternal.h */,
2781244413AFA6CD0051A99D /* CouchChangeTracker.h */,
2781244513AFA6CD0051A99D /* CouchChangeTracker.m */,
- 27CB6550143A767900EEA1F2 /* CouchbaseViewRegistry.h */,
+ 27D083B7143FBEEA0067702F /* CouchbaseCallbacks.h */,
27CDEC3913C6806400C979BB /* CouchPrefix.pch */,
);
name = Internal;
@@ -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;
};
@@ -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;
};
Please sign in to comment.
Something went wrong with that request. Please try again.