Giter Site home page Giter Site logo

chassis's Introduction

Project Chassis (a Kotlin Code Generator)

Kotlin GitHub Repo stars

from a custom Kotlin DSL using https://square.github.io/kotlinpoet, you're able to generate (Kotlin) source code.

Over and over and over again...

TL;DR

current example DSL (4 DTOs, 1 DCO with Fillers and DB CRUDs plus 4 abstract DTOs Base-Classes):

|           |  lines |  #files |
|:---------:|-------:|--------:|
|    DSL    |    319 |       3 |
| generated |   2190 |      43 |
|     %     | ~700 % | ~1400 % |


Chassis Docs: Docs (in early alpha state - work in progress) is available at: https://hoffipublic.github.io/chassis/



This is what you're (currently) able to generate (all kotlin)

  • Dto classes/interfaces/objects (Data-Transfer-Objects)
    • properties with "known" types
      • mutable and immutable (currently Integer, Long, String, Boolean, java.util.uuid, kotlinx.datetime.Instant, kotlinx.datetime.LocalDateTime (see TYP.kt)
        • mutable and immutable collection types (currently List, Set, Collection, Iterable)
        • nullable, default initializers or initializers specified in DSL
    • properties by referencing their KClass<...>, mutable and immutable (also for collection generic-type)
      • nullable or initializers specified in DSL
    • properties by referencing other model types defined somewhere else in (other) chassis DSLs
    • by Tagging properties in the Dsl, they can "contribute" to generated things e.g.
      • toString()
      • equals()
      • hashCode()
    • primary (public/protected) constructor (via Tag on props)
    • companion object
      • create()
      • createWithUuid()
      • val NULL = ... (therefore, no need to have nullable model variables anymore!)
    • no generic types yet (TBD)
    • extending super classes and interfaces
      • also by referencing other DSL model instances as super type or interfaces
        • ability to specify "common" super classes/interfaces for all models in a modelgroup or all model instances in a model
    • gather (add) all properties (or/and of that ones super classes) by just saying e.g.:
      • propertiesOf(DTO inModelgroup "PERSISTENTGROUP" withModelName "SomeModelName")
      • use this to define an abstract model (code generation might be suppressed for that one) and reference its props from models in your DSL which should also have exactly THAT set of props (DRY).
  • Fillers
    • generate static copy methods to fill any DSL model from any other DSL model
      (as long as the prop names and their types are compatible)
      • specifying CopyBoundrys to specify in DSL how "deep" to copy model objects (or e.g. instead do not recurse but give fresh initialized model instances to a prop)
        • Dto <--> Dto
          • Dto <--> Dco
          • Table (SQL resultRow) <--> Dto
  • Table RDBMS Objects (currently for https://github.com/JetBrains/Exposed)
    • by just specifying tableFor(DTO) in the DSL model
      • no many2many mappings yet
        • one2one to another DSL Model
        • one2many, many2one to another DSL Model
        • if the model has a property named uuid with TYP java.util.Uuid
          • generate PRIMARY KEY to Table and enable FOREIGN KEY integrity on generated jetbrain exposed Tables
            • DSL enable to set DB related property stuff, like PK, nullable, index, etc.
  • CRUD DB access methods (CREATE/insert, READ/select, UPDATE/update, DELETE/delete)
    • either via DB joins of all (recursive) contained DSL model objects
      (inside the DSL model object you want to "CRUD")
      model instance has to have a PK named 'uuid' for join's to work
      • or via distinct selects to the db for each (recursive) contained DSL model object
      • specifying CopyBoundrys to specify in DSL how deep to "CRUD" from DB
        • this will generate separate filler and CRUD Methods for each "set/prefix/businessCase" that needs its own CopyBoundrys

using:

  • ability to use/references other defined models and modelinstances to construct your code class
    • reference another DSL model/instance as super-class, super interface, prop type, prop collection type
      • specify abstract base classes and use their properties and super-class/interfaces to "include" directly (without extending class inheritance)
        (propertiesOf(DTO inModelgroup "PERSISTENTGROUP" withModelName "SomeModelName"))
  • flexible naming strategies for
    • naming props, classes/interfaces/objects, methods, table_names, column_names,
      (CamelCase, snake_case, kebab-case, capitalized, decapitalized, prefixed, postfixed, replace pre/postfix, add to current pre/postfix, etc.)
  • flexible destination spec in DSL
    • absolute package, addendum pre/postfix to package, same for classe names and same for destination path of generated classes
      • either in DSL run or
        • modelgroup (for all models and model instances (dto/table/...))
        • model (for all model instances of that model (dto/table))
        • model instance
        • implement your own strategy like SPECIAL_WINS_ON_ABSOLUTE_STUFF_BUT_CONCAT_ADDENDUMS
  • take preferred defaults without having to specify too much in the DSL
    • just switch the strategies and get "your corporate design governance" compliant code

This is a personal pet project
and on top still very alpha and very work in progress!!!

*(although it is already a complete rewrite of the initial version)

Known Limitations

  • no Model classes with generic types, no List/Set/Collections which hold a generic type (sure works for List<MyModel>)
  • no many2many RDBMS table mapping yet
  • cannot have more than one FK Relation to the same Model (e.g. one2Many to ModelX and on2One also to ModelX)
  • primary key is val uuid : java.util.Uuid (nothing else implemented yet ... no, no Integer PKs, no autoincrements)
  • DTOs get their PK UUID on instantiation in code ... so they have their "identity" given at time of "birth" (that is object instantiation)
    (otherwise each model instance would have NO IDENTITY until they are written to the DB for the first time)
  • many loose ends which are not implemented yet
  • many corner cases that will explode as by now I was concentrating on the "how-to"s and "architecture" instead of feature-completeness and robustness

TODO

current ToDos:

  • implementing COLLECTION, ITERABLE prop types
  • ย 
  • (additional/remove)ToString members on DslModelgroup
  • Primary Keys and FK Constraints on Database Tables (if not UuidTable)
  • Cascading delete

coming up:

  • write the docs on arch, principles, modules and code generation to enable people to participate in this project

  • DB access: SQL UPDATE and DELETE Functions (honouring contained model instances and CopyBoundrys)

    • custom jetbrain exposed lambda parameters to "tweak stuff" on DB CRUD operations from Table's with CopyBoundries (eager/lazy loading via different generated functions)
      • further CRUD DB operations with "own function names" for each CopyBoundry
  • Exposed Extensions

  • many to many relations

  • Properties with generic types

  • collections with generic generic types

  • properties with generic types that are models

  • collections with generic types that are models

  • DB mappings for above properties

Future TODOs

  • primary key of more than one column
  • more generics on models and functions
  • generating other things than kotlin code (e.g. openAPI spec)
  • generating API code (REST, gRPC, ProtoBuffers, ...)
  • generating PlantUML
  • ...
<style> /* automatic heading numbering */ h1 { counter-reset: h2counter; font-size: 24pt; } h2 { counter-reset: h3counter; font-size: 22pt; margin-top: 2em; } h3 { counter-reset: h4counter; font-size: 16pt; } h4 { counter-reset: h5counter; font-size: 14pt; } h5 { counter-reset: h6counter; } h6 { } h2:before { counter-increment: h2counter; content: counter(h2counter) ". "; } h3:before { counter-increment: h3counter; content: counter(h2counter) "." counter(h3counter) ". "; } h4:before { counter-increment: h4counter; content: counter(h2counter) "." counter(h3counter) "." counter(h4counter) ". "; } h5:before { counter-increment: h5counter; content: counter(h2counter) "." counter(h3counter) "." counter(h4counter) "." counter(h5counter) ". "; } h6:before { counter-increment: h6counter; content: counter(h2counter) "." counter(h3counter) "." counter(h4counter) "." counter(h5counter) "." counter(h6counter) ". "; } </style>

Blog posts to write

  • DSL referencing elements
  • DTO code generation (predefined types, predefined inheritance, predefined "helper" and companion methods for each model instance)
  • CRUD DB method generation (by join, by select)
    • copy Boundrys in genrated code instead of lazy loading with a bloated and heavy OR Mapping Framework

debug watch expressions

  • dslCtx.allCtxObjs.filter { it.key.simpleName == "Base" }.map { it.value }
  • dslCtx.genCtx.genModels.filter { it.key.simpleName == "Base" }.map { it.value }
  • DslRefString.REF("disc:commonBaseModelsDisc;modelgroup:Simple;model:Entity;dto")

implementing you're own (non table) proper Models (subelements of model { })

  1. create by e.g. copying a simple existing DslModel Subelement (e.g. class DslDco)
  2. create DslRef subelement for it in class DslRef (and add it to the needed places for reffing in DslRef.kt)
  3. create a GenModel for it in sealed class GenModel
  4. create the IDslApiXxx and IDslImplXxx interfaces for your new proper model subelement (e.g. study DslShowcase.kt)
  5. add a fun xxxNameAndWhereto(...) to interface IDslApiNameAndWheretoOnSubElements
    (if you want to be able to specify where all you xxx generated proper models of a modelgroup should go (package, path, prefix, postfix, ...))
  6. add your Xxx to enum class MODELREFENUM
  7. add your xxx to interface IDslApiModel
  8. add implementation for it to class DslModel and also add a val dslXxxs: MutableMap<String, DslXxx> = mutableMapOf()
  9. at this point a lot of when (...) { should not compile anymore ... add meaningfull stuff to all of them for your Xxx

a minimal interface IDslApiXxx might look like:

interface IDslApiDco : IDslApi
    :   IDslApiModelAndModelSubelementsCommon,
        IDslApiSubelementsOnlyCommon

For table's and CRUDS it is more difficult as these are very heavily dependant on you persistence Framework,
but your xxx should work just fine with the existing JetBrains "exposed" table's.

chassis's People

Contributors

hoffimuc avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.