Skip to main content

A utility for simple model transformation and source code generation

Project description

# Fashion

Fashion is a command line utility for simple model transformation and source code generation, aimed at a target audience of developers.

## The problem

It only takes a few lines of code and a template library to throw together an ad hoc code generator. Many languages are suitable for writing transforms and generators. Models need not be complicated, or in strict formats like UML or MOF, be graphical, or even be usable by non-developers.

The problem is managing a growing number ad hoc models, transformations and code generators - and their dependencies.

## The fashion solution

The three-legged stool of fashion:

- A model is just a structured input file.
- A transform is just a simple script.
- A generator just substitutes values from a model into a template.

Fashion introduces a little structure, convention and metadata to make transformations and generators more managable. Python was selected as the language for transformation due to its' popularity and that no compilation step is required. Rather than introduce a new language for transformation, just learn python.

### Models

A model in fashion contains structured data, but no structure is prescribed. Typically a model resembles a key:value map like a python dictionary, but many structures are possible. A fashion model is not limited to UML or MOF-like formats. Fashion models are intended to be simple for developers to create, edit, diff and version control; they aren't intended to be used by non-developers.

### Transforms

A transform:
- reads 0 or more input models,
- produces 0 or more output models, (transformation)
- generates 0 or more output files using 0 or more templates. (generation)

"0 or more" means there is no requirement for a transform to either read input produce output. "1 or more" is more typical.

A single transform could do any combination of both transformation and generation. It is up to you to keep models and transform scripts at a size you decide is managable.

### Templates

A template is a [Mako template](http://www.makotemplates.org/), which could look very similar to a source code file written in some programming language, with placeholders and looping driectives (in Mako syntax).

### Hierarchy of models

Note that templates are easier to read and contain less logic if the model they use is simple, and designed specifically for that template. A design intent of fashion is to use a hierarchy of simple models from very abstract, through more specific, to the final model which is specific to one template.

## Installation

Fashion is a straightforward python 3.5 application.

### prerequisites
- install python 3.5+

### install from github
- TBD

### install from github pip
- TBD

## Getting started

Create a new directory for your target project.

$ mkdir greet
$ cd greet

Set up fashion in this directory.

$ fashion init

This creates the subdirectory `fashion` and an empty fashion project.

A fashion model is just a structured data file, typically in yaml format. Other formats are possible (e.g. json, xml, csv, ini, etc.). Any format is possible - as long as the transforms which use the model can parse it.

Create a simple model of kind 'greeting' in yaml format.

$ fashion create-model hellos.yaml greeting

This creates the file `fashion/model/hellos.yaml`.

Edit the new model file. Initially it looks like:

# default empty yaml file

Add a list of greetings similar to hello, so that `hellos.yaml` looks like:

# List of words meaning hello
- Hello
- Hallo
- Bonjour

Transforms are just python modules which strictly follow certain conventions, such as having functions with specific, pre-determined names. Otherwise, transforms can contain any legal python code, and dependencies (assuming the dependencies are properly installed and located, documentation coming soon).

Now create a transform to change this model into an output source file.

$ fashion create-xform greetWorld

The empty transform should look like this:

'''
greetWorld xform.
'''

import logging

from fashion.xformUtil import readModels
from fashion.xformUtil import generate

def inputKinds():
'''Inputs consumed by this xform'''
return [ ]

def outputKinds():
'''Outputs generated by this xform'''
return [ 'fashion_gen' ]

def police():
'''Validate input combinations.'''
pass

def xform():
'''Generate many source files from 2 input models.'''
logging.debug("greetWorld xform running")

model = { }

generate(model, "myTemplate", "myTarget")

Edit the transform to look like this:

'''
greetWorld xform.
'''

import logging

from fashion.xformUtil import readModels
from fashion.xformUtil import generate

def inputKinds():
'''Input kinds consumed by this xform'''
return [ 'greeting' ]

def outputKinds():
'''Output kinds generated by this xform'''
return [ 'fashion_gen' ]

def police(greeting):
'''Validate input combinations.'''
pass

# xform argument names must match input kinds exactly, but order not significant
def xform(greeting):
'''Generate source files input models.'''
logging.debug("greetWorld xform running")

# read all the greeting input models
# since each input model is a list, flatten=True
# changes list of lists [[],[], ...] into a flat list []
greetings = readModels(greeting, flatten=True)
# greetings should be ['Hello', 'Hallo', 'Bonjour']

# create the model handed to the template
model = { 'greetings': greetings }

# generate the file from the template and the model
generate(model, "greetWorld_template.c", "greetWorld.c")

Templates are implemented by [Mako](http://www.makotemplates.org/). For details of template syntax, refer to the Mako documentation.

Create the template file fashion/template/greetWorld_template.c

/* greetWorld.c */

#include <stdio.h>

main() {
% for greet in greetings:
printf("${greet} world!\n");
% endfor
}

Now run the fashion `build` command, which executes all the transforms.

$ fashion build

This should produce the file greetWorld.c which looks like;

/* greetWorld.c */

#include <stdio.h>

main() {
printf("Hello world!\n");
printf("Hallo world!\n");
printf("Bonjour world!\n");
}

### nab command

Next, let's try to reverse engineer an existing file into a transform and template using the fashion 'nab' command.

If you were introducing fashion into an existing project, you'd expect there would already be existing source files. We'll create a file, and pretend this file already existed before we did `fashion init`.

Create `HelloWorld.java` with the content below:

public class HelloWorld {

public static void main(String[] args) {
System.out.println("Hello, world!");
}
}

This creates a template `fashion/template/HelloWorld.java` and a transform `HelloWorld.py`. The new template is identical to the generation target `HelloWorld.java`, just waiting for template placeholders to be added. Now we can modify the transform and template to finish our generation work.

Modify the template to look like:

public class HelloWorld {

public static void main(String[] args) {
% for greet in greetings:
System.out.println("${greet}, world!");
% endfor
}
}

Modify the transform to look like:

'''
HelloWorld xform.
'''

import logging

from fashion.xformUtil import readModels
from fashion.xformUtil import generate

def inputKinds():
'''Input kinds consumed by this xform'''
return [ 'greeting' ]

def outputKinds():
'''Output kinds generated by this xform'''
return [ 'fashion_gen' ]

def police(greeting):
'''Validate input combinations.'''
pass

def xform(greeting):
'''Generate source files input models.'''
logging.debug("HelloWorld xform running")

# read all the greeting input models
# since each input model is a list, flatten=True
# changes list of lists [[],[], ...] into a flat list []
greetings = readModels(greeting, flatten=True)
# greetings should be ['Hello', 'Hallo', 'Bonjour']

# create the model handed to the template
model = { 'greetings': greetings }

# generate the file from the template and the model
generate(model, "HelloWorld.java", "HelloWorld.java")

The output file `HelloWorld.java` should look like:

public class HelloWorld {

public static void main(String[] args) {
System.out.println("Hello, world!");
System.out.println("Hallo, world!");
System.out.println("Bonjour, world!");
}
}

So now we have source files in two languages generated from a common model.

## Directory structure

Fashion creates a directory for itself under your project directory. Fashion is design to work within your existing project directory (as long as you don't already have a subdirectory named 'fashion'). The transformation and generation assets and metadata are stored under this directory.

fashion/
+ bak/ # backup files (not yet implemented)
+ mirror/ # copies of generated files to detect external modification
+ model/ # model files go here
+ template/ # template files go here
+ tmplMods/ # mako compiles templates into python modules here
+ xform/ # xforms (python modules) go here
+ fashion.yaml # model for this fashion project
+ library.yaml # library model for this fashion project
+ fashion.db # sqlite3 database for fashions' file metadata

### version control

It is recommended to store your transformation and generation assets in your source code version control system, but not everything under `fashion/` needs to be controlled.

Control these:

fashion/
+ model/ # model files go here
+ template/ # template files go here
+ xform/ # xforms (python modules) go here
+ fashion.yaml # model for this fashion project
+ library.yaml # library model for this fashion project

Don't control these:

fashion/
+ bak/ # backup files (not yet implemented)
+ mirror/ # copies of generated files to detect external modification
+ tmplMods/ # mako compiles templates into python modules here
+ fashion.db # sqlite3 database for fashions' file metadata

## Dependencies
The following python libraries are used:
- [Mako templates](http://www.makotemplates.org/) (MIT license)
- [PyYAML](http://pyyaml.org/) (MIT license) Soon to be replaced by [ruamel](https://bitbucket.org/ruamel/yaml)
- [peewee](http://docs.peewee-orm.com/en/latest/) (MIT license)

## The name

Think of "fashion" as a verb, e.g. "I fashioned this code from this simple model."

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fashionModel-0.1.1.dev1.tar.gz (22.0 kB view hashes)

Uploaded Source

Built Distribution

fashionModel-0.1.1.dev1-py3-none-any.whl (20.6 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page