Browse files

Merge branch 'master' into develop

  • Loading branch information...
rentzsch committed Nov 10, 2012
2 parents 0467af3 + 6746f96 commit f6eed37151e7a9ec0dc8062976304bc65cc054fd
Showing with 67 additions and 3 deletions.
  1. +1 −0 mogenerator.h
  2. +13 −2 mogenerator.m
  3. +1 −1 templates/machine.h.motemplate
  4. +52 −0 test/Test README.markdown
@@ -59,6 +59,7 @@
NSManagedObjectModel *model;
NSString *configuration;
NSString *baseClass;
+ NSString *baseClassImport;
NSString *baseClassForce;
NSString *includem;
NSString *includeh;
@@ -10,6 +10,7 @@
static NSString * const kTemplateVar = @"TemplateVar";
NSString *gCustomBaseClass;
+NSString *gCustomBaseClassImport;
NSString *gCustomBaseClassForced;
@interface NSEntityDescription (fetchedPropertiesAdditions)
@@ -55,7 +56,7 @@ - (NSArray*)entitiesWithACustomSubclassInConfiguration:(NSString *)configuration
nsenumerate (allEntities, NSEntityDescription, entity) {
NSString *entityClassName = [entity managedObjectClassName];
- if ([entityClassName isEqualToString:@"NSManagedObject"] || [entityClassName isEqualToString:gCustomBaseClass]){
+ if ([entityClassName isEqualToString:@"NSManagedObject"] || [entityClassName isEqualToString:@""] || [entityClassName isEqualToString:gCustomBaseClass]){
if (verbose_) {
ddprintf(@"skipping entity %@ (%@) because it doesn't use a custom subclass.\n",, entityClassName);
@@ -72,6 +73,12 @@ - (NSArray*)entitiesWithACustomSubclassInConfiguration:(NSString *)configuration
@implementation NSEntityDescription (customBaseClass)
+- (BOOL)hasCustomBaseCaseImport {
+ return gCustomBaseClassImport == nil ? NO : YES;
+- (NSString *)baseClassImport {
+ return gCustomBaseClassImport;
- (BOOL)hasCustomSuperentity {
NSString *forcedBaseClass = [self forcedCustomBaseClass];
if (!forcedBaseClass) {
@@ -499,7 +506,8 @@ - (void) application: (DDCliApplication *) app
// Long Short Argument options
{@"model", 'm', DDGetoptRequiredArgument},
{@"configuration", 'C', DDGetoptRequiredArgument},
- {@"base-class", 0, DDGetoptRequiredArgument},
+ {@"base-class", 0, DDGetoptRequiredArgument},
+ {@"base-class-import", 0, DDGetoptRequiredArgument},
{@"base-class-force", 0, DDGetoptRequiredArgument},
// For compatibility:
{@"baseClass", 0, DDGetoptRequiredArgument},
@@ -530,6 +538,7 @@ - (void) printUsage;
" -m, --model MODEL Path to model\n"
" -C, --configuration CONFIG Only consider entities included in the named configuration\n"
" --base-class CLASS Custom base class\n"
+ " --base-class-import TEXT Imports base class as #import TEXT\n"
" --base-class-force CLASS Same as --base-class except will force all entities to have the specified base class. Even if a super entity exists\n"
" --includem FILE Generate aggregate include file for .m files for both human and machine generated source files\n"
" --includeh FILE Generate aggregate include file for .h files for human generated source files only\n"
@@ -695,8 +704,10 @@ - (int) application: (DDCliApplication *) app
if(baseClassForce) {
gCustomBaseClassForced = [baseClassForce retain];
gCustomBaseClass = gCustomBaseClassForced;
+ gCustomBaseClassImport = [baseClassImport retain];
} else {
gCustomBaseClass = [baseClass retain];
+ gCustomBaseClassImport = [baseClassImport retain];
NSString * mfilePath = includem;
@@ -2,7 +2,7 @@
// Make changes to <$managedObjectClassName$>.h instead.
#import <CoreData/CoreData.h>
-<$if hasCustomSuperentity$>#import "<$customSuperentity$>.h"<$endif$>
+<$if hasCustomSuperentity$>#import <$if hasCustomBaseCaseImport$><$baseClassImport$><$else$>"<$customSuperentity$>.h"<$endif$><$endif$>
extern const struct <$managedObjectClassName$>Attributes {<$foreach Attribute noninheritedAttributes do$>
<$if TemplateVar.arc$>__unsafe_unretained<$endif$> NSString *<$$>;<$endforeach do$>
@@ -0,0 +1,52 @@
+Welcome to mogenerator's Test Dir
+This directory exists to test mogenerator's operation and catch regressions. It's especially handy for ensuring correct operation of obscure features.
+How to Use
+If you're contributing to mogenerator, please ensure tests pass prior to issuing a Pull Request. It's easy:
+ $ cd mogenerator/test
+ $ rake
+ success
+ success
+`test/Rakefile` does the hard work of building mogenerator from source (via `xcodebuild`) and exercising it against the test.xcmodeld data model. It ensures:
+1. mogenerator can be built from source.
+2. You're using the latest source tree templates (so template errors fail immediately).
+3. mogenerator can read the test.xcmodeld file.
+4. mogenerator can generate source files from the model.
+5. Generated source files are compilable.
+6. A small but real program can use the generated classes to create managed objects, modify their attributes, hook up relationships and save them to an in-memory store.
+It does this for both MRC and ARC modes (hence the double `success` in the output).
+Points of Highlight
+* Unlike the old Test Mule, the Rakefile should cleanup after itself unless something goes horribly wrong (no more checking-in differently-generated source files). It probably could be made to clean after itself even when things go wrong, but my Rakefile-fu is too weak to figure out how and it's handy to have files laying about when things do go wrong for debugging.
+* ParentMO has an attribute for each attribute type Core Data supports, exercising Core Data type => Objective-C type translation (and NSNumber-boxing code generation).
+* ParentMO and ChildMO are both subclasses of HumanMO, exercising inheritance.
+* A number of model-defined fetch requests, exercising codegen for a number of scenarios:
+ * HumanMO.allHumans, whose empty predicates results in code that should fetch all HumanMOs.
+ * HumanMO.byHumanName, whose predicate includes a substitution variable `$humanName` which gets translated into a parameter in the fetch request wrapper's method name.
+ * HumanMO.oneByHumanName, which is like HumanMO.byHumanName but whose wrapper ensures only zero or one objects result from a fetch. If more are returned, and detailed error is logged and nil is returned.
+ * ChildMO.byParent, whose predicate includes a substitution variable `$parent` representing a relationship (with its own destination class) instead of an attribute.
+* The Rakefile and test.m exercise mogenerator's `--baseClass` option, passing MyBaseClass. MyBaseClass defines an ivar (uncreatively named `ivar`) and accessors, which should be accessible to all model-defined classes.
+* The Rakefile overwrites the default (empty) generated MOs/ files with the one in the base test directory to simulate persistence of Human code (since the MOs directory is recursively deleted after each task).
+* Say you want to store an object not directly supported by Core Data (NSColor is a popular example). HumanMO.hairColor/hairColorStorage illustrates the old way to do it requiring Human code support. ParentMO.myTransformableWithClassName illustrates the 10.5+ way to do it with Core Data's NSValueTransformer support.
+ mogenerator supports both ways, but the newer NSValueTransformer way is better since it's directly supported and centralizes data <=> marshalling to one class. mogenerator sweetens the pot by allowing you to define the name of the class after it's been transformed (via the `attributeValueClassName` attribute user info key-value).

0 comments on commit f6eed37

Please sign in to comment.