-
Notifications
You must be signed in to change notification settings - Fork 395
Mogenerator internals
A quick introduction and reference for anyone who wants to hack on mogenerator
itself.
mogenerator
has been around for a long time. Go check the commit history if you like. It's been around since the iPhone was just a glimmer in Steve Jobs' eye. Parts of it are older than Mac OS X. As such it's written entirely in Objective-C, and will continue to be so until some highly motivated person finds a reason to rewrite it.
Code is grouped as follows:
-
mogenerator.[hm]
: The Core ofmogenerator
. Everything about combining a data model with templates to produce model classes happens here. It includes-
MOGeneratorApp
, the core ofmogenerator
's operations - Numerous categories on Core Data classes (see below under "template variables" for a discussion of these).
-
-
momcom
: A collection of classes implementing a Core Data model compiler. Used bymogenerator
to compile a data model into anNSManagedObjectModel
. Derived from this repo. -
MiscMerge
: Template engine used to process template source code files. This comes from Don Yactman and Carl Lindberg's old MiscKit project, and can now also be found in this repo. -
ponso
: No longer used. -
ddcli
: Dave Dribin's ddcli, which is used to parse command line arguments.
Arguments are processed by ddcli
. This happens in main
, which is a single line that calls DDCliAppRunWithClass
, passing an instance of MOGeneratorApp
as the sole argument.
Any new or updated command line arguments should update all of the following:
- Argument names (long and short) plus argument type are listed in
-[MOGeneratorApp application:willParseOptions:]
. "Type" here means the command-line argument type, not the data type. Things like whether the argument requires a value or can stand on its own, for example. - Each argument corresponds to an instance variable declared on
MOGeneratorApp
, which in turn is declared inmogenerator.h
. This is where the data type for the argument is declared. - Usage descriptions are found in strings in
-[MOGeneratorApp printUsage]
.
Once arguments have been processed, the main mogenerator
work is kicked off by a call to -[MOGeneratorApp application:runWithArguments:]
. Instance variables on MOGeneratorApp
will have values dereived from command line arguments.
Here's a simple example. The --output-dir
argument tells mogenerator
where to write generated source files. The process is as follows.
-
The entry point calls
DDCliAppRunWithClass([MOGeneratorApp class])
, which tellsddcli
to start processing command line arguments. -
Command line arguments are listed in
-application:willParseOptions:
. This method lists valid arguments. The entry for--output-dir
looks like this:{@"output-dir", 'O', DDGetoptRequiredArgument},
This says that the
output-dir
agument has a shorthand version,-O
, and that it requires a value-- which here is the path use when writing files. -
ddcli
assigns the value of the-output-dir
argument to theoutputDir
attribute ofMOGeneratorApp
(ddcli
does some argument-name mangling to ensure that the argument name translates to a valid property name), and the directory path can be found by accessing that property. .
Most template variables correspond to properties or methods on the relevant Core Data class. For example,
- The value of
managedObjectClassName
template variable on an entity is found by calling themanagedObjectClassName
method on theNSEntityDescription
instance for the entity. - When iterating over an entity's attributes, the value of the
usesRawValueEnumType
template variable comes from callingusesRawValueEnumType
on theNSAttributeDescription
instance for the attribute.
Lookups use both built-in Core Data methods and any methods implemented in categories on those classes. Categories with such methods are found in mogenerator.m
.
The project defines many extra methods in categories of Core Data classes in order to provide template variables. A side effect of this is that many methods declared in code are never directly called, but still must be present because they are used in templates. Don't remove a category method without checking template files to see if it's used there.
Template variables can also be declared via command line aguments using the syntax --template-var KEY=VALUE
. These key-value pairs are copied to the templateVar
property of MOGeneratorApp
. That dictionary is passed on to the template engine (MiscMerge), and the variables are available when processing templates.
Templates can also use loop variables when iterating over collections. For example, consider the following template code
<$foreach Attribute noninheritedAttributes do$>
<$if Attribute.hasDefinedAttributeType$>
<$if Attribute.hasScalarAttributeType$>
...
<$endforeach do$>
- The
foreach
loop calls thenoninheritedAttributes
method on the current entity to get an array of attributes. This method is implemented in a category onNSEntityDescription
found inmogenerator.m
. -
Attribute
is a loop variable which represents each attribute in turn. SincenoninheritedAttributes
returns an array ofNSAttributeDescription
, its template variables correspond to properties or methods on that class. - The
hasDefinedAttributeType
andhasScalarAttributeType
template variables are looked up using methods of the same name onNSAttributeDescription
. Both of these are defined in a category onNSAttributeDescription
.
Custom userInfo
keys configured in data models are available in code via the userInfo
property on NSEntityDescription
, NSAttributeDescription
, and NSRelationshipDescription
. These may be mapped to template variables but the names don't need to match. User info variables do not automatically become template variables.
For example, attribute descriptions may use a key named attributeValueScalarType
. There's a category method on NSAttributeDescription
that looks up the value of this user info key. The method is called usesCustomScalarAttributeType
, so that's also the name of the corresponding template variable.