Restructured
This commit is contained in:
parent
2d6ec2ed17
commit
721e465f32
50 changed files with 1004 additions and 484 deletions
1
.idea/.name
Normal file
1
.idea/.name
Normal file
|
@ -0,0 +1 @@
|
|||
orm
|
451
.idea/dbnavigator.xml
Normal file
451
.idea/dbnavigator.xml
Normal file
|
@ -0,0 +1,451 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DBNavigator.Project.DataEditorManager">
|
||||
<record-view-column-sorting-type value="BY_INDEX" />
|
||||
<value-preview-text-wrapping value="true" />
|
||||
<value-preview-pinned value="false" />
|
||||
</component>
|
||||
<component name="DBNavigator.Project.DataExportManager">
|
||||
<export-instructions>
|
||||
<create-header value="true" />
|
||||
<quote-values-containing-separator value="true" />
|
||||
<quote-all-values value="false" />
|
||||
<value-separator value="" />
|
||||
<file-name value="" />
|
||||
<file-location value="" />
|
||||
<scope value="GLOBAL" />
|
||||
<destination value="FILE" />
|
||||
<format value="EXCEL" />
|
||||
<charset value="windows-1252" />
|
||||
</export-instructions>
|
||||
</component>
|
||||
<component name="DBNavigator.Project.DatabaseBrowserManager">
|
||||
<autoscroll-to-editor value="false" />
|
||||
<autoscroll-from-editor value="true" />
|
||||
<show-object-properties value="true" />
|
||||
<loaded-nodes />
|
||||
</component>
|
||||
<component name="DBNavigator.Project.EditorStateManager">
|
||||
<last-used-providers />
|
||||
</component>
|
||||
<component name="DBNavigator.Project.MethodExecutionManager">
|
||||
<method-browser />
|
||||
<execution-history>
|
||||
<group-entries value="true" />
|
||||
<execution-inputs />
|
||||
</execution-history>
|
||||
<argument-values-cache />
|
||||
</component>
|
||||
<component name="DBNavigator.Project.ObjectDependencyManager">
|
||||
<last-used-dependency-type value="INCOMING" />
|
||||
</component>
|
||||
<component name="DBNavigator.Project.ObjectQuickFilterManager">
|
||||
<last-used-operator value="EQUAL" />
|
||||
<filters />
|
||||
</component>
|
||||
<component name="DBNavigator.Project.ScriptExecutionManager" clear-outputs="true">
|
||||
<recently-used-interfaces />
|
||||
</component>
|
||||
<component name="DBNavigator.Project.Settings">
|
||||
<connections />
|
||||
<browser-settings>
|
||||
<general>
|
||||
<display-mode value="TABBED" />
|
||||
<navigation-history-size value="100" />
|
||||
<show-object-details value="false" />
|
||||
</general>
|
||||
<filters>
|
||||
<object-type-filter>
|
||||
<object-type name="SCHEMA" enabled="true" />
|
||||
<object-type name="USER" enabled="true" />
|
||||
<object-type name="ROLE" enabled="true" />
|
||||
<object-type name="PRIVILEGE" enabled="true" />
|
||||
<object-type name="CHARSET" enabled="true" />
|
||||
<object-type name="TABLE" enabled="true" />
|
||||
<object-type name="VIEW" enabled="true" />
|
||||
<object-type name="MATERIALIZED_VIEW" enabled="true" />
|
||||
<object-type name="NESTED_TABLE" enabled="true" />
|
||||
<object-type name="COLUMN" enabled="true" />
|
||||
<object-type name="INDEX" enabled="true" />
|
||||
<object-type name="CONSTRAINT" enabled="true" />
|
||||
<object-type name="DATASET_TRIGGER" enabled="true" />
|
||||
<object-type name="DATABASE_TRIGGER" enabled="true" />
|
||||
<object-type name="SYNONYM" enabled="true" />
|
||||
<object-type name="SEQUENCE" enabled="true" />
|
||||
<object-type name="PROCEDURE" enabled="true" />
|
||||
<object-type name="FUNCTION" enabled="true" />
|
||||
<object-type name="PACKAGE" enabled="true" />
|
||||
<object-type name="TYPE" enabled="true" />
|
||||
<object-type name="TYPE_ATTRIBUTE" enabled="true" />
|
||||
<object-type name="ARGUMENT" enabled="true" />
|
||||
<object-type name="DIMENSION" enabled="true" />
|
||||
<object-type name="CLUSTER" enabled="true" />
|
||||
<object-type name="DBLINK" enabled="true" />
|
||||
</object-type-filter>
|
||||
</filters>
|
||||
<sorting>
|
||||
<object-type name="COLUMN" sorting-type="NAME" />
|
||||
<object-type name="FUNCTION" sorting-type="NAME" />
|
||||
<object-type name="PROCEDURE" sorting-type="NAME" />
|
||||
<object-type name="ARGUMENT" sorting-type="POSITION" />
|
||||
</sorting>
|
||||
<default-editors>
|
||||
<object-type name="VIEW" editor-type="SELECTION" />
|
||||
<object-type name="PACKAGE" editor-type="SELECTION" />
|
||||
<object-type name="TYPE" editor-type="SELECTION" />
|
||||
</default-editors>
|
||||
</browser-settings>
|
||||
<navigation-settings>
|
||||
<lookup-filters>
|
||||
<lookup-objects>
|
||||
<object-type name="SCHEMA" enabled="true" />
|
||||
<object-type name="USER" enabled="false" />
|
||||
<object-type name="ROLE" enabled="false" />
|
||||
<object-type name="PRIVILEGE" enabled="false" />
|
||||
<object-type name="CHARSET" enabled="false" />
|
||||
<object-type name="TABLE" enabled="true" />
|
||||
<object-type name="VIEW" enabled="true" />
|
||||
<object-type name="MATERIALIZED VIEW" enabled="true" />
|
||||
<object-type name="NESTED TABLE" enabled="false" />
|
||||
<object-type name="COLUMN" enabled="false" />
|
||||
<object-type name="INDEX" enabled="true" />
|
||||
<object-type name="CONSTRAINT" enabled="true" />
|
||||
<object-type name="DATASET TRIGGER" enabled="true" />
|
||||
<object-type name="DATABASE TRIGGER" enabled="true" />
|
||||
<object-type name="SYNONYM" enabled="false" />
|
||||
<object-type name="SEQUENCE" enabled="true" />
|
||||
<object-type name="PROCEDURE" enabled="true" />
|
||||
<object-type name="FUNCTION" enabled="true" />
|
||||
<object-type name="PACKAGE" enabled="true" />
|
||||
<object-type name="TYPE" enabled="true" />
|
||||
<object-type name="TYPE ATTRIBUTE" enabled="false" />
|
||||
<object-type name="ARGUMENT" enabled="false" />
|
||||
<object-type name="DIMENSION" enabled="false" />
|
||||
<object-type name="CLUSTER" enabled="false" />
|
||||
<object-type name="DBLINK" enabled="true" />
|
||||
</lookup-objects>
|
||||
<force-database-load value="false" />
|
||||
<prompt-connection-selection value="true" />
|
||||
<prompt-schema-selection value="true" />
|
||||
</lookup-filters>
|
||||
</navigation-settings>
|
||||
<dataset-grid-settings>
|
||||
<general>
|
||||
<enable-zooming value="true" />
|
||||
</general>
|
||||
<sorting>
|
||||
<nulls-first value="true" />
|
||||
<max-sorting-columns value="4" />
|
||||
</sorting>
|
||||
<tracking-columns>
|
||||
<columnNames value="" />
|
||||
<visible value="true" />
|
||||
<editable value="false" />
|
||||
</tracking-columns>
|
||||
</dataset-grid-settings>
|
||||
<dataset-editor-settings>
|
||||
<text-editor-popup>
|
||||
<active value="false" />
|
||||
<active-if-empty value="false" />
|
||||
<data-length-threshold value="100" />
|
||||
<popup-delay value="1000" />
|
||||
</text-editor-popup>
|
||||
<values-list-popup>
|
||||
<show-popup-button value="true" />
|
||||
<element-count-threshold value="1000" />
|
||||
<data-length-threshold value="250" />
|
||||
</values-list-popup>
|
||||
<general>
|
||||
<fetch-block-size value="100" />
|
||||
<fetch-timeout value="30" />
|
||||
<trim-whitespaces value="true" />
|
||||
<convert-empty-strings-to-null value="true" />
|
||||
<select-content-on-cell-edit value="true" />
|
||||
<large-value-preview-active value="true" />
|
||||
</general>
|
||||
<filters>
|
||||
<prompt-filter-dialog value="true" />
|
||||
<default-filter-type value="BASIC" />
|
||||
</filters>
|
||||
<qualified-text-editor text-length-threshold="300">
|
||||
<content-types>
|
||||
<content-type name="Text" enabled="true" />
|
||||
<content-type name="XML" enabled="true" />
|
||||
<content-type name="DTD" enabled="true" />
|
||||
<content-type name="HTML" enabled="true" />
|
||||
<content-type name="XHTML" enabled="true" />
|
||||
<content-type name="CSS" enabled="true" />
|
||||
<content-type name="SQL" enabled="true" />
|
||||
<content-type name="PL/SQL" enabled="true" />
|
||||
<content-type name="JavaScript" enabled="true" />
|
||||
<content-type name="JSP" enabled="true" />
|
||||
<content-type name="JSPx" enabled="true" />
|
||||
<content-type name="ASP" enabled="true" />
|
||||
<content-type name="YAML" enabled="true" />
|
||||
<content-type name="Bash" enabled="true" />
|
||||
</content-types>
|
||||
</qualified-text-editor>
|
||||
<record-navigation>
|
||||
<navigation-target value="VIEWER" />
|
||||
</record-navigation>
|
||||
</dataset-editor-settings>
|
||||
<code-editor-settings>
|
||||
<general>
|
||||
<show-object-navigation-gutter value="false" />
|
||||
<show-spec-declaration-navigation-gutter value="true" />
|
||||
</general>
|
||||
<confirmations>
|
||||
<save-changes value="false" />
|
||||
<revert-changes value="true" />
|
||||
</confirmations>
|
||||
</code-editor-settings>
|
||||
<code-completion-settings>
|
||||
<filters>
|
||||
<basic-filter>
|
||||
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
|
||||
<filter-element type="RESERVED_WORD" id="function" selected="true" />
|
||||
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
|
||||
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
|
||||
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
|
||||
<filter-element type="OBJECT" id="schema" selected="true" />
|
||||
<filter-element type="OBJECT" id="role" selected="true" />
|
||||
<filter-element type="OBJECT" id="user" selected="true" />
|
||||
<filter-element type="OBJECT" id="privilege" selected="true" />
|
||||
<user-schema>
|
||||
<filter-element type="OBJECT" id="table" selected="true" />
|
||||
<filter-element type="OBJECT" id="view" selected="true" />
|
||||
<filter-element type="OBJECT" id="materialized view" selected="true" />
|
||||
<filter-element type="OBJECT" id="index" selected="true" />
|
||||
<filter-element type="OBJECT" id="constraint" selected="true" />
|
||||
<filter-element type="OBJECT" id="trigger" selected="true" />
|
||||
<filter-element type="OBJECT" id="synonym" selected="false" />
|
||||
<filter-element type="OBJECT" id="sequence" selected="true" />
|
||||
<filter-element type="OBJECT" id="procedure" selected="true" />
|
||||
<filter-element type="OBJECT" id="function" selected="true" />
|
||||
<filter-element type="OBJECT" id="package" selected="true" />
|
||||
<filter-element type="OBJECT" id="type" selected="true" />
|
||||
<filter-element type="OBJECT" id="dimension" selected="true" />
|
||||
<filter-element type="OBJECT" id="cluster" selected="true" />
|
||||
<filter-element type="OBJECT" id="dblink" selected="true" />
|
||||
</user-schema>
|
||||
<public-schema>
|
||||
<filter-element type="OBJECT" id="table" selected="false" />
|
||||
<filter-element type="OBJECT" id="view" selected="false" />
|
||||
<filter-element type="OBJECT" id="materialized view" selected="false" />
|
||||
<filter-element type="OBJECT" id="index" selected="false" />
|
||||
<filter-element type="OBJECT" id="constraint" selected="false" />
|
||||
<filter-element type="OBJECT" id="trigger" selected="false" />
|
||||
<filter-element type="OBJECT" id="synonym" selected="false" />
|
||||
<filter-element type="OBJECT" id="sequence" selected="false" />
|
||||
<filter-element type="OBJECT" id="procedure" selected="false" />
|
||||
<filter-element type="OBJECT" id="function" selected="false" />
|
||||
<filter-element type="OBJECT" id="package" selected="false" />
|
||||
<filter-element type="OBJECT" id="type" selected="false" />
|
||||
<filter-element type="OBJECT" id="dimension" selected="false" />
|
||||
<filter-element type="OBJECT" id="cluster" selected="false" />
|
||||
<filter-element type="OBJECT" id="dblink" selected="false" />
|
||||
</public-schema>
|
||||
<any-schema>
|
||||
<filter-element type="OBJECT" id="table" selected="true" />
|
||||
<filter-element type="OBJECT" id="view" selected="true" />
|
||||
<filter-element type="OBJECT" id="materialized view" selected="true" />
|
||||
<filter-element type="OBJECT" id="index" selected="true" />
|
||||
<filter-element type="OBJECT" id="constraint" selected="true" />
|
||||
<filter-element type="OBJECT" id="trigger" selected="true" />
|
||||
<filter-element type="OBJECT" id="synonym" selected="true" />
|
||||
<filter-element type="OBJECT" id="sequence" selected="true" />
|
||||
<filter-element type="OBJECT" id="procedure" selected="true" />
|
||||
<filter-element type="OBJECT" id="function" selected="true" />
|
||||
<filter-element type="OBJECT" id="package" selected="true" />
|
||||
<filter-element type="OBJECT" id="type" selected="true" />
|
||||
<filter-element type="OBJECT" id="dimension" selected="true" />
|
||||
<filter-element type="OBJECT" id="cluster" selected="true" />
|
||||
<filter-element type="OBJECT" id="dblink" selected="true" />
|
||||
</any-schema>
|
||||
</basic-filter>
|
||||
<extended-filter>
|
||||
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
|
||||
<filter-element type="RESERVED_WORD" id="function" selected="true" />
|
||||
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
|
||||
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
|
||||
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
|
||||
<filter-element type="OBJECT" id="schema" selected="true" />
|
||||
<filter-element type="OBJECT" id="user" selected="true" />
|
||||
<filter-element type="OBJECT" id="role" selected="true" />
|
||||
<filter-element type="OBJECT" id="privilege" selected="true" />
|
||||
<user-schema>
|
||||
<filter-element type="OBJECT" id="table" selected="true" />
|
||||
<filter-element type="OBJECT" id="view" selected="true" />
|
||||
<filter-element type="OBJECT" id="materialized view" selected="true" />
|
||||
<filter-element type="OBJECT" id="index" selected="true" />
|
||||
<filter-element type="OBJECT" id="constraint" selected="true" />
|
||||
<filter-element type="OBJECT" id="trigger" selected="true" />
|
||||
<filter-element type="OBJECT" id="synonym" selected="true" />
|
||||
<filter-element type="OBJECT" id="sequence" selected="true" />
|
||||
<filter-element type="OBJECT" id="procedure" selected="true" />
|
||||
<filter-element type="OBJECT" id="function" selected="true" />
|
||||
<filter-element type="OBJECT" id="package" selected="true" />
|
||||
<filter-element type="OBJECT" id="type" selected="true" />
|
||||
<filter-element type="OBJECT" id="dimension" selected="true" />
|
||||
<filter-element type="OBJECT" id="cluster" selected="true" />
|
||||
<filter-element type="OBJECT" id="dblink" selected="true" />
|
||||
</user-schema>
|
||||
<public-schema>
|
||||
<filter-element type="OBJECT" id="table" selected="true" />
|
||||
<filter-element type="OBJECT" id="view" selected="true" />
|
||||
<filter-element type="OBJECT" id="materialized view" selected="true" />
|
||||
<filter-element type="OBJECT" id="index" selected="true" />
|
||||
<filter-element type="OBJECT" id="constraint" selected="true" />
|
||||
<filter-element type="OBJECT" id="trigger" selected="true" />
|
||||
<filter-element type="OBJECT" id="synonym" selected="true" />
|
||||
<filter-element type="OBJECT" id="sequence" selected="true" />
|
||||
<filter-element type="OBJECT" id="procedure" selected="true" />
|
||||
<filter-element type="OBJECT" id="function" selected="true" />
|
||||
<filter-element type="OBJECT" id="package" selected="true" />
|
||||
<filter-element type="OBJECT" id="type" selected="true" />
|
||||
<filter-element type="OBJECT" id="dimension" selected="true" />
|
||||
<filter-element type="OBJECT" id="cluster" selected="true" />
|
||||
<filter-element type="OBJECT" id="dblink" selected="true" />
|
||||
</public-schema>
|
||||
<any-schema>
|
||||
<filter-element type="OBJECT" id="table" selected="true" />
|
||||
<filter-element type="OBJECT" id="view" selected="true" />
|
||||
<filter-element type="OBJECT" id="materialized view" selected="true" />
|
||||
<filter-element type="OBJECT" id="index" selected="true" />
|
||||
<filter-element type="OBJECT" id="constraint" selected="true" />
|
||||
<filter-element type="OBJECT" id="trigger" selected="true" />
|
||||
<filter-element type="OBJECT" id="synonym" selected="true" />
|
||||
<filter-element type="OBJECT" id="sequence" selected="true" />
|
||||
<filter-element type="OBJECT" id="procedure" selected="true" />
|
||||
<filter-element type="OBJECT" id="function" selected="true" />
|
||||
<filter-element type="OBJECT" id="package" selected="true" />
|
||||
<filter-element type="OBJECT" id="type" selected="true" />
|
||||
<filter-element type="OBJECT" id="dimension" selected="true" />
|
||||
<filter-element type="OBJECT" id="cluster" selected="true" />
|
||||
<filter-element type="OBJECT" id="dblink" selected="true" />
|
||||
</any-schema>
|
||||
</extended-filter>
|
||||
</filters>
|
||||
<sorting enabled="true">
|
||||
<sorting-element type="RESERVED_WORD" id="keyword" />
|
||||
<sorting-element type="RESERVED_WORD" id="datatype" />
|
||||
<sorting-element type="OBJECT" id="column" />
|
||||
<sorting-element type="OBJECT" id="table" />
|
||||
<sorting-element type="OBJECT" id="view" />
|
||||
<sorting-element type="OBJECT" id="materialized view" />
|
||||
<sorting-element type="OBJECT" id="index" />
|
||||
<sorting-element type="OBJECT" id="constraint" />
|
||||
<sorting-element type="OBJECT" id="trigger" />
|
||||
<sorting-element type="OBJECT" id="synonym" />
|
||||
<sorting-element type="OBJECT" id="sequence" />
|
||||
<sorting-element type="OBJECT" id="procedure" />
|
||||
<sorting-element type="OBJECT" id="function" />
|
||||
<sorting-element type="OBJECT" id="package" />
|
||||
<sorting-element type="OBJECT" id="type" />
|
||||
<sorting-element type="OBJECT" id="dimension" />
|
||||
<sorting-element type="OBJECT" id="cluster" />
|
||||
<sorting-element type="OBJECT" id="dblink" />
|
||||
<sorting-element type="OBJECT" id="schema" />
|
||||
<sorting-element type="OBJECT" id="role" />
|
||||
<sorting-element type="OBJECT" id="user" />
|
||||
<sorting-element type="RESERVED_WORD" id="function" />
|
||||
<sorting-element type="RESERVED_WORD" id="parameter" />
|
||||
</sorting>
|
||||
<format>
|
||||
<enforce-code-style-case value="true" />
|
||||
</format>
|
||||
</code-completion-settings>
|
||||
<execution-engine-settings>
|
||||
<statement-execution>
|
||||
<fetch-block-size value="100" />
|
||||
<execution-timeout value="20" />
|
||||
<debug-execution-timeout value="600" />
|
||||
<focus-result value="false" />
|
||||
<prompt-execution value="false" />
|
||||
</statement-execution>
|
||||
<script-execution>
|
||||
<command-line-interfaces />
|
||||
<execution-timeout value="300" />
|
||||
</script-execution>
|
||||
<method-execution>
|
||||
<execution-timeout value="30" />
|
||||
<debug-execution-timeout value="600" />
|
||||
<parameter-history-size value="10" />
|
||||
</method-execution>
|
||||
</execution-engine-settings>
|
||||
<operation-settings>
|
||||
<transactions>
|
||||
<uncommitted-changes>
|
||||
<on-project-close value="ASK" />
|
||||
<on-disconnect value="ASK" />
|
||||
<on-autocommit-toggle value="ASK" />
|
||||
</uncommitted-changes>
|
||||
<multiple-uncommitted-changes>
|
||||
<on-commit value="ASK" />
|
||||
<on-rollback value="ASK" />
|
||||
</multiple-uncommitted-changes>
|
||||
</transactions>
|
||||
<session-browser>
|
||||
<disconnect-session value="ASK" />
|
||||
<kill-session value="ASK" />
|
||||
<reload-on-filter-change value="false" />
|
||||
</session-browser>
|
||||
<compiler>
|
||||
<compile-type value="KEEP" />
|
||||
<compile-dependencies value="ASK" />
|
||||
<always-show-controls value="false" />
|
||||
</compiler>
|
||||
<debugger>
|
||||
<debugger-type value="JDBC" />
|
||||
<use-generic-runners value="true" />
|
||||
</debugger>
|
||||
</operation-settings>
|
||||
<ddl-file-settings>
|
||||
<extensions>
|
||||
<mapping file-type-id="VIEW" extensions="vw" />
|
||||
<mapping file-type-id="TRIGGER" extensions="trg" />
|
||||
<mapping file-type-id="PROCEDURE" extensions="prc" />
|
||||
<mapping file-type-id="FUNCTION" extensions="fnc" />
|
||||
<mapping file-type-id="PACKAGE" extensions="pkg" />
|
||||
<mapping file-type-id="PACKAGE_SPEC" extensions="pks" />
|
||||
<mapping file-type-id="PACKAGE_BODY" extensions="pkb" />
|
||||
<mapping file-type-id="TYPE" extensions="tpe" />
|
||||
<mapping file-type-id="TYPE_SPEC" extensions="tps" />
|
||||
<mapping file-type-id="TYPE_BODY" extensions="tpb" />
|
||||
</extensions>
|
||||
<general>
|
||||
<lookup-ddl-files value="true" />
|
||||
<create-ddl-files value="false" />
|
||||
<synchronize-ddl-files value="true" />
|
||||
<use-qualified-names value="false" />
|
||||
<make-scripts-rerunnable value="true" />
|
||||
</general>
|
||||
</ddl-file-settings>
|
||||
<general-settings>
|
||||
<regional-settings>
|
||||
<date-format value="MEDIUM" />
|
||||
<number-format value="UNGROUPED" />
|
||||
<locale value="SYSTEM_DEFAULT" />
|
||||
<use-custom-formats value="false" />
|
||||
</regional-settings>
|
||||
<environment>
|
||||
<environment-types>
|
||||
<environment-type id="development" name="Development" description="Development environment" color="-2430209/-12296320" readonly-code="false" readonly-data="false" />
|
||||
<environment-type id="integration" name="Integration" description="Integration environment" color="-2621494/-12163514" readonly-code="true" readonly-data="false" />
|
||||
<environment-type id="production" name="Production" description="Productive environment" color="-11574/-10271420" readonly-code="true" readonly-data="true" />
|
||||
<environment-type id="other" name="Other" description="" color="-1576/-10724543" readonly-code="false" readonly-data="false" />
|
||||
</environment-types>
|
||||
<visibility-settings>
|
||||
<connection-tabs value="true" />
|
||||
<dialog-headers value="true" />
|
||||
<object-editor-tabs value="true" />
|
||||
<script-editor-tabs value="false" />
|
||||
<execution-result-tabs value="true" />
|
||||
</visibility-settings>
|
||||
</environment>
|
||||
</general-settings>
|
||||
</component>
|
||||
<component name="DBNavigator.Project.StatementExecutionManager">
|
||||
<execution-variables />
|
||||
</component>
|
||||
</project>
|
|
@ -1,24 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/test/models/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/test/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tool/packages" />
|
||||
</content>
|
||||
<content url="file://$MODULE_DIR$/../serialize">
|
||||
<excludeFolder url="file://$MODULE_DIR$/../serialize/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../serialize/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../serialize/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../serialize/test/models/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../serialize/test/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../serialize/tool/packages" />
|
||||
<content url="file://$MODULE_DIR$/angel_orm" />
|
||||
<content url="file://$MODULE_DIR$/angel_orm_generator">
|
||||
<excludeFolder url="file://$MODULE_DIR$/angel_orm_generator/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/angel_orm_generator/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/angel_orm_generator/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/angel_orm_generator/test/models/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/angel_orm_generator/test/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/angel_orm_generator/tool/packages" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="postgres::build.dart" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/tool/build.dart" />
|
||||
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||
<option name="filePath" value="$PROJECT_DIR$/angel_orm_generator/tool/build.dart" />
|
||||
<option name="workingDirectory" value="$PROJECT_DIR$/angel_orm_generator" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1 +1,7 @@
|
|||
language: dart
|
||||
language: dart
|
||||
script: bash tool/.travis.sh
|
||||
before_script:
|
||||
- psql -c 'create database angel_orm_test;' -U postgres
|
||||
- psql -c "CREATE USER angel_orm WITH PASSWORD 'angel_orm';" -U postgres
|
||||
services:
|
||||
- postgresql
|
35
README.md
35
README.md
|
@ -2,15 +2,31 @@
|
|||
[![Pub](https://img.shields.io/pub/v/angel_orm.svg)](https://pub.dartlang.org/packages/angel_orm)
|
||||
[![build status](https://travis-ci.org/angel-dart/orm.svg)](https://travis-ci.org/angel-dart/orm)
|
||||
|
||||
**This project is currently in the early stages, and may change at any given
|
||||
time without warning.**
|
||||
|
||||
Source-generated PostgreSQL ORM for use with the
|
||||
[Angel framework](https://angel-dart.github.io).
|
||||
Now you can combine the power and flexibility of Angel with a strongly-typed ORM.
|
||||
|
||||
Currently supported:
|
||||
* PostgreSQL
|
||||
* [Usage](#usage)
|
||||
* [Model Definitions](#models)
|
||||
* [MVC Example](#example)
|
||||
* [Relationships](#relations)
|
||||
|
||||
# Usage
|
||||
You'll need these dependencies in your `pubspec.yaml`:
|
||||
```yaml
|
||||
dependencies:
|
||||
angel_orm: ^1.0.0-alpha
|
||||
dev_dependencies:
|
||||
angel_orm_generator: ^1.0.0-alpha
|
||||
build_runner: ^0.3.0
|
||||
```
|
||||
|
||||
`package:angel_orm_generator` exports two classes that you can include
|
||||
in a `package:build` flow:
|
||||
* `PostgreORMGenerator` - Fueled by `package:source_gen`; include this within a `GeneratorBuilder`.
|
||||
* `SQLMigrationGenerator` - This is its own `Builder`; it generates a SQL schema, as well as a SQL script to drop a generated table.
|
||||
|
||||
You should pass an `InputSet` containing your project's models.
|
||||
|
||||
# Models
|
||||
Your model, courtesy of `package:angel_serialize`:
|
||||
|
@ -38,6 +54,13 @@ Models can use the `@Alias()` annotation; `package:angel_orm` obeys it.
|
|||
After building, you'll have access to a `Query` class with strongly-typed methods that
|
||||
allow to run asynchronous queries without a headache.
|
||||
|
||||
**IMPORTANT:** The ORM *assumes* that you are using `package:angel_serialize`, and will only generate code
|
||||
designed for such a workflow. Save yourself a headache and build models with `angel_serialize`:
|
||||
|
||||
https://github.com/angel-dart/serialize
|
||||
|
||||
# Example
|
||||
|
||||
MVC just got a whole lot easier:
|
||||
|
||||
```dart
|
||||
|
@ -91,7 +114,7 @@ class CarService extends Controller {
|
|||
```
|
||||
|
||||
# Relations
|
||||
**NOTE**: This is not yet implemented.
|
||||
**NOTE**: This is not yet implemented. Expect to see more documentation about this soon.
|
||||
|
||||
* `@HasOne()`
|
||||
* `@HasMany()`
|
||||
|
|
56
angel_orm/.gitignore
vendored
Normal file
56
angel_orm/.gitignore
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
7
angel_orm/README.md
Normal file
7
angel_orm/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# angel_orm
|
||||
Runtime support for Angel's ORM. Includes SQL expression generators, as well
|
||||
as a friendly `PostgreSQLConnectionPool` class that you can use to pool connections
|
||||
to a PostgreSQL database.
|
||||
|
||||
For documentation about the ORM, head to the main project repo:
|
||||
https://github.com/angel-dart/orm
|
11
angel_orm/pubspec.yaml
Normal file
11
angel_orm/pubspec.yaml
Normal file
|
@ -0,0 +1,11 @@
|
|||
name: angel_orm
|
||||
version: 1.0.0-alpha
|
||||
description: Runtime support for Angel's ORM.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/orm
|
||||
environment:
|
||||
sdk: ">=1.19.0"
|
||||
dependencies:
|
||||
intl: ^0.15.1
|
||||
pool: ^1.0.0
|
||||
postgres: ^0.9.5
|
2
angel_orm_generator/.analysis-options
Normal file
2
angel_orm_generator/.analysis-options
Normal file
|
@ -0,0 +1,2 @@
|
|||
analyzer:
|
||||
strong-mode: true
|
56
angel_orm_generator/.gitignore
vendored
Normal file
56
angel_orm_generator/.gitignore
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
21
angel_orm_generator/LICENSE
Normal file
21
angel_orm_generator/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 The Angel Framework
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
8
angel_orm_generator/README.md
Normal file
8
angel_orm_generator/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# angel_orm_generator
|
||||
Source code generators for Angel's ORM.
|
||||
This package can generate:
|
||||
* A strongly-typed ORM
|
||||
* SQL migration scripts
|
||||
|
||||
For documentation about the ORM, head to the main project repo:
|
||||
https://github.com/angel-dart/orm
|
|
@ -1,18 +1,14 @@
|
|||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:angel_serialize/build_context.dart' as serialize;
|
||||
import 'package:angel_serialize/context.dart' as serialize;
|
||||
import 'package:angel_orm/angel_orm.dart';
|
||||
import 'package:angel_serialize_generator/src/find_annotation.dart';
|
||||
import 'package:angel_serialize_generator/build_context.dart' as serialize;
|
||||
import 'package:angel_serialize_generator/context.dart' as serialize;
|
||||
import 'package:build/build.dart';
|
||||
import 'package:inflection/inflection.dart';
|
||||
import 'package:recase/recase.dart';
|
||||
import '../../annotations.dart';
|
||||
import '../../migration.dart';
|
||||
import '../../relations.dart';
|
||||
import 'package:angel_serialize/src/find_annotation.dart';
|
||||
import 'package:source_gen/src/annotation.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
import 'postgres_build_context.dart';
|
||||
|
||||
// TODO: Should add id, createdAt, updatedAt...
|
||||
PostgresBuildContext buildContext(
|
||||
ClassElement clazz,
|
||||
ORM annotation,
|
|
@ -1,19 +1,9 @@
|
|||
import 'dart:async';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:angel_orm/angel_orm.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:code_builder/dart/async.dart';
|
||||
import 'package:code_builder/dart/core.dart';
|
||||
import 'package:code_builder/code_builder.dart';
|
||||
import 'package:inflection/inflection.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:recase/recase.dart';
|
||||
import 'package:source_gen/src/annotation.dart';
|
||||
import 'package:source_gen/src/utils.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
import '../../annotations.dart';
|
||||
import '../../migration.dart';
|
||||
import 'package:angel_serialize/src/find_annotation.dart';
|
||||
import 'build_context.dart';
|
||||
import 'postgres_build_context.dart';
|
||||
|
|
@ -1,19 +1,14 @@
|
|||
import 'dart:async';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:angel_orm/angel_orm.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:code_builder/dart/async.dart';
|
||||
import 'package:code_builder/dart/core.dart';
|
||||
import 'package:code_builder/code_builder.dart';
|
||||
import 'package:inflection/inflection.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:recase/recase.dart';
|
||||
import 'package:source_gen/src/annotation.dart';
|
||||
import 'package:source_gen/src/utils.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
import '../../annotations.dart';
|
||||
import '../../migration.dart';
|
||||
import 'package:angel_serialize/src/find_annotation.dart';
|
||||
import 'build_context.dart';
|
||||
import 'postgres_build_context.dart';
|
||||
|
||||
|
@ -154,6 +149,12 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
|||
// Add insert()...
|
||||
clazz.addMethod(buildInsertMethod(ctx), asStatic: true);
|
||||
|
||||
// Add insertX()
|
||||
clazz.addMethod(buildInsertModelMethod(ctx), asStatic: true);
|
||||
|
||||
// Add updateX()
|
||||
clazz.addMethod(buildUpdateModelMethod(ctx), asStatic: true);
|
||||
|
||||
// Add getAll() => new TodoQuery().get();
|
||||
clazz.addMethod(
|
||||
new MethodBuilder('getAll',
|
||||
|
@ -315,7 +316,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
|||
}
|
||||
|
||||
void _addReturning(StringBuffer buf, PostgresBuildContext ctx) {
|
||||
buf.write(' RETURNING (');
|
||||
buf.write(' RETURNING ');
|
||||
int i = 0;
|
||||
ctx.fields.forEach((field) {
|
||||
if (i++ > 0) buf.write(', ');
|
||||
|
@ -323,7 +324,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
|||
buf.write('"$name"');
|
||||
});
|
||||
|
||||
buf.write(');');
|
||||
buf.write(';');
|
||||
}
|
||||
|
||||
void _ensureDates(MethodBuilder meth, PostgresBuildContext ctx) {
|
||||
|
@ -424,6 +425,44 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
|||
genericTypes: [new TypeBuilder(ctx.modelClassName)]));
|
||||
meth.addPositional(
|
||||
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
|
||||
var buf = reference('buf'), whereClause = reference('whereClause');
|
||||
|
||||
meth.addStatement(varField('buf',
|
||||
value: lib$core.StringBuffer
|
||||
.newInstance([literal('DELETE FROM "${ctx.tableName}"')])));
|
||||
meth.addStatement(varField('whereClause',
|
||||
value: reference('where').invoke('toWhereClause', [])));
|
||||
|
||||
var ifStmt = ifThen(whereClause.notEquals(literal(null)), [
|
||||
buf.invoke('write', [literal(' ') + whereClause])
|
||||
]);
|
||||
meth.addStatement(ifStmt);
|
||||
|
||||
for (var relation in RELATIONS) {
|
||||
var ref = reference('_$relation');
|
||||
var upper = relation.toUpperCase();
|
||||
ifStmt.addStatement(ifThen(ref.property('isNotEmpty'), [
|
||||
buf.invoke('write', [
|
||||
literal(' $upper (') +
|
||||
ref.invoke('join', [literal(', ')]) +
|
||||
literal(')')
|
||||
])
|
||||
]));
|
||||
}
|
||||
|
||||
var litBuf = new StringBuffer();
|
||||
_addReturning(litBuf, ctx);
|
||||
meth.addStatement(buf.invoke('write', [literal(litBuf.toString())]));
|
||||
|
||||
var streamController = new TypeBuilder('StreamController',
|
||||
genericTypes: [new TypeBuilder(ctx.modelClassName)]);
|
||||
meth.addStatement(varField('ctrl',
|
||||
type: streamController, value: streamController.newInstance([])));
|
||||
|
||||
var future =
|
||||
reference('connection').invoke('query', [buf.invoke('toString', [])]);
|
||||
_invokeStreamClosure(future, meth);
|
||||
|
||||
return meth;
|
||||
}
|
||||
|
||||
|
@ -438,32 +477,21 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
|||
|
||||
var id = reference('id');
|
||||
var connection = reference('connection');
|
||||
var beforeDelete = reference('__ormBeforeDelete__');
|
||||
var result = reference('result');
|
||||
|
||||
// var __ormBeforeDelete__ = await XQuery.getOne(id, connection);
|
||||
meth.addStatement(varField('__ormBeforeDelete__',
|
||||
value: new TypeBuilder(ctx.queryClassName)
|
||||
.invoke('getOne', [id, connection]).asAwait()));
|
||||
var buf = new StringBuffer('DELETE FROM "${ctx.tableName}" WHERE id = @id');
|
||||
_addReturning(buf, ctx);
|
||||
|
||||
// await connection.execute('...');
|
||||
meth.addStatement(varField('result',
|
||||
value: connection.invoke('execute', [
|
||||
literal('DELETE FROM "${ctx.tableName}" WHERE id = @id;')
|
||||
value: connection.invoke('query', [
|
||||
literal(buf.toString())
|
||||
], namedArguments: {
|
||||
'substitutionValues': map({'id': id})
|
||||
}).asAwait()));
|
||||
|
||||
meth.addStatement(ifThen(result.notEquals(literal(1)), [
|
||||
lib$core.StateError.newInstance([
|
||||
literal('DELETE query deleted ') +
|
||||
result +
|
||||
literal(' row(s), instead of exactly 1 row.')
|
||||
])
|
||||
]));
|
||||
|
||||
// return __ormBeforeDelete__
|
||||
meth.addStatement(beforeDelete.asReturn());
|
||||
meth.addStatement(
|
||||
reference('parseRow').call([result[literal(0)]]).asReturn());
|
||||
return meth;
|
||||
}
|
||||
|
||||
|
@ -501,43 +529,85 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
|
|||
}
|
||||
});
|
||||
|
||||
buf.write(');');
|
||||
buf.write(')');
|
||||
// meth.addStatement(lib$core.print.call([literal(buf.toString())]));
|
||||
|
||||
_addReturning(buf, ctx);
|
||||
_ensureDates(meth, ctx);
|
||||
|
||||
var substitutionValues = _buildSubstitutionValues(ctx);
|
||||
|
||||
// connection.execute...
|
||||
var connection = reference('connection'), nRows = reference('nRows');
|
||||
meth.addStatement(varField('nRows',
|
||||
value: connection.invoke('execute', [
|
||||
literal(buf.toString())
|
||||
var connection = reference('connection');
|
||||
var query = literal(buf.toString());
|
||||
var result = reference('result');
|
||||
meth.addStatement(varField('result',
|
||||
value: connection.invoke('query', [
|
||||
query
|
||||
], namedArguments: {
|
||||
'substitutionValues': map(substitutionValues)
|
||||
}).asAwait()));
|
||||
meth.addStatement(
|
||||
reference('parseRow').call([result[literal(0)]]).asReturn());
|
||||
return meth;
|
||||
}
|
||||
|
||||
meth.addStatement(ifThen(nRows < literal(1), [
|
||||
lib$core.StateError.newInstance([
|
||||
literal('Insertion into "${ctx.tableName}" table failed.')
|
||||
]).asThrow()
|
||||
MethodBuilder buildInsertModelMethod(PostgresBuildContext ctx) {
|
||||
var rc = new ReCase(ctx.modelClassName);
|
||||
var meth = new MethodBuilder('insert${rc.pascalCase}',
|
||||
returnType: new TypeBuilder('Future',
|
||||
genericTypes: [new TypeBuilder(ctx.modelClassName)]));
|
||||
|
||||
meth.addPositional(
|
||||
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
|
||||
meth.addPositional(
|
||||
parameter(rc.snakeCase, [new TypeBuilder(ctx.modelClassName)]));
|
||||
|
||||
Map<String, ExpressionBuilder> args = {};
|
||||
var ref = reference(rc.snakeCase);
|
||||
|
||||
ctx.fields.forEach((f) {
|
||||
if (f.name != 'id') args[f.name] = ref.property(f.name);
|
||||
});
|
||||
|
||||
meth.addStatement(new TypeBuilder(ctx.queryClassName)
|
||||
.invoke('insert', [reference('connection')], namedArguments: args)
|
||||
.asReturn());
|
||||
|
||||
return meth;
|
||||
}
|
||||
|
||||
MethodBuilder buildUpdateModelMethod(PostgresBuildContext ctx) {
|
||||
var rc = new ReCase(ctx.modelClassName);
|
||||
var meth = new MethodBuilder('update${rc.pascalCase}',
|
||||
returnType: new TypeBuilder('Future',
|
||||
genericTypes: [new TypeBuilder(ctx.modelClassName)]));
|
||||
|
||||
meth.addPositional(
|
||||
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
|
||||
meth.addPositional(
|
||||
parameter(rc.snakeCase, [new TypeBuilder(ctx.modelClassName)]));
|
||||
|
||||
// var query = new XQuery();
|
||||
var ref = reference(rc.snakeCase);
|
||||
var query = reference('query');
|
||||
meth.addStatement(varField('query',
|
||||
value: new TypeBuilder(ctx.queryClassName).newInstance([])));
|
||||
|
||||
// query.where.id.equals(x.id);
|
||||
meth.addStatement(query.property('where').property('id').invoke('equals', [
|
||||
lib$core.int.invoke('parse', [ref.property('id')])
|
||||
]));
|
||||
|
||||
// Query the last value...
|
||||
/*
|
||||
var currval = await connection.query("SELECT * FROM cars WHERE id = currval(pg_get_serial_sequence('cars', 'id'));");
|
||||
print(currval);
|
||||
return parseRow(currval[0]);
|
||||
*/
|
||||
// return query.update(connection, ...).first;
|
||||
Map<String, ExpressionBuilder> args = {};
|
||||
ctx.fields.forEach((f) {
|
||||
if (f.name != 'id') args[f.name] = ref.property(f.name);
|
||||
});
|
||||
|
||||
var update =
|
||||
query.invoke('update', [reference('connection')], namedArguments: args);
|
||||
meth.addStatement(update.property('first').asReturn());
|
||||
|
||||
var currVal = reference('currVal');
|
||||
meth.addStatement(varField('currVal',
|
||||
value: connection.invoke('query', [
|
||||
literal(
|
||||
'SELECT * FROM "${ctx.tableName}" WHERE id = currval(pg_get_serial_sequence(\'${ctx.tableName}\', \'id\'));')
|
||||
]).asAwait()));
|
||||
meth.addStatement(
|
||||
reference('parseRow').call([currVal[literal(0)]]).asReturn());
|
||||
return meth;
|
||||
}
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
import 'package:angel_orm/angel_orm.dart';
|
||||
import 'package:angel_serialize_generator/context.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:angel_serialize/context.dart';
|
||||
import '../../annotations.dart';
|
||||
import '../../migration.dart';
|
||||
import '../../relations.dart';
|
||||
|
||||
class PostgresBuildContext extends BuildContext {
|
||||
DartType _dateTimeTypeCache;
|
20
angel_orm_generator/pubspec.yaml
Normal file
20
angel_orm_generator/pubspec.yaml
Normal file
|
@ -0,0 +1,20 @@
|
|||
name: angel_orm_generator
|
||||
version: 1.0.0-alpha
|
||||
description: Code generators for Angel's ORM.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/orm
|
||||
environment:
|
||||
sdk: ">=1.19.0"
|
||||
dependencies:
|
||||
angel_orm: ^1.0.0-alpha
|
||||
angel_serialize_generator: ^1.0.0-alpha
|
||||
code_builder: ^1.0.0
|
||||
inflection: ^0.4.1
|
||||
recase: ^1.0.0
|
||||
source_gen: ^0.6.0
|
||||
dev_dependencies:
|
||||
angel_diagnostics: ^1.0.0
|
||||
angel_framework: ^1.0.0
|
||||
angel_test: ^1.0.0
|
||||
build_runner: ^0.3.0
|
||||
test: ^0.12.0
|
|
@ -106,12 +106,26 @@ main() {
|
|||
expect(cars, isEmpty);
|
||||
});
|
||||
|
||||
test('delete', () async {
|
||||
var query = new CarQuery();
|
||||
test('delete stream', () async {
|
||||
var query = new CarQuery()..where.make.equals('Ferrari');
|
||||
var cars = await query.delete(connection).toList();
|
||||
expect(cars, hasLength(1));
|
||||
expect(cars.first.toJson(), ferrari.toJson());
|
||||
});
|
||||
|
||||
test('update', () async {
|
||||
var query = new CarQuery()..where.id.equals(int.parse(ferrari.id));
|
||||
var cars = await query.update(connection, make: 'Hyundai').toList();
|
||||
expect(cars, hasLength(1));
|
||||
expect(cars.first.make, 'Hyundai');
|
||||
});
|
||||
|
||||
test('update car', () async {
|
||||
var cloned = ferrari.clone()..make = 'Angel';
|
||||
var car = await CarQuery.updateCar(connection, cloned);
|
||||
print(car.toJson());
|
||||
expect(car.toJson(), cloned.toJson());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -126,8 +140,25 @@ main() {
|
|||
expect(car.make, 'Honda');
|
||||
expect(car.description, 'Hello');
|
||||
expect(car.familyFriendly, isTrue);
|
||||
expect(DATE_YMD_HMS.format(car.recalledAt), DATE_YMD_HMS.format(recalledAt));
|
||||
expect(
|
||||
DATE_YMD_HMS.format(car.recalledAt), DATE_YMD_HMS.format(recalledAt));
|
||||
expect(car.createdAt, allOf(isNotNull, equals(car.updatedAt)));
|
||||
});
|
||||
|
||||
test('insert car', () async {
|
||||
var recalledAt = new DateTime.now();
|
||||
var beetle = new Car(
|
||||
make: 'Beetle',
|
||||
description: 'Herbie',
|
||||
familyFriendly: true,
|
||||
recalledAt: recalledAt);
|
||||
var car = await CarQuery.insertCar(connection, beetle);
|
||||
print(car.toJson());
|
||||
expect(car.make, beetle.make);
|
||||
expect(car.description, beetle.description);
|
||||
expect(car.familyFriendly, beetle.familyFriendly);
|
||||
expect(DATE_YMD_HMS.format(car.recalledAt),
|
||||
DATE_YMD_HMS.format(beetle.recalledAt));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -46,4 +46,8 @@ class Author extends _Author {
|
|||
};
|
||||
|
||||
static Author parse(Map map) => new Author.fromJson(map);
|
||||
|
||||
Author clone() {
|
||||
return new Author.fromJson(toJson());
|
||||
}
|
||||
}
|
|
@ -95,8 +95,7 @@ class AuthorQuery {
|
|||
var __ormNow__ = new DateTime.now();
|
||||
var ctrl = new StreamController<Author>();
|
||||
connection.query(
|
||||
buf.toString() +
|
||||
' RETURNING ("id", "name", "created_at", "updated_at");',
|
||||
buf.toString() + ' RETURNING "id", "name", "created_at", "updated_at";',
|
||||
substitutionValues: {
|
||||
'name': name,
|
||||
'createdAt': createdAt != null ? createdAt : __ormNow__,
|
||||
|
@ -108,38 +107,69 @@ class AuthorQuery {
|
|||
return ctrl.stream;
|
||||
}
|
||||
|
||||
Stream<Author> delete(PostgreSQLConnection connection) async {}
|
||||
Stream<Author> delete(PostgreSQLConnection connection) {
|
||||
var buf = new StringBuffer('DELETE FROM "authors"');
|
||||
var whereClause = where.toWhereClause();
|
||||
if (whereClause != null) {
|
||||
buf.write(' ' + whereClause);
|
||||
if (_and.isNotEmpty) {
|
||||
buf.write(' AND (' + _and.join(', ') + ')');
|
||||
}
|
||||
if (_or.isNotEmpty) {
|
||||
buf.write(' OR (' + _or.join(', ') + ')');
|
||||
}
|
||||
if (_not.isNotEmpty) {
|
||||
buf.write(' NOT (' + _not.join(', ') + ')');
|
||||
}
|
||||
}
|
||||
buf.write(' RETURNING "id", "name", "created_at", "updated_at";');
|
||||
StreamController<Author> ctrl = new StreamController<Author>();
|
||||
connection.query(buf.toString()).then((rows) {
|
||||
rows.map(parseRow).forEach(ctrl.add);
|
||||
ctrl.close();
|
||||
}).catchError(ctrl.addError);
|
||||
return ctrl.stream;
|
||||
}
|
||||
|
||||
static Future<Author> deleteOne(
|
||||
int id, PostgreSQLConnection connection) async {
|
||||
var __ormBeforeDelete__ = await AuthorQuery.getOne(id, connection);
|
||||
var result = await connection.execute(
|
||||
'DELETE FROM "authors" WHERE id = @id;',
|
||||
var result = await connection.query(
|
||||
'DELETE FROM "authors" WHERE id = @id RETURNING "id", "name", "created_at", "updated_at";',
|
||||
substitutionValues: {'id': id});
|
||||
if (result != 1) {
|
||||
new StateError('DELETE query deleted ' +
|
||||
result +
|
||||
' row(s), instead of exactly 1 row.');
|
||||
}
|
||||
return __ormBeforeDelete__;
|
||||
return parseRow(result[0]);
|
||||
}
|
||||
|
||||
static Future<Author> insert(PostgreSQLConnection connection,
|
||||
{String name, DateTime createdAt, DateTime updatedAt}) async {
|
||||
var __ormNow__ = new DateTime.now();
|
||||
var nRows = await connection.execute(
|
||||
'INSERT INTO "authors" ("name", "created_at", "updated_at") VALUES (@name, @createdAt, @updatedAt);',
|
||||
var result = await connection.query(
|
||||
'INSERT INTO "authors" ("name", "created_at", "updated_at") VALUES (@name, @createdAt, @updatedAt) RETURNING "id", "name", "created_at", "updated_at";',
|
||||
substitutionValues: {
|
||||
'name': name,
|
||||
'createdAt': createdAt != null ? createdAt : __ormNow__,
|
||||
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
|
||||
});
|
||||
if (nRows < 1) {
|
||||
throw new StateError('Insertion into "authors" table failed.');
|
||||
}
|
||||
var currVal = await connection.query(
|
||||
'SELECT * FROM "authors" WHERE id = currval(pg_get_serial_sequence(\'authors\', \'id\'));');
|
||||
return parseRow(currVal[0]);
|
||||
return parseRow(result[0]);
|
||||
}
|
||||
|
||||
static Future<Author> insertAuthor(
|
||||
PostgreSQLConnection connection, Author author) {
|
||||
return AuthorQuery.insert(connection,
|
||||
name: author.name,
|
||||
createdAt: author.createdAt,
|
||||
updatedAt: author.updatedAt);
|
||||
}
|
||||
|
||||
static Future<Author> updateAuthor(
|
||||
PostgreSQLConnection connection, Author author) {
|
||||
var query = new AuthorQuery();
|
||||
query.where.id.equals(int.parse(author.id));
|
||||
return query
|
||||
.update(connection,
|
||||
name: author.name,
|
||||
createdAt: author.createdAt,
|
||||
updatedAt: author.updatedAt)
|
||||
.first;
|
||||
}
|
||||
|
||||
static Stream<Author> getAll(PostgreSQLConnection connection) =>
|
|
@ -51,4 +51,8 @@ class Book extends _Book {
|
|||
};
|
||||
|
||||
static Book parse(Map map) => new Book.fromJson(map);
|
||||
|
||||
Book clone() {
|
||||
return new Book.fromJson(toJson());
|
||||
}
|
||||
}
|
|
@ -97,8 +97,7 @@ class BookQuery {
|
|||
var __ormNow__ = new DateTime.now();
|
||||
var ctrl = new StreamController<Book>();
|
||||
connection.query(
|
||||
buf.toString() +
|
||||
' RETURNING ("id", "name", "created_at", "updated_at");',
|
||||
buf.toString() + ' RETURNING "id", "name", "created_at", "updated_at";',
|
||||
substitutionValues: {
|
||||
'name': name,
|
||||
'createdAt': createdAt != null ? createdAt : __ormNow__,
|
||||
|
@ -110,36 +109,64 @@ class BookQuery {
|
|||
return ctrl.stream;
|
||||
}
|
||||
|
||||
Stream<Book> delete(PostgreSQLConnection connection) async {}
|
||||
Stream<Book> delete(PostgreSQLConnection connection) {
|
||||
var buf = new StringBuffer('DELETE FROM "books"');
|
||||
var whereClause = where.toWhereClause();
|
||||
if (whereClause != null) {
|
||||
buf.write(' ' + whereClause);
|
||||
if (_and.isNotEmpty) {
|
||||
buf.write(' AND (' + _and.join(', ') + ')');
|
||||
}
|
||||
if (_or.isNotEmpty) {
|
||||
buf.write(' OR (' + _or.join(', ') + ')');
|
||||
}
|
||||
if (_not.isNotEmpty) {
|
||||
buf.write(' NOT (' + _not.join(', ') + ')');
|
||||
}
|
||||
}
|
||||
buf.write(' RETURNING "id", "name", "created_at", "updated_at";');
|
||||
StreamController<Book> ctrl = new StreamController<Book>();
|
||||
connection.query(buf.toString()).then((rows) {
|
||||
rows.map(parseRow).forEach(ctrl.add);
|
||||
ctrl.close();
|
||||
}).catchError(ctrl.addError);
|
||||
return ctrl.stream;
|
||||
}
|
||||
|
||||
static Future<Book> deleteOne(int id, PostgreSQLConnection connection) async {
|
||||
var __ormBeforeDelete__ = await BookQuery.getOne(id, connection);
|
||||
var result = await connection.execute('DELETE FROM "books" WHERE id = @id;',
|
||||
var result = await connection.query(
|
||||
'DELETE FROM "books" WHERE id = @id RETURNING "id", "name", "created_at", "updated_at";',
|
||||
substitutionValues: {'id': id});
|
||||
if (result != 1) {
|
||||
new StateError('DELETE query deleted ' +
|
||||
result +
|
||||
' row(s), instead of exactly 1 row.');
|
||||
}
|
||||
return __ormBeforeDelete__;
|
||||
return parseRow(result[0]);
|
||||
}
|
||||
|
||||
static Future<Book> insert(PostgreSQLConnection connection,
|
||||
{String name, DateTime createdAt, DateTime updatedAt}) async {
|
||||
var __ormNow__ = new DateTime.now();
|
||||
var nRows = await connection.execute(
|
||||
'INSERT INTO "books" ("name", "created_at", "updated_at") VALUES (@name, @createdAt, @updatedAt);',
|
||||
var result = await connection.query(
|
||||
'INSERT INTO "books" ("name", "created_at", "updated_at") VALUES (@name, @createdAt, @updatedAt) RETURNING "id", "name", "created_at", "updated_at";',
|
||||
substitutionValues: {
|
||||
'name': name,
|
||||
'createdAt': createdAt != null ? createdAt : __ormNow__,
|
||||
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
|
||||
});
|
||||
if (nRows < 1) {
|
||||
throw new StateError('Insertion into "books" table failed.');
|
||||
}
|
||||
var currVal = await connection.query(
|
||||
'SELECT * FROM "books" WHERE id = currval(pg_get_serial_sequence(\'books\', \'id\'));');
|
||||
return parseRow(currVal[0]);
|
||||
return parseRow(result[0]);
|
||||
}
|
||||
|
||||
static Future<Book> insertBook(PostgreSQLConnection connection, Book book) {
|
||||
return BookQuery.insert(connection,
|
||||
name: book.name, createdAt: book.createdAt, updatedAt: book.updatedAt);
|
||||
}
|
||||
|
||||
static Future<Book> updateBook(PostgreSQLConnection connection, Book book) {
|
||||
var query = new BookQuery();
|
||||
query.where.id.equals(int.parse(book.id));
|
||||
return query
|
||||
.update(connection,
|
||||
name: book.name,
|
||||
createdAt: book.createdAt,
|
||||
updatedAt: book.updatedAt)
|
||||
.first;
|
||||
}
|
||||
|
||||
static Stream<Book> getAll(PostgreSQLConnection connection) =>
|
|
@ -72,4 +72,8 @@ class Car extends _Car {
|
|||
};
|
||||
|
||||
static Car parse(Map map) => new Car.fromJson(map);
|
||||
|
||||
Car clone() {
|
||||
return new Car.fromJson(toJson());
|
||||
}
|
||||
}
|
|
@ -104,7 +104,7 @@ class CarQuery {
|
|||
var ctrl = new StreamController<Car>();
|
||||
connection.query(
|
||||
buf.toString() +
|
||||
' RETURNING ("id", "make", "description", "family_friendly", "recalled_at", "created_at", "updated_at");',
|
||||
' RETURNING "id", "make", "description", "family_friendly", "recalled_at", "created_at", "updated_at";',
|
||||
substitutionValues: {
|
||||
'make': make,
|
||||
'description': description,
|
||||
|
@ -120,10 +120,24 @@ class CarQuery {
|
|||
}
|
||||
|
||||
Stream<Car> delete(PostgreSQLConnection connection) {
|
||||
var query = 'DELETE FROM "cars" RETURNING ("id", "make", "description", "family_friendly", "recalled_at", "created_at", "updated_at");';
|
||||
var buf = new StringBuffer('DELETE FROM "cars"');
|
||||
var whereClause = where.toWhereClause();
|
||||
if (whereClause != null) {
|
||||
buf.write(' ' + whereClause);
|
||||
if (_and.isNotEmpty) {
|
||||
buf.write(' AND (' + _and.join(', ') + ')');
|
||||
}
|
||||
if (_or.isNotEmpty) {
|
||||
buf.write(' OR (' + _or.join(', ') + ')');
|
||||
}
|
||||
if (_not.isNotEmpty) {
|
||||
buf.write(' NOT (' + _not.join(', ') + ')');
|
||||
}
|
||||
}
|
||||
buf.write(
|
||||
' RETURNING "id", "make", "description", "family_friendly", "recalled_at", "created_at", "updated_at";');
|
||||
StreamController<Car> ctrl = new StreamController<Car>();
|
||||
connection.execute(query).then((rows) {
|
||||
print('Rows: $rows');
|
||||
connection.query(buf.toString()).then((rows) {
|
||||
rows.map(parseRow).forEach(ctrl.add);
|
||||
ctrl.close();
|
||||
}).catchError(ctrl.addError);
|
||||
|
@ -131,15 +145,10 @@ class CarQuery {
|
|||
}
|
||||
|
||||
static Future<Car> deleteOne(int id, PostgreSQLConnection connection) async {
|
||||
var __ormBeforeDelete__ = await CarQuery.getOne(id, connection);
|
||||
var result = await connection.execute('DELETE FROM "cars" WHERE id = @id;',
|
||||
var result = await connection.query(
|
||||
'DELETE FROM "cars" WHERE id = @id RETURNING "id", "make", "description", "family_friendly", "recalled_at", "created_at", "updated_at";',
|
||||
substitutionValues: {'id': id});
|
||||
if (result != 1) {
|
||||
new StateError('DELETE query deleted ' +
|
||||
result +
|
||||
' row(s), instead of exactly 1 row.');
|
||||
}
|
||||
return __ormBeforeDelete__;
|
||||
return parseRow(result[0]);
|
||||
}
|
||||
|
||||
static Future<Car> insert(PostgreSQLConnection connection,
|
||||
|
@ -150,8 +159,8 @@ class CarQuery {
|
|||
DateTime createdAt,
|
||||
DateTime updatedAt}) async {
|
||||
var __ormNow__ = new DateTime.now();
|
||||
var nRows = await connection.execute(
|
||||
'INSERT INTO "cars" ("make", "description", "family_friendly", "recalled_at", "created_at", "updated_at") VALUES (@make, @description, @familyFriendly, @recalledAt, @createdAt, @updatedAt);',
|
||||
var result = await connection.query(
|
||||
'INSERT INTO "cars" ("make", "description", "family_friendly", "recalled_at", "created_at", "updated_at") VALUES (@make, @description, @familyFriendly, @recalledAt, @createdAt, @updatedAt) RETURNING "id", "make", "description", "family_friendly", "recalled_at", "created_at", "updated_at";',
|
||||
substitutionValues: {
|
||||
'make': make,
|
||||
'description': description,
|
||||
|
@ -160,12 +169,31 @@ class CarQuery {
|
|||
'createdAt': createdAt != null ? createdAt : __ormNow__,
|
||||
'updatedAt': updatedAt != null ? updatedAt : __ormNow__
|
||||
});
|
||||
if (nRows < 1) {
|
||||
throw new StateError('Insertion into "cars" table failed.');
|
||||
}
|
||||
var currVal = await connection.query(
|
||||
'SELECT * FROM "cars" WHERE id = currval(pg_get_serial_sequence(\'cars\', \'id\'));');
|
||||
return parseRow(currVal[0]);
|
||||
return parseRow(result[0]);
|
||||
}
|
||||
|
||||
static Future<Car> insertCar(PostgreSQLConnection connection, Car car) {
|
||||
return CarQuery.insert(connection,
|
||||
make: car.make,
|
||||
description: car.description,
|
||||
familyFriendly: car.familyFriendly,
|
||||
recalledAt: car.recalledAt,
|
||||
createdAt: car.createdAt,
|
||||
updatedAt: car.updatedAt);
|
||||
}
|
||||
|
||||
static Future<Car> updateCar(PostgreSQLConnection connection, Car car) {
|
||||
var query = new CarQuery();
|
||||
query.where.id.equals(int.parse(car.id));
|
||||
return query
|
||||
.update(connection,
|
||||
make: car.make,
|
||||
description: car.description,
|
||||
familyFriendly: car.familyFriendly,
|
||||
recalledAt: car.recalledAt,
|
||||
createdAt: car.createdAt,
|
||||
updatedAt: car.updatedAt)
|
||||
.first;
|
||||
}
|
||||
|
||||
static Stream<Car> getAll(PostgreSQLConnection connection) =>
|
|
@ -1,9 +1,10 @@
|
|||
import 'package:build_runner/build_runner.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
import 'package:angel_orm/builder.dart';
|
||||
import 'package:angel_serialize/builder.dart';
|
||||
import 'package:angel_orm_generator/angel_orm_generator.dart';
|
||||
import 'package:angel_serialize_generator/angel_serialize_generator.dart';
|
||||
|
||||
final InputSet MODELS = new InputSet('angel_orm', const ['test/models/*.dart']);
|
||||
final InputSet MODELS =
|
||||
new InputSet('angel_orm_generator', const ['test/models/*.dart']);
|
||||
|
||||
final PhaseGroup PHASES = new PhaseGroup()
|
||||
..addPhase(new Phase()
|
|
@ -1,296 +0,0 @@
|
|||
import 'dart:async';
|
||||
import 'dart:mirrors';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:code_builder/dart/async.dart';
|
||||
import 'package:code_builder/dart/core.dart';
|
||||
import 'package:code_builder/code_builder.dart';
|
||||
import 'package:inflection/inflection.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:recase/recase.dart';
|
||||
import 'package:source_gen/src/annotation.dart';
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
import 'package:query_builder_sql/query_builder_sql.dart';
|
||||
import '../annotations.dart';
|
||||
|
||||
// TODO: whereXLessThan, greaterThan, etc.
|
||||
|
||||
final RegExp _leadingDot = new RegExp(r'^\.+');
|
||||
|
||||
const List<String> QUERY_DO_NOT_OVERRIDE = const ['when'];
|
||||
|
||||
typedef Iterable<ExpressionBuilder> SuperArgumentProvider(
|
||||
ORM model, ClassElement clazz);
|
||||
|
||||
class AngelQueryBuilderGenerator extends GeneratorForAnnotation<ORM> {
|
||||
ClassMirror _baseRepositoryClassMirror;
|
||||
final List<String> _imports = [
|
||||
'dart:async',
|
||||
'package:query_builder/query_builder.dart'
|
||||
];
|
||||
|
||||
final Map<String, TypeBuilder> _constructorParams = {};
|
||||
SuperArgumentProvider _superArgProvider;
|
||||
|
||||
AngelQueryBuilderGenerator(Type baseRepositoryQueryClass,
|
||||
{Iterable<String> additonalImports: const [],
|
||||
Map<String, TypeBuilder> constructorParams: const {},
|
||||
SuperArgumentProvider superArgProvider}) {
|
||||
_baseRepositoryClassMirror = reflectClass(baseRepositoryQueryClass);
|
||||
_imports.addAll(additonalImports ?? []);
|
||||
_constructorParams.addAll(constructorParams ?? {});
|
||||
_superArgProvider = superArgProvider ??
|
||||
(annotation, clazz) => [
|
||||
literal(annotation.tableName?.isNotEmpty == true
|
||||
? annotation.tableName
|
||||
: pluralize(new ReCase(clazz.name.substring(1)).snakeCase))
|
||||
];
|
||||
}
|
||||
|
||||
factory AngelQueryBuilderGenerator.postgresql() =>
|
||||
new AngelQueryBuilderGenerator(SqlRepositoryQuery, constructorParams: {
|
||||
'connection': new TypeBuilder('PostgreSQLConnection')
|
||||
}, additonalImports: [
|
||||
'package:postgres/postgres.dart',
|
||||
'package:query_builder_sql/query_builder_sql.dart'
|
||||
]);
|
||||
|
||||
@override
|
||||
Future<String> generateForAnnotatedElement(
|
||||
Element element, ORM annotation, BuildStep buildStep) async {
|
||||
if (element.kind != ElementKind.CLASS)
|
||||
throw 'Only classes may be annotated with @model.';
|
||||
var lib = generatePostgresLibrary(element, annotation, buildStep.inputId);
|
||||
return prettyToSource(lib.buildAst());
|
||||
}
|
||||
|
||||
LibraryBuilder generatePostgresLibrary(
|
||||
ClassElement clazz, ORM annotation, AssetId inputId) {
|
||||
if (!clazz.name.startsWith('_'))
|
||||
throw 'Classes annotated with @model must have names starting with an underscore.';
|
||||
var lib = new LibraryBuilder();
|
||||
lib.addDirectives(_imports.map((p) => new ImportBuilder(p)));
|
||||
lib.addDirective(new ImportBuilder(p.basename(inputId.path)));
|
||||
|
||||
// Find all aliases...
|
||||
Map<String, String> aliases = {};
|
||||
clazz.fields.forEach((field) {
|
||||
var aliasAnnotation = field.metadata
|
||||
.firstWhere((ann) => matchAnnotation(Alias, ann), orElse: () => null);
|
||||
if (aliasAnnotation != null) {
|
||||
var alias = instantiateAnnotation(aliasAnnotation) as Alias;
|
||||
aliases[field.name] = alias.name;
|
||||
}
|
||||
});
|
||||
|
||||
lib.addMember(generateRepositoryClass(clazz, aliases));
|
||||
lib.addMember(generateRepositoryQueryClass(clazz, annotation, aliases));
|
||||
return lib;
|
||||
}
|
||||
|
||||
ClassBuilder generateRepositoryClass(
|
||||
ClassElement clazz, Map<String, String> aliases) {
|
||||
var genClassName = clazz.name.substring(1) + 'Repository';
|
||||
var genQueryClassName = genClassName + 'Query';
|
||||
var genClass = new ClassBuilder(genClassName);
|
||||
var genQueryType = new TypeBuilder(genQueryClassName);
|
||||
|
||||
// Add `connection` field + constructor
|
||||
|
||||
var genConstructor = new ConstructorBuilder();
|
||||
_constructorParams.forEach((name, type) {
|
||||
genClass.addField(varFinal(name, type: type));
|
||||
genConstructor.addPositional(parameter(name), asField: true);
|
||||
});
|
||||
genClass.addConstructor(genConstructor);
|
||||
|
||||
// Add an all method
|
||||
genClass.addMethod(new MethodBuilder('all',
|
||||
returnType: new TypeBuilder(genQueryClassName),
|
||||
returns: new TypeBuilder(genQueryClassName)
|
||||
.newInstance([reference('connection')])));
|
||||
|
||||
// For each field, add a whereX() method...
|
||||
clazz.fields
|
||||
.map((field) => generateWhereFieldMethod(
|
||||
field, reference('all').call([]), genQueryType, aliases))
|
||||
.forEach(genClass.addMethod);
|
||||
return genClass;
|
||||
}
|
||||
|
||||
ClassBuilder generateRepositoryQueryClass(
|
||||
ClassElement clazz, ORM annotation, Map<String, String> aliases) {
|
||||
var modelClassName = clazz.name.substring(1);
|
||||
var genClassName = clazz.name.substring(1) + 'RepositoryQuery';
|
||||
var genClass = new ClassBuilder(genClassName,
|
||||
asExtends: new TypeBuilder(
|
||||
MirrorSystem.getName(_baseRepositoryClassMirror.simpleName),
|
||||
genericTypes: [new TypeBuilder(modelClassName)]));
|
||||
var genQueryType = new TypeBuilder(genClassName);
|
||||
|
||||
// Add `connection` field + constructor
|
||||
|
||||
var genConstructor = new ConstructorBuilder(
|
||||
invokeSuper: _superArgProvider(annotation, clazz));
|
||||
_constructorParams.forEach((name, type) {
|
||||
genClass.addField(varFinal(name, type: type));
|
||||
genConstructor.addPositional(parameter(name), asField: true);
|
||||
});
|
||||
genClass.addConstructor(genConstructor);
|
||||
|
||||
// For each field, add a whereX() method...
|
||||
clazz.fields
|
||||
.map((field) => generateWhereFieldMethod(
|
||||
field, explicitThis, genQueryType, aliases))
|
||||
.forEach(genClass.addMethod);
|
||||
|
||||
// Add orWhereX()
|
||||
clazz.fields
|
||||
.map((f) => generateOrWhereFieldMethod(genQueryType, f))
|
||||
.forEach(genClass.addMethod);
|
||||
|
||||
// Override any query methods
|
||||
_baseRepositoryClassMirror.instanceMembers.forEach((sym, method) {
|
||||
// Skip setters, etc.
|
||||
if (!method.isRegularMethod) return;
|
||||
|
||||
// Only if return type contains 'RepositoryQuery'
|
||||
var methodName = MirrorSystem.getName(sym);
|
||||
|
||||
if (QUERY_DO_NOT_OVERRIDE.contains(methodName)) return;
|
||||
|
||||
var returnTypeName = MirrorSystem.getName(method.returnType.simpleName);
|
||||
|
||||
if (returnTypeName.contains('RepositoryQuery')) {
|
||||
var overriddenMethod =
|
||||
new MethodBuilder(methodName, returnType: genQueryType);
|
||||
// Add @override
|
||||
overriddenMethod.addAnnotation(lib$core.override);
|
||||
|
||||
// Find all positional and named args
|
||||
List<String> args = [];
|
||||
List<String> named = [];
|
||||
|
||||
method.parameters.forEach((param) {
|
||||
var paramName = MirrorSystem.getName(param.simpleName);
|
||||
var typeName = MirrorSystem.getName(param.type.simpleName);
|
||||
var paramType = new TypeBuilder(typeName);
|
||||
var genParam = parameter(paramName, [paramType]);
|
||||
|
||||
if (!param.isNamed) {
|
||||
args.add(paramName);
|
||||
overriddenMethod.addPositional(
|
||||
param.isOptional ? genParam.asOptional() : genParam);
|
||||
} else {
|
||||
overriddenMethod.addNamed(genParam);
|
||||
named.add(paramName);
|
||||
}
|
||||
});
|
||||
|
||||
// Invoke super
|
||||
overriddenMethod.addStatement(reference('super')
|
||||
.invoke(methodName, args.map(reference),
|
||||
namedArguments: named.fold<Map<String, ExpressionBuilder>>(
|
||||
{}, (out, k) => out..[k] = reference(k)))
|
||||
.asReturn());
|
||||
|
||||
genClass.addMethod(overriddenMethod);
|
||||
}
|
||||
});
|
||||
|
||||
// Override toSql to put keys in desired order
|
||||
// TODO: Override toSql
|
||||
|
||||
// Add get()
|
||||
genClass.addMethod(generateGetMethod(clazz, modelClassName, aliases));
|
||||
|
||||
return genClass;
|
||||
}
|
||||
|
||||
MethodBuilder generateGetMethod(
|
||||
ClassElement clazz, String modelClassName, Map<String, String> aliases) {
|
||||
var meth = new MethodBuilder('get')..addAnnotation(lib$core.override);
|
||||
|
||||
// Map rows to model...
|
||||
var mapRowsToModel = new MethodBuilder.closure()
|
||||
..addPositional(parameter('rows'));
|
||||
|
||||
// First, figure out which rows we fetched...
|
||||
//
|
||||
// var requestedKeys = whereFields.keys.isNotEmpty ? whereFields.keys : [<all fields...>];
|
||||
|
||||
//var allModelFields = clazz.fields
|
||||
// .map((f) => aliases.containsKey(f.name) ? aliases[f.name] : f.name);
|
||||
//var whereFieldsKeys = reference('whereFields').property('keys');
|
||||
|
||||
// return new Stream<>.fromFuture(...)
|
||||
meth.addStatement(lib$async.Stream.newInstance([
|
||||
reference('connection')
|
||||
.invoke('query', [reference('toSql').call([])]).invoke(
|
||||
'then', [mapRowsToModel])
|
||||
], constructor: 'fromFuture').asReturn());
|
||||
|
||||
return meth;
|
||||
}
|
||||
|
||||
MethodBuilder generateWhereFieldMethod(
|
||||
FieldElement field,
|
||||
ExpressionBuilder baseQuery,
|
||||
TypeBuilder returnType,
|
||||
Map<String, String> aliases) {
|
||||
var rc = new ReCase(field.name);
|
||||
var whereMethod =
|
||||
new MethodBuilder('where${rc.pascalCase}', returnType: returnType);
|
||||
var columnName =
|
||||
aliases.containsKey(field.name) ? aliases[field.name] : field.name;
|
||||
whereMethod.addPositional(
|
||||
parameter(field.name, [new TypeBuilder(field.type.displayName)]));
|
||||
|
||||
if (field.type.name == 'DateTime') {
|
||||
// Add named `{time: true}`
|
||||
whereMethod.addNamed(
|
||||
parameter('time', [lib$core.bool]).asOptional(literal(true)));
|
||||
// return all().whereDate('x', x, time: time != false);
|
||||
// return all().where('x', x);
|
||||
whereMethod.addStatement(baseQuery.invoke('whereDate', [
|
||||
literal(columnName),
|
||||
reference(field.name)
|
||||
], namedArguments: {
|
||||
'time': reference('time').notEquals(literal(false))
|
||||
}).asReturn());
|
||||
} else {
|
||||
// return all().where('x', x);
|
||||
whereMethod.addStatement(baseQuery.invoke(
|
||||
'where', [literal(columnName), reference(field.name)]).asReturn());
|
||||
}
|
||||
|
||||
return whereMethod;
|
||||
}
|
||||
|
||||
MethodBuilder generateOrWhereFieldMethod(
|
||||
TypeBuilder genQueryClassType, FieldElement field) {
|
||||
var rc = new ReCase(field.name);
|
||||
var orWhereMethod = new MethodBuilder('orWhere' + rc.pascalCase,
|
||||
returnType: genQueryClassType);
|
||||
orWhereMethod.addPositional(
|
||||
parameter(field.name, [new TypeBuilder(field.type.displayName)]));
|
||||
|
||||
if (field.type.name == 'DateTime') {
|
||||
orWhereMethod.addNamed(parameter('time', [lib$core.bool]));
|
||||
orWhereMethod.addStatement(reference('or').call([
|
||||
reference('where' + rc.pascalCase).call([
|
||||
reference(field.name)
|
||||
], namedArguments: {
|
||||
'time': reference('time').notEquals(literal(false))
|
||||
})
|
||||
]).asReturn());
|
||||
} else {
|
||||
orWhereMethod.addStatement(reference('or').call([
|
||||
reference('where' + rc.pascalCase).call([reference(field.name)])
|
||||
]).asReturn());
|
||||
}
|
||||
|
||||
return orWhereMethod;
|
||||
}
|
||||
}
|
24
pubspec.yaml
24
pubspec.yaml
|
@ -1,24 +0,0 @@
|
|||
name: angel_orm
|
||||
version: 0.0.0
|
||||
description: Source-generated ORM for use with the Angel framework.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/angel_mongo
|
||||
dependencies:
|
||||
angel_framework: ">=1.0.0-dev < 2.0.0"
|
||||
code_builder: ^1.0.0
|
||||
id: ^1.0.0
|
||||
inflection: ^0.4.1
|
||||
intl: ^0.15.1
|
||||
pool: ^1.0.0
|
||||
postgres: ">=0.9.5 <1.0.0"
|
||||
query_builder_sql: ^1.0.0-alpha
|
||||
recase: ^1.0.0
|
||||
source_gen: ^0.6.0
|
||||
dev_dependencies:
|
||||
angel_diagnostics: ">=1.0.0 <2.0.0"
|
||||
angel_serialize:
|
||||
path: ../serialize
|
||||
angel_test: ">=1.0.0 <2.0.0"
|
||||
build_runner: ^0.3.0
|
||||
http: ">= 0.11.3 < 0.12.0"
|
||||
test: ">= 0.12.13 < 0.13.0"
|
5
tool/.travis.sh
Normal file
5
tool/.travis.sh
Normal file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
cd angel_orm_generator
|
||||
pub get
|
||||
dart tool/build.dart
|
||||
POSTGRES_USERNAME="angel_orm" POSTGRES_PASSWORD="angel_orm" pub run test
|
Loading…
Reference in a new issue