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
- have a look at the DSL chassis DSL examples
- find generated (non persistence related) DTO kotlin code in github generated DTO
- find generated (persistence related) exposed tables and CRUD operations in github generated TABLE
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/
- 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
- mutable and immutable collection types (currently
- mutable and immutable (currently
- 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
- also by referencing other DSL model instances as super type or interfaces
- 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).
- properties with "known" types
- 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
- Dto <--> Dto
- 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)
- generate static copy methods to fill any DSL model from any other DSL model
- 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 TYPjava.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.
- DSL enable to set DB related property stuff, like
- generate PRIMARY KEY to Table and enable FOREIGN KEY integrity on generated jetbrain exposed Tables
- no many2many mappings yet
- by just specifying
- 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 aPK named 'uuid' for join's to work
- or via distinct
select
s 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
andCRUD
Methods for each "set/prefix/businessCase" that needs its own CopyBoundrys
- this will generate separate
- or via distinct
- either via DB
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")
)
- specify abstract base classes and use their properties and super-class/interfaces to "include" directly (without extending class inheritance)
- reference another DSL model/instance as super-class, super interface, prop type, prop collection type
- 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.)
- naming props, classes/interfaces/objects, methods, table_names, column_names,
- 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
- either in DSL run or
- absolute package, addendum pre/postfix to package, same for classe names and same for destination path of generated classes
- take preferred defaults without having to specify too much in the DSL
- just switch the strategies and get "your corporate design governance" compliant code
- 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!!!
- 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
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
- custom jetbrain exposed lambda parameters to "tweak stuff" on DB CRUD operations from Table's with CopyBoundries (eager/lazy loading via different generated functions)
-
Exposed Extensions
- upsert ?
- insert/delete if not exists stackoverflow: how-can-i-do-insert-if-not-exists-in-mysql{:target="_blank"}
- upsert ?
-
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
- 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
- ...
- 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
- 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")
- create by e.g. copying a simple existing DslModel Subelement (e.g.
class DslDco
) - create DslRef subelement for it in
class DslRef
(and add it to the needed places for reffing inDslRef.kt
) - create a GenModel for it in
sealed class GenModel
- create the
IDslApiXxx
andIDslImplXxx
interfaces for your new proper model subelement (e.g. studyDslShowcase.kt
) - add a
fun xxxNameAndWhereto(...)
tointerface 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, ...)) - add your Xxx to
enum class MODELREFENUM
- add your xxx to
interface IDslApiModel
- add implementation for it to
class DslModel
and also add aval dslXxxs: MutableMap<String, DslXxx> = mutableMapOf()
- 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.