Skip to content

Mogenerator internals

Tom Harrington edited this page Apr 27, 2019 · 3 revisions

A quick introduction and reference for anyone who wants to hack on mogenerator itself.

Overview

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 of mogenerator. Everything about combining a data model with templates to produce model classes happens here. It includes
    • MOGeneratorApp, the core of mogenerator'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 by mogenerator to compile a data model into an NSManagedObjectModel. 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.

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 in mogenerator.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 tells ddcli 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 the outputDir attribute of MOGeneratorApp (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. .

Template Variables

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 the managedObjectClassName method on the NSEntityDescription instance for the entity.
  • When iterating over an entity's attributes, the value of the usesRawValueEnumType template variable comes from calling usesRawValueEnumType on the NSAttributeDescription 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.

Command line template variables

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.

Loop variables

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 the noninheritedAttributes method on the current entity to get an array of attributes. This method is implemented in a category on NSEntityDescription found in mogenerator.m.
  • Attribute is a loop variable which represents each attribute in turn. Since noninheritedAttributes returns an array of NSAttributeDescription, its template variables correspond to properties or methods on that class.
  • The hasDefinedAttributeType and hasScalarAttributeType template variables are looked up using methods of the same name on NSAttributeDescription. Both of these are defined in a category on NSAttributeDescription.

User Info Variables

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.