crudex

package module
v0.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 3, 2024 License: Apache-2.0 Imports: 14 Imported by: 0

README

CRUDEX

The simplest way to build your crud APIs, admin pages (and maybe more).

crudex does not interfere with your code, it just helps you to build your App faster by providing a set of tools to build your CRUD APIs and admin interfaces.

It is based on Gin and Gorm, so you can use all the features of these libraries without any restrictions.

Crudex allows you to create and extend CRUD controllers based on the model you want to expose. It also provides a set of tools to create admin interfaces for your models. It is flexible enough so you can customize the controllers and the admin interfaces as you need.

Crudex comes with predefined scaffold templates for the admin interfaces, but you can create your own templates and use them in your project.

At this time there is no specific configuration for the access permissions but you can handle that with some middlewarethere is no specific configuration for the access permissions but you can handle that with some middleware.

Installation

go get github.com/halicea/crudex

Docs

https://pkg.go.dev/github.com/halicea/crudex

How to use

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/halicea/crudex"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func main() {
	app := gin.New()                                             //create gin app
	db, _ := gorm.Open(sqlite.Open("sample.db"), &gorm.Config{}) //create gorm db connection
	db.AutoMigrate(&Car{}, &Driver{})                            //migrate the models

	crudex.Setup() // this configuration is used by crudex to setup the controllers and scaffold behaviours
	// this is the default configuration, you can customize it by calling the methods on the returned object
	// for example: crudex.Setup().SetScaffoldRootDir("gen").SetScaffoldCreateStrategy(crudex.SCAFFOLD_ALWAYS)
	// Check the documentation for more information on the available methods, or browse the source code

	app.HTMLRender = crudex.NewRenderer() //set the renderer to the one provided by crudex

	var ctrls = []crudex.ICrudCtrl{ // create the controllers for the models
		crudex.New[Car](db).OnRouter(app.Group("cars")).ScaffoldDefaults(),
		crudex.New[Driver](db).OnRouter(app.Group("drivers")).ScaffoldDefaults(),
	}

	crudex.ScaffoldIndex(app, "gen/index.html", ctrls...) // create an index page that lists all the models
	app.Run(":8080")                                      //run the app
}

type Car struct {
	crudex.BaseModel
	//you can also customize the input type and placeholder	through the crud-input and crud-placeholder tags
	Name        string `crud-input:"text" crud-placeholder:"Enter name"`
	License     string `crud-input:"text" crud-placeholder:"Enter the license plate"`
	Description string `crud-input:"wysiwyg" crud-placeholder:"Describe it"`
	Year        int    `crud-input:"number" crud-placeholder:"Model year of the car"`
}

type Driver struct {
	crudex.BaseModel
	Name  string
	CarID uint
	Car   Car `gorm:"foreignKey:CarID"`
}

What you get

For every model there are six(6) routes created by default:

  • GET /model/new Shows a form to create a new record

  • GET /model/edit/:id Shows a form to edit a record

  • GET /model Lists all the records (with html or json)

  • GET /model/:id Shows a single record (with html or json)

  • POST /model/new Creates a new record and redirects to the list. It accepts either form data or json data

  • PUT /model/:id Updates a record and redirects to the list. It accepts either form data or json data

  • DELETE /model/:id Deletes a record and redirects to the list

Cusomization

There are three parts that you can further customize to your needs:

  1. Generated templates

    The templates are exported the first time you create a new model (by default in the 'gen' directory). You can modify them in the way it is suitable for your use case

  2. Scaffold templates

    Scaffold templates are the templates that generate the model templates used by the CrudCtrl[T].

    You can export them once by invoking crudex --export-scaffolds in the root of your module. This will create a scaffolds directory that will be further used to generate the CRUD templates.

    This scaffold templates can be customized to generate new CRUD templates with the look, feel and functionality suitable to You.

    You can also create your own ScaffoldMap and use it to generate the templates.

  3. Controller

    You can create your own controller that builds on top CrudCtrl[T]

    This way you can override the List, Details, Form, List handlers and add your own.

    Behind the scenes crudex uses gin handlers, so you can build any route without additional need to learn something new.

Wishlist

  • [P1] Start with tests

  • [P1] Add more customization options

  • [P1] Initial README

  • [P2] Allow the possibility for different UI packages to be glued to it

    • For example:
      • generate UI templates with daisyUI and React
      • generate UI templates with HTMX and tailwind
      • e.t.c
  • [P2] Add more documentation

  • [P2] Add more tests

  • [P3] Use source generators to scaffold the templates (through go generate)

  • [P3] Create separate package for the template scaffolding and leave just the controllers in this package

  • [P3] Fully document the public methods, interfaces and structs

Documentation

Index

Constants

View Source
const (
	CmdArgStrategyAlways      = "always"
	CmdArgStrategyIfNotExists = "newonly"
	CmdArgStrategyNever       = "never"
)
View Source
const (
	SEARCH_OP_EQUAL           = "="
	SEARCH_OP_NOT_EQUAL       = "<>"
	SEARCH_OP_MATCHES_PATTERN = "~"
	SEARCH_OP_IN              = "in"
	SEARCH_OP_NOT_IN          = "not in"
	SEARCH_OP_LT              = "<"
	SEARCH_OP_LTE             = "<="
	SEARCH_OP_GT              = ">"
	SEARCH_OP_GTE             = ">="
)

TODO: Complete this part of the code to allow search and pagination for the models

Variables

Functions

func BindForm

func BindForm[T any](r *http.Request, out *T) error

func DefaultFormHandler

func DefaultFormHandler[T IModel](c *gin.Context, out *T) error

DefaultFormHandler is a default form binder that binds the form data to a model using the form field names as the model field names

func FlushAll

func FlushAll(dst string, models ...interface{})

func GenDetailTmpl added in v0.0.11

func GenDetailTmpl(data interface{}, rootDir string)

func GenFormTmpl added in v0.0.10

func GenFormTmpl(data interface{}, rootDir string)

func GenLayout

func GenLayout(fileName string, controllers []ICrudCtrl)

func GenListTmpl added in v0.0.10

func GenListTmpl(data interface{}, rootDir string)

func NewRenderer added in v0.0.10

func NewRenderer() multitemplate.Renderer

func Respond added in v0.1.5

func Respond(c *gin.Context, data gin.H, templateName string)

Respond is a function that renders a template with the given data If the request is request accepts application/json it will the data as json If the request is an Htmx request it will render the template with the data If the request is not an Htmx request it will use the layout to render the data

  • The layout should be aware of the data that is passed to it and conditionally render that template

it uses the default hxConfig to render the template See `RenderWithConfig` for more control over the rendering

func RespondWithConfig added in v0.1.5

func RespondWithConfig(c *gin.Context, data gin.H, templateName string, conf IConfig)

RespondWithConfig is a function that renders a template with the given data and the render configuration See Render for more information on the rendering See Config for more information on the configuration

func ScaffoldIndex added in v0.0.10

func ScaffoldIndex(r IRouter, fileName string, controllers ...ICrudCtrl) gin.IRoutes

ScaffoldIndex creates a simple index page that lists all the controllers

Types

type BaseModel

type BaseModel struct {
	gorm.Model
}

func (BaseModel) GetID

func (self BaseModel) GetID() uint

func (BaseModel) SetID

func (self BaseModel) SetID(id uint)

type Config

type Config struct {
	// contains filtered or unexported fields
}

func NewConfig

func NewConfig() *Config

func Setup added in v0.0.11

func Setup() *Config

NewConfig creates a new configuration crud configuration containing all the defaults

func (*Config) EnableLayoutOnNonHxRequest

func (self *Config) EnableLayoutOnNonHxRequest() bool

if true the layout will be used even if the request is not an Htmx request, otherwise the template will be rendered without the layout

func (*Config) HasAPI added in v0.1.5

func (self *Config) HasAPI() bool

HasAPI returns true if the configuration has the API enabled

func (*Config) HasUI added in v0.1.5

func (self *Config) HasUI() bool

HasUI returns true if the configuration has the UI enabled

func (*Config) LayoutDataFunc

func (self *Config) LayoutDataFunc() func(c *gin.Context, data gin.H)

a function that is used to supply the layout with data

func (*Config) LayoutName added in v0.0.10

func (self *Config) LayoutName() string

the layout to use on the templates for full page rendering

func (*Config) ScaffoldMap added in v0.0.11

func (self *Config) ScaffoldMap() IScaffoldMap

func (*Config) ScaffoldRootDir added in v0.0.10

func (self *Config) ScaffoldRootDir() string

where to create the templates

func (*Config) ScaffoldStrategy added in v0.0.10

func (self *Config) ScaffoldStrategy() ScaffoldStrategy

how to create the templates

func (*Config) SetAsDefault added in v0.0.11

func (self *Config) SetAsDefault() *Config

SetAsDefault sets the current configuration as the default configuration

func (*Config) String added in v0.1.5

func (self *Config) String() string

func (*Config) TemplateDirs added in v0.0.10

func (self *Config) TemplateDirs() []string

Which template directories to scan for templates

func (*Config) WithAPI added in v0.1.5

func (self *Config) WithAPI(value bool) *Config

WithAPI sets the configuration to enable the API endpoints

func (*Config) WithCommandLineArgs added in v0.0.10

func (self *Config) WithCommandLineArgs(args []string) *Config

WithCommandLineArgs sets the configuration from the command line arguments

func (*Config) WithEnableLayoutOnNonHxRequest

func (c *Config) WithEnableLayoutOnNonHxRequest(enableLayoutOnNonHxRequest bool) *Config

if true the layout will be used even if the request is not an Htmx request, otherwise the template will be rendered without the layout

func (*Config) WithLayoutDataFunc

func (c *Config) WithLayoutDataFunc(layoutDataFunc func(c *gin.Context, data gin.H)) *Config

WithLayoutDataFunc is function that is used to supply the layout with the data needed to render the layout

func (*Config) WithLayoutName added in v0.0.10

func (c *Config) WithLayoutName(layoutName string) *Config

the layout to use on the templates for full page rendering

func (*Config) WithScaffoldMap added in v0.0.11

func (self *Config) WithScaffoldMap(scaffoldMap IScaffoldMap) *Config

WithScaffoldMap sets the scaffold map that will be used to generate the scaffolded templates

func (*Config) WithScaffoldRootDir added in v0.0.10

func (self *Config) WithScaffoldRootDir(value string) *Config

WithScaffoldRootDir sets the root directory where the scaffolded templates will be placed

func (*Config) WithScaffoldStrategy added in v0.0.10

func (self *Config) WithScaffoldStrategy(value ScaffoldStrategy) *Config

WithScaffoldStrategy sets the strategy to use when creating the scaffolded templates The default is ScaffoldCreateAlways, options are ScaffoldCreateAlways, ScaffoldCreateIfNotExist, ScaffoldCreateNever This option is not used at the moment

func (*Config) WithTemplateDirs

func (c *Config) WithTemplateDirs(dirs ...string) *Config

WithTemplateDirs sets the template directories to scan for templates when setting up the renderer

func (*Config) WithUI added in v0.1.5

func (self *Config) WithUI(value bool) *Config

WithUI sets the configuration to enable the UI endpoints

type CrudCtrl

type CrudCtrl[T IModel] struct {
	Db     *gorm.DB
	Router IRouter
	Config IConfig

	ModelName    string
	ModelKeyName string
	FormBinder   FormBinder[T]
}

CrudCtrl is a controller that implements the basic CRUD operations for a model with a gorm backend

func New

func New[T IModel](db *gorm.DB) *CrudCtrl[T]

New creates a new CRUD controller for the provided model

It uses the default configuration See `NewWithConfig` for more control over the configuration

func NewWithConfig added in v0.1.5

func NewWithConfig[T IModel](db *gorm.DB, conf IConfig) *CrudCtrl[T]

New creates a new CRUD controller for the provided model

It uses the provided configuration to define its behaviour

func (*CrudCtrl[T]) BasePath

func (self *CrudCtrl[T]) BasePath() string

BasePath returns the base path of the controller

func (*CrudCtrl[T]) Delete

func (self *CrudCtrl[T]) Delete(c *gin.Context)

Delete is a handler that deletes an item of the model it is a DELETE request

func (*CrudCtrl[T]) Details

func (self *CrudCtrl[T]) Details(c *gin.Context)

Details is a handler that shows the details of a single item of the model it is a GET request !Requires the template to be named as modelName-details.html where the modelName is lowercased model name

func (*CrudCtrl[T]) Form

func (self *CrudCtrl[T]) Form(c *gin.Context)

Form is a handler that shows the form for editing an item of the model it is a GET request !Requires the template to be named as modelName-edit.html where the modelName is lowercased model name

func (*CrudCtrl[T]) GetModelName

func (self *CrudCtrl[T]) GetModelName() string

Returns the Name of the model

func (*CrudCtrl[T]) List

func (self *CrudCtrl[T]) List(c *gin.Context)

ROUTE HANDLERS List is a handler that lists all the items of the model it is a GET request !Requres the template to be named as modelName-list.html where the modelName is lowercased model name

func (*CrudCtrl[T]) OnRouter

func (self *CrudCtrl[T]) OnRouter(r IRouter) *CrudCtrl[T]

OnRouter attaches the CRUD routes to the provided router

func (*CrudCtrl[T]) Scaffold added in v0.0.10

func (self *CrudCtrl[T]) Scaffold(scaffoldTmpl string, conf *ScaffoldDataModelConfigurator) *CrudCtrl[T]

Creates and Flushes custom scaffold template

func (*CrudCtrl[T]) ScaffoldDefaults added in v0.0.11

func (self *CrudCtrl[T]) ScaffoldDefaults() *CrudCtrl[T]

func (*CrudCtrl[T]) Upsert

func (self *CrudCtrl[T]) Upsert(c *gin.Context)

Upsert is a handler that saves an item of the model it is a POST or PUT request depending on the presence of the id parameter !Requires the form fields to be named as the model field names(case sensitive) It redirects to the details page of the saved item

func (*CrudCtrl[T]) WithFormBinder

func (self *CrudCtrl[T]) WithFormBinder(handler FormBinder[T]) *CrudCtrl[T]

WithFormBinder sets the form binder for the controller to be used when binding form data to the model on the POST and PUT requests. It is used in the Upsert method of the controller If not set, the default form binder is used which assumes that the form field names are the same as the model field names(case sensitive)

type FormBinder

type FormBinder[T IModel] func(c *gin.Context, out *T) error

FormBinder is a function that binds the form data to a model

type IConfig added in v0.0.10

type IConfig interface {

	// how to create the templates
	ScaffoldStrategy() ScaffoldStrategy

	// where to place the scaffolded templates
	ScaffoldRootDir() string

	// the scaffold map contains all the scaffold templates and is used to generate the model templates
	ScaffoldMap() IScaffoldMap

	// Which template directories to scan for templates
	TemplateDirs() []string

	// the layout to use on the templates for full page rendering
	LayoutName() string

	// a function that is used to supply the layout with data
	LayoutDataFunc() func(c *gin.Context, data gin.H)

	// if true the layout will be used if the request is not an Htmx request, otherwise the template will be rendered without the layout
	EnableLayoutOnNonHxRequest() bool

	// HasUI returns true if the response should be rendered as a UI
	HasUI() bool

	// HasAPI returns true if the response should be rendered as an API
	HasAPI() bool
}

IConfig is an interface that defines the configuration for the crudex package

func GetConfig added in v0.1.5

func GetConfig() IConfig

GetConfig returns the default crudex configuration for the package

type ICrudCtrl

type ICrudCtrl interface {
	BasePath() string
	GetModelName() string

	List(c *gin.Context)
	Details(c *gin.Context)
	Form(c *gin.Context)
	Upsert(c *gin.Context)
	Delete(c *gin.Context)
}

ICrudCtrl is an interface that defines the basic CRUD operations for a model

type IModel

type IModel interface {
	GetID() uint
	SetID(id uint)
}

type IResponseCapabilities added in v0.1.5

type IResponseCapabilities interface {
	// HasUI returns true if the response should be rendered as a UI
	HasUI() bool
	// HasAPI returns true if the response should be rendered as an API
	HasAPI() bool
	// EnableLayoutOnNonHxRequest returns true if the layout should be used even if the request is not an Htmx request
	EnableLayoutOnNonHxRequest() bool
}

IResponseCapabilities is an interface that defines the capabilities of the response

IConfig is already compliant with this interface

type IRouter

type IRouter interface {
	gin.IRoutes
	Group(string, ...gin.HandlerFunc) *gin.RouterGroup
	BasePath() string
}

IRouter is an interface that defines the router that is used to scaffold the model templates

It extends the gin.IRoutes interface but adds the BasePath method that returns the base path of the router.

Note: the `RouterGroup` struct has a `BasePath()` method already, so there is no need to implement it

type IScaffoldMap added in v0.0.11

type IScaffoldMap interface {
	// All returns a map of scaffolded template functions
	//
	// These functions return the string representation of the scaffolded template (usually loaded from a file)
	// They are used to scaffold the templates for any given model
	All() map[string]func() string

	// Get returns a scaffolded template function by name
	Get(name string) func() string

	// Returns the function map that is passed to the template engine when generating the templates from the scaffolded templates
	FuncMap() template.FuncMap

	// Export exports the scaffolded templates to the file system in the `scaffolds` directory.
	//
	// If the `forceIfExists` parameter is true, it will overwrite any existing scaffold templates
	Export(forceIfExists bool) error
}

IScaffoldMap is an interface that defines the scaffold map that is used to generate the model templates

type ResponseCapabilities added in v0.1.5

type ResponseCapabilities struct {
	UI     bool
	API    bool
	Layout bool
}

func NewResponseCapabilities added in v0.1.5

func NewResponseCapabilities() *ResponseCapabilities

func (*ResponseCapabilities) EnableLayoutOnNonHxRequest added in v0.1.5

func (self *ResponseCapabilities) EnableLayoutOnNonHxRequest() bool

func (*ResponseCapabilities) HasAPI added in v0.1.5

func (self *ResponseCapabilities) HasAPI() bool

func (*ResponseCapabilities) HasUI added in v0.1.5

func (self *ResponseCapabilities) HasUI() bool

type ScaffoldDataModel added in v0.1.5

type ScaffoldDataModel struct {
	// Type is the reflect.Type of the model
	Type reflect.Type

	// Name is the name of the model
	Name string

	// TemplateFileName is the name of the file where the template will be written
	TemplateFileName string

	Fields []reflect.StructField

	// AllFields is a slice of reflect.StructField that represent all the fields of the model
	AllFields []reflect.StructField
}

ScaffoldDataModel is a struct that holds the information needed to scaffold a template for a model. It is used to scaffold the templates for the given model

func NewScaffoldDataModel added in v0.1.5

func NewScaffoldDataModel(data interface{}, opts *ScaffoldDataModelConfigurator) *ScaffoldDataModel

func (*ScaffoldDataModel) Flush added in v0.1.5

func (md *ScaffoldDataModel) Flush(definition string, strategy ScaffoldStrategy) error

type ScaffoldDataModelConfigurator added in v0.1.5

type ScaffoldDataModelConfigurator struct {
	// RootDir is the root directory where the templates will be written.
	//
	// It is used to create the TemplateFileName of the ModelDescriptor
	RootDir string

	// ModelNameSuffix is the suffix that will be added to the model name
	ModelNameSuffix string

	// TemplateNameSuffix is the suffix that will be added to the template name
	TemplateNameSuffix string

	// TemplateNamePrefix is the prefix that will be added to the template name
	TemplateNamePrefix string

	// TemplateExtension is the extension that will be added to the template name
	TemplateExtension string
}

ScaffoldDataModelConfigurator is a struct that is used to create a ModelDescriptor

it defines the rules for the creation of the ModelDescriptor

type ScaffoldLayoutDataModel added in v0.1.5

type ScaffoldLayoutDataModel struct {
	TemplateFileName string
	Menu             []ScaffoldMenuItem
}

ScaffoldLayoutDataModel is a struct that holds the data needed to scaffold the layout template

type ScaffoldMenuItem added in v0.1.5

type ScaffoldMenuItem struct {
	Title string
	Path  string
}

ScaffoldMenuItem is a struct that holds the data needed to render a link to a model page in the layout template

type ScaffoldStrategy added in v0.0.10

type ScaffoldStrategy int
const (
	// SCAFFOLD_ALWAYS will always scaffold the model templates
	SCAFFOLD_ALWAYS ScaffoldStrategy = iota
	// SCAFFOLD_IF_NOT_EXISTS will only scaffold the model templates if they do not exist
	SCAFFOLD_IF_NOT_EXISTS
	// SCAFFOLD_NEVER will never scaffold the model templates
	SCAFFOLD_NEVER
)

func (ScaffoldStrategy) String added in v0.0.11

func (i ScaffoldStrategy) String() string

type SearchArgs added in v0.0.11

type SearchArgs struct {
	Search string
	Page   int
	Limit  int
}

Struct for filter and pagination

func NewSearchArgs added in v0.0.11

func NewSearchArgs() SearchArgs

func NewSearchArgsFromQuery added in v0.0.11

func NewSearchArgsFromQuery(c *gin.Context) (SearchArgs, error)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL