Safe Haskell | None |
---|---|
Language | Haskell98 |
This module provides functions to generate the auxiliary structures for the user data type
- mkPersist :: CodegenConfig -> PersistDefinitions -> Q [Dec]
- groundhog :: QuasiQuoter
- groundhogFile :: QuasiQuoter
- data CodegenConfig = CodegenConfig {
- namingStyle :: NamingStyle
- migrationFunction :: Maybe String
- mkEntityDecs :: [[THEntityDef] -> Q [Dec]]
- mkEmbeddedDecs :: [[THEmbeddedDef] -> Q [Dec]]
- mkPrimitiveDecs :: [[THPrimitiveDef] -> Q [Dec]]
- defaultCodegenConfig :: CodegenConfig
- defaultMkEntityDecs :: [THEntityDef] -> Q [Dec]
- defaultMkEmbeddedDecs :: [THEmbeddedDef] -> Q [Dec]
- defaultMkPrimitiveDecs :: [THPrimitiveDef] -> Q [Dec]
- data NamingStyle = NamingStyle {
- mkDbEntityName :: String -> String
- mkEntityKeyName :: String -> String
- mkPhantomName :: String -> String -> Int -> String
- mkUniqueKeyPhantomName :: String -> String -> String -> String
- mkUniqueKeyConstrName :: String -> String -> String -> String
- mkUniqueKeyDbName :: String -> String -> String -> String
- mkDbConstrName :: String -> String -> Int -> String
- mkDbConstrAutoKeyName :: String -> String -> Int -> String
- mkDbFieldName :: String -> String -> Int -> String -> Int -> String
- mkExprFieldName :: String -> String -> Int -> String -> Int -> String
- mkExprSelectorName :: String -> String -> String -> Int -> String
- mkNormalFieldName :: String -> String -> Int -> Int -> String
- mkNormalDbFieldName :: String -> String -> Int -> Int -> String
- mkNormalExprFieldName :: String -> String -> Int -> Int -> String
- mkNormalExprSelectorName :: String -> String -> Int -> String
- suffixNamingStyle :: NamingStyle
- persistentNamingStyle :: NamingStyle
- conciseNamingStyle :: NamingStyle
- lowerCaseSuffixNamingStyle :: NamingStyle
- toUnderscore :: String -> String
- firstChar :: (Char -> Char) -> String -> String
- mkTHEntityDef :: NamingStyle -> Dec -> THEntityDef
- mkTHEmbeddedDef :: NamingStyle -> Dec -> THEmbeddedDef
- mkTHPrimitiveDef :: NamingStyle -> Dec -> THPrimitiveDef
- applyEntitySettings :: NamingStyle -> PSEntityDef -> THEntityDef -> THEntityDef
- applyEmbeddedSettings :: PSEmbeddedDef -> THEmbeddedDef -> THEmbeddedDef
- applyPrimitiveSettings :: PSPrimitiveDef -> THPrimitiveDef -> THPrimitiveDef
Settings format
Groundhog needs to analyze the datatypes and create the auxiliary definitions before it can work with them. We use YAML-based settings to list the datatypes and adjust the result of their introspection.
A datatype can be treated as entity or embedded. An entity is stored in its own table, can be referenced in fields of other data, etc. It is a first-class value. An embedded type can only be a field of an entity or another embedded type. For example, the tuples are embedded. You can create your own embedded types and adjust the fields names of an existing embedded type individually for any place where it is used.
Unless the property is marked as mandatory, it can be omitted. In this case value created by the NamingStyle will be used.
data Settable = First {foo :: String, bar :: Int, next :: Maybe (Key Settable BackendSpecific)} deriving (Eq, Show) -- The declaration with defaulted names mkPersist defaultCodegenConfig [groundhog| entity: Settable # If we did not have a constraint, this line would be enough keys: - name: someconstraint constructors: - name: First uniques: - name: someconstraint fields: [foo, bar] |]
Which is equivalent to the example below that has all properties set explicitly.
mkPersist defaultCodegenConfig [groundhog|
definitions: # First level key whose value is a list of definitions. It can be considered an optional header.
# The list elements start with hyphen+space. Keys are separated from values by a colon+space. See full definition at http://yaml.org/spec/1.2/spec.html.
- entity: Settable # Mandatory. Entity datatype name
dbName: Settable # Name of the main table
# schema: public # Name of the schema to which the table belongs
autoKey: # Description of the autoincremented key for data family Key instance
constrName: SettableKey # Name of constructor
default: true # The default key is used when entity is referenced without key wrapper. E.g., "field :: SomeData" instead of "field :: Key SomeData keytype"
keys: # List of the unique keys. An entity may have unique keys only if it has one constructor
- name: someconstraint # This name references names from uniques field of constructor
keyPhantom: Someconstraint # Name of phantom datatype that corresponds for each unique key
constrName: SomeconstraintKey # Name of data family Key instance constructor for this unique key
dbName: Key#Someconstraint # It is used for function "persistName" of "PersistField (Key Settable (Unique Someconstraint))"
fields: [] # Set fields that comprise this unique constraint. It works like setting fields in constructors
mkEmbedded: false # Defines if instance of "Embedded (Key Settable (Unique Someconstraint))" will be created. The "Selector" constructor names are defined by properties of key fields.
default: false # Defines if this unique key is used as default
constructors: # List of constructors. The constructors you don't change can be omitted
- name: First # Mandatory. Constructor name
phantomName: FooBarConstructor # Constructor phantom type name used to guarantee type safety
dbName: First # Name of constructor table which is created only for datatypes with multiple constructors
keyDbName: id # Name for the primary key column
fields: # List of constructor fields. If you don't change a field, you can omit it
- name: foo # The name as in constructor record. If constructor is not a record, the name is created by mkNormalFieldName
. For example, the fields in constructor SomeConstr would have names someConstr0 and someConstr1 by default.
dbName: foo # Column name
exprName: FooField # Name of a field used in expressions
# type: varchar # This would result in having field type DbOther "varchar" instead of DbString. Value of this attribute will be used by DB backend for migration
# default: foo_value # The default value for column in the clause
# reference: # This is explicit reference to a parent table not mapped by Groundhog
# schema: myschema # Optional schema
# table: mytable # Name of the parent table
# columns: [mytable_id] # Parent columns. If the current field is embedded, e.g., a tuple, it will be a composite key
# onDelete: cascade # Defines ON DELETE clause of references. It can have values: no action, restrict, cascade, set null, set default
# onUpdate: restrict # Defines ON UPDATE
# onDelete: cascade # Clauses onDelete and onUpdate can be set outside of reference too. This is deprecated and kept for compatibility
# If onDelete or onUpdate are omitted, the database will choose the action automatically. Note that it may differ across databases.
# For example, MySQL has "restrict" by default, but in PostgreSQL it is "no action".
- name: bar
dbName: bar
exprName: BarField
# For some databases "type: integer" would be appropriate
- name: next
dbName: next
exprName: NextField
uniques:
- name: someconstraint
type: constraint # The type can be be "constraint", "index", or "primary"
fields: [foo, bar] # List of constructor parameter names. Not column names.
# This is example for databases which support expression indexes.
# Note that for checking index during migration expression should be written in exactly the same form as database returns.
# - name: myuniqueindex
# type: index
# fields: [foo, {expr: "(bar + 1)" }]
|]
This is an example of embedded datatype usage.
data Company = Company {name :: String, headquarter :: Address, dataCentre :: Address, salesOffice :: Address} deriving (Eq, Show) data Address = Address {city :: String, zipCode :: String, street :: String} deriving (Eq, Show) mkPersist defaultCodegenConfig [groundhog| definitions: - entity: Company constructors: - name: Company fields: # Property embeddedType of headquarter field is not mentioned, so the corresponding table columns will have names prefixed with headquarter (headquarter$city, headquarter$zip_code, headquarter$street) - name: dataCentre embeddedType: # If a field has an embedded type you can access its subfields. If you do it, the database columns will match with the embedded dbNames (no prefixing). - name: city # Just a regular list of fields. However, note that you should use default dbNames of embedded dbName: dc_city - name: zip_code # Here we use embedded dbName (zip_code) which differs from the name used in Address definition (zipCode) for accessing the field. dbName: dc_zipcode - name: street dbName: dc_street - name: salesOffice embeddedType: # Similar declaration, but using another syntax for YAML objects - {name: city, dbName: sales_city} - {name: zip_code, dbName: sales_zipcode} - {name: street, dbName: sales_street} - embedded: Address fields: # The syntax is the same as for constructor fields. Nested embedded types are allowed. - name: city # This line does nothing and can be omitted. Default settings for city are not changed. - name: zipCode dbName: zip_code # Change column name. # Street is not mentioned so it will have default settings. |]
We can also make our types instances of PrimitivePersistField
to store them in one column.
data WeekDay = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Eq, Show, Enum) data Point = Point Int Int deriving (Eq, Show, Read) mkPersist defaultCodegenConfig [groundhog| definitions: - primitive: WeekDay representation: enum # Its column will have integer type. The conversion will use Enum instance. - primitive: Point representation: showread # Its column will have string type. The conversion will use Show/Read instances. If representation is omitted, showread will be used by default. |]
mkPersist :: CodegenConfig -> PersistDefinitions -> Q [Dec] Source
Creates the auxiliary structures.
Particularly, it creates GADT Field
data instance for referring to the fields in expressions and phantom types for data constructors.
The default names of auxiliary datatypes and names used in database are generated using the naming style and can be changed via configuration.
The datatypes and their generation options are defined via YAML configuration parsed by quasiquoter groundhog
.
groundhog :: QuasiQuoter Source
Converts quasiquoted settings into the datatype used by mkPersist.
groundhogFile :: QuasiQuoter Source
Parses configuration stored in the file
mkPersist defaultCodegenConfig [groundhogFile|../groundhog.yaml|]
Settings for code generation
data CodegenConfig Source
CodegenConfig | |
|
defaultMkEntityDecs :: [THEntityDef] -> Q [Dec] Source
defaultMkEmbeddedDecs :: [THEmbeddedDef] -> Q [Dec] Source
defaultMkPrimitiveDecs :: [THPrimitiveDef] -> Q [Dec] Source
When describing a datatype you can omit the most of the declarations. In this case the omitted parts of description will be automatically generated using the default names created by naming style. Any default name can be overridden by setting its value explicitly.
data NamingStyle Source
Defines how the names are created. The mk* functions correspond to the set* functions. Functions mkNormal* define names of non-record constructor Field
NamingStyle | |
|
suffixNamingStyle :: NamingStyle Source
Default style. Adds "Field" to each record field name.
Example:
data SomeData a = Normal Int | Record { bar :: Maybe String, asc :: a} -- Generated code data NormalConstructor data RecordConstructor instance PersistEntity where data Field (SomeData a) where Normal0Field :: Field NormalConstructor Int BarField :: Field RecordConstructor (Maybe String) AscField :: Field RecordConstructor a ...
persistentNamingStyle :: NamingStyle Source
Creates field names in Persistent fashion prepending constructor names to the fields.
Example:
data SomeData a = Normal Int | Record { bar :: Maybe String, asc :: a} -- Generated code data NormalConstructor data RecordConstructor instance PersistEntity where data Field (SomeData a) where Normal0 :: Field NormalConstructor Int RecordBar :: Field RecordConstructor (Maybe String) RecordAsc :: Field RecordConstructor a ...
conciseNamingStyle :: NamingStyle Source
Creates the shortest field names. It is more likely to lead in name conflicts than other naming styles.
Example:
data SomeData a = Normal Int | Record { bar :: Maybe String, asc :: a} -- Generated code data NormalConstructor data RecordConstructor instance PersistEntity where data Field (SomeData a) where Normal0 :: Field NormalConstructor Int Bar :: Field RecordConstructor (Maybe String) Asc :: Field RecordConstructor a ...
lowerCaseSuffixNamingStyle :: NamingStyle Source
The generated Haskell names of phantom types (constructors, fields, etc.) are the same as with suffixNamingStyle. But the table names and columns are converted from camelCase to underscore_lower_case with toUnderscore
.
toUnderscore :: String -> String Source
Transforms string from camelCase to lower_case_underscore naming convention. ColumnName -> column_name, parseURL -> parse_url, FieldIEEE754Floating -> field_ieee754_floating
Utility functions
mkTHEntityDef :: NamingStyle -> Dec -> THEntityDef Source
mkTHEmbeddedDef :: NamingStyle -> Dec -> THEmbeddedDef Source
mkTHPrimitiveDef :: NamingStyle -> Dec -> THPrimitiveDef Source