├── .project ├── .properties ├── Depot-UI ├── package.st ├── ManifestDepotUI.class.st ├── DpAppBar.class.st ├── DpListing.class.st ├── DpComponent.class.st ├── DpFiltersForm.class.st ├── DpApp.class.st ├── DpEntityListing.class.st └── DpStyles.class.st ├── Depot-Core ├── package.st ├── DpMain.class.st ├── DpFirstRunState.class.st ├── DpEntityCreateState.class.st ├── DpEntityListingState.class.st ├── DpRootState.class.st ├── CompiledMethod.extension.st ├── DpRouteState.class.st ├── DpRouter.class.st ├── DpEntityViewState.class.st ├── DpConfig.class.st ├── DpEntityRouteState.class.st ├── DpPage.class.st ├── DpEntity.class.st └── DpFirstRun.class.st ├── Depot-Entity ├── package.st ├── DpTimeField.class.st ├── DpFloatField.class.st ├── DpIntegerField.class.st ├── DpDateAndTimeField.class.st ├── DpScaledDecimalField.class.st ├── DpNumberField.class.st ├── DpDateField.class.st ├── ManifestDepotEntity.class.st ├── DpChildMapping.class.st ├── DpStringField.class.st ├── DpField.class.st └── DpEntityBuilder.class.st ├── Depot-Styles ├── package.st ├── ManifestDepotStyles.class.st ├── DpDevStyleVisitor.class.st └── DpDevStyleListener.class.st ├── Depot-User ├── package.st └── ManifestDepotUser.class.st ├── Depot-Example ├── package.st ├── ManifestDepotExample.class.st ├── Project.class.st └── Issue.class.st ├── README.md └── LICENSE /.project: -------------------------------------------------------------------------------- 1 | { 2 | 'srcDirectory' : '' 3 | } -------------------------------------------------------------------------------- /.properties: -------------------------------------------------------------------------------- 1 | { 2 | #format : #tonel 3 | } -------------------------------------------------------------------------------- /Depot-UI/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Depot-UI' } 2 | -------------------------------------------------------------------------------- /Depot-Core/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Depot-Core' } 2 | -------------------------------------------------------------------------------- /Depot-Entity/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Depot-Entity' } 2 | -------------------------------------------------------------------------------- /Depot-Styles/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Depot-Styles' } 2 | -------------------------------------------------------------------------------- /Depot-User/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Depot-User' } 2 | -------------------------------------------------------------------------------- /Depot-Example/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Depot-Example' } 2 | -------------------------------------------------------------------------------- /Depot-Entity/DpTimeField.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpTimeField, 3 | #superclass : #DpField, 4 | #category : #'Depot-Entity' 5 | } 6 | -------------------------------------------------------------------------------- /Depot-Entity/DpFloatField.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpFloatField, 3 | #superclass : #DpNumberField, 4 | #category : #'Depot-Entity' 5 | } 6 | -------------------------------------------------------------------------------- /Depot-Entity/DpIntegerField.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpIntegerField, 3 | #superclass : #DpNumberField, 4 | #category : #'Depot-Entity' 5 | } 6 | -------------------------------------------------------------------------------- /Depot-Entity/DpDateAndTimeField.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpDateAndTimeField, 3 | #superclass : #DpField, 4 | #category : #'Depot-Entity' 5 | } 6 | -------------------------------------------------------------------------------- /Depot-Entity/DpScaledDecimalField.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpScaledDecimalField, 3 | #superclass : #DpNumberField, 4 | #category : #'Depot-Entity' 5 | } 6 | -------------------------------------------------------------------------------- /Depot-Entity/DpNumberField.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpNumberField, 3 | #superclass : #DpField, 4 | #instVars : [ 5 | 'minValue', 6 | 'maxValue' 7 | ], 8 | #category : #'Depot-Entity' 9 | } 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Depot 2 | 3 | Depot is your one stop web app for organizing data. 4 | 5 | Combines Pharo Smalltalk, LiveWeb and XTDB to quickly build web-based information systems. 6 | 7 | **Highly experimental!** 8 | -------------------------------------------------------------------------------- /Depot-Core/DpMain.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpMain, 3 | #superclass : #LWComponent, 4 | #category : #'Depot-Core' 5 | } 6 | 7 | { #category : #rendering } 8 | DpMain >> renderOn: h [ 9 | h div: 'Welcome to Depot!' 10 | ] 11 | -------------------------------------------------------------------------------- /Depot-User/ManifestDepotUser.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This package contains all user code generated through the Depot UI tools. 3 | " 4 | Class { 5 | #name : #ManifestDepotUser, 6 | #superclass : #PackageManifest, 7 | #category : #'Depot-User-Manifest' 8 | } 9 | -------------------------------------------------------------------------------- /Depot-Core/DpFirstRunState.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpFirstRunState, 3 | #superclass : #DpRouteState, 4 | #category : #'Depot-Core' 5 | } 6 | 7 | { #category : #'as yet unclassified' } 8 | DpFirstRunState >> bodyComponent [ 9 | ^ DpFirstRun new 10 | ] 11 | -------------------------------------------------------------------------------- /Depot-Entity/DpDateField.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpDateField, 3 | #superclass : #DpField, 4 | #category : #'Depot-Entity' 5 | } 6 | 7 | { #category : #converting } 8 | DpDateField >> asLWEditFormComponent: form accessor: accessor [ 9 | ^ LWDateEditor new form: form 10 | ] 11 | -------------------------------------------------------------------------------- /Depot-Core/DpEntityCreateState.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am the route for creating a new instance of an entity. 3 | 4 | After saving, I send user to the DpEntityListingState. 5 | " 6 | Class { 7 | #name : #DpEntityCreateState, 8 | #superclass : #DpEntityRouteState, 9 | #category : #'Depot-Core' 10 | } 11 | -------------------------------------------------------------------------------- /Depot-Core/DpEntityListingState.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpEntityListingState, 3 | #superclass : #DpEntityRouteState, 4 | #category : #'Depot-Core' 5 | } 6 | 7 | { #category : #'as yet unclassified' } 8 | DpEntityListingState >> bodyComponent [ 9 | ^ DpEntityListing new entityClass: self entityClass 10 | ] 11 | -------------------------------------------------------------------------------- /Depot-Example/ManifestDepotExample.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This package contains a simple example of Depot application. 3 | It contains a simple project tracker with Project, Issue and IssueLink 4 | " 5 | Class { 6 | #name : #ManifestDepotExample, 7 | #superclass : #PackageManifest, 8 | #category : #'Depot-Example-Manifest' 9 | } 10 | -------------------------------------------------------------------------------- /Depot-Core/DpRootState.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am the root state that does not show any specific entity. 3 | 4 | " 5 | Class { 6 | #name : #DpRootState, 7 | #superclass : #DpRouteState, 8 | #category : #'Depot-Core' 9 | } 10 | 11 | { #category : #'as yet unclassified' } 12 | DpRootState >> bodyComponent [ 13 | ^ DpMain new 14 | ] 15 | -------------------------------------------------------------------------------- /Depot-Styles/ManifestDepotStyles.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Helper code to use TailwindCSS and daisyUI styling for Depot. 3 | In development mode, writes used CSS classes by listening to MethodAnnouncements. 4 | " 5 | Class { 6 | #name : #ManifestDepotStyles, 7 | #superclass : #PackageManifest, 8 | #category : #'Depot-Styles-Manifest' 9 | } 10 | -------------------------------------------------------------------------------- /Depot-UI/ManifestDepotUI.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I contain the Depot UI components: the main body component, the app bar, and so on. 3 | All top level components that comprise the web page are here. 4 | 5 | 6 | " 7 | Class { 8 | #name : #ManifestDepotUI, 9 | #superclass : #PackageManifest, 10 | #category : #'Depot-UI-Manifest' 11 | } 12 | -------------------------------------------------------------------------------- /Depot-Entity/ManifestDepotEntity.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This package contains the field definitions to map fields in 3 | entity classes to XTDB attributes. 4 | 5 | Depot enables more detailed type and constraints than simply the 6 | raw XTDB client mapping. 7 | " 8 | Class { 9 | #name : #ManifestDepotEntity, 10 | #superclass : #PackageManifest, 11 | #category : #'Depot-Entity-Manifest' 12 | } 13 | -------------------------------------------------------------------------------- /Depot-Core/CompiledMethod.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #CompiledMethod } 2 | 3 | { #category : #'*Depot-Core' } 4 | CompiledMethod >> dpViewRefersTo [ 5 | "Return all the instance variables this view method refers to" 6 | (self hasPragmaNamed: #dpView:) ifFalse: [ self error: 'Not a view method!' ]. 7 | ^ (self instanceVariableReadNodes collect: [ :n | n variable name ] as: Set) asOrderedCollection 8 | ] 9 | -------------------------------------------------------------------------------- /Depot-Core/DpRouteState.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am the base class for all route states in Depot. 3 | " 4 | Class { 5 | #name : #DpRouteState, 6 | #superclass : #LWPushStateBase, 7 | #category : #'Depot-Core' 8 | } 9 | 10 | { #category : #'as yet unclassified' } 11 | DpRouteState >> bodyComponent [ 12 | ^ LWBlockContainer new block: [ :h | h div: 'Please implement component for route: ', (STON toString: self) ] 13 | ] 14 | -------------------------------------------------------------------------------- /Depot-Core/DpRouter.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpRouter, 3 | #superclass : #LWPushStateRouter, 4 | #category : #'Depot-Core' 5 | } 6 | 7 | { #category : #initialization } 8 | DpRouter >> initialize [ 9 | super initialize. 10 | self 11 | route: '/' as: DpRootState; 12 | route: '/:entity' as: DpEntityListingState; 13 | route: '/:entity/create' as: DpEntityCreateState; 14 | route: '/:entity/:id' as: DpEntityViewState. 15 | ] 16 | -------------------------------------------------------------------------------- /Depot-Core/DpEntityViewState.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am the route that is used to view a single entity instance. 3 | " 4 | Class { 5 | #name : #DpEntityViewState, 6 | #superclass : #DpEntityRouteState, 7 | #instVars : [ 8 | 'id' 9 | ], 10 | #category : #'Depot-Core' 11 | } 12 | 13 | { #category : #accessing } 14 | DpEntityViewState >> id [ 15 | 16 | ^ id 17 | ] 18 | 19 | { #category : #accessing } 20 | DpEntityViewState >> id: anObject [ 21 | 22 | id := anObject 23 | ] 24 | -------------------------------------------------------------------------------- /Depot-Entity/DpChildMapping.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpChildMapping, 3 | #superclass : #XtChildMapping, 4 | #category : #'Depot-Entity' 5 | } 6 | 7 | { #category : #converting } 8 | DpChildMapping >> asDpMappingSource [ 9 | ^String streamContents:[:out | out 10 | << self class name; << ' of: '; << '#'''; << attribute; << ''' -> #'; << getter; 11 | << ' to: '; << entityClass name 12 | ] 13 | ] 14 | 15 | { #category : #accessing } 16 | DpChildMapping >> searchable [ 17 | ^ false 18 | 19 | ] 20 | -------------------------------------------------------------------------------- /Depot-UI/DpAppBar.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpAppBar, 3 | #superclass : #DpComponent, 4 | #category : #'Depot-UI' 5 | } 6 | 7 | { #category : #rendering } 8 | DpAppBar >> renderOn: h [ 9 | h nav: { self style appbar } with: [ 10 | h span: { self style appbarHeader } with: 'Depot'; 11 | div: { self style appbarMenu } with: [ 12 | DpEntity subclassesDo: [ :cls | 13 | h a: ({ self style appbarLink. }, (self entityLinkAttrs: cls)) 14 | with: cls name 15 | ] 16 | ] 17 | 18 | ] 19 | ] 20 | -------------------------------------------------------------------------------- /Depot-Styles/DpDevStyleVisitor.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpDevStyleVisitor, 3 | #superclass : #RBProgramNodeVisitor, 4 | #instVars : [ 5 | 'strings' 6 | ], 7 | #category : #'Depot-Styles' 8 | } 9 | 10 | { #category : #initialization } 11 | DpDevStyleVisitor >> initialize [ 12 | strings := Set new. 13 | ] 14 | 15 | { #category : #accessing } 16 | DpDevStyleVisitor >> strings [ 17 | ^ strings 18 | ] 19 | 20 | { #category : #visiting } 21 | DpDevStyleVisitor >> visitLiteralNode: aNode [ 22 | (aNode value isKindOf: String) ifTrue: [ 23 | strings add: aNode value. 24 | ]. 25 | 26 | ] 27 | -------------------------------------------------------------------------------- /Depot-Entity/DpStringField.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I model a field whose value is a string. 3 | I can also have a maximum length and regular expression pattern that 4 | the string must satisfy. 5 | " 6 | Class { 7 | #name : #DpStringField, 8 | #superclass : #DpField, 9 | #instVars : [ 10 | 'maxLength', 11 | 'pattern' 12 | ], 13 | #category : #'Depot-Entity' 14 | } 15 | 16 | { #category : #converting } 17 | DpStringField >> asLWEditFormComponent: form accessor: accessor [ 18 | ^ LWStringEditor new form: form 19 | ] 20 | 21 | { #category : #'as yet unclassified' } 22 | DpStringField >> search: e value: val [ 23 | ^ e textSearch: val 24 | ] 25 | -------------------------------------------------------------------------------- /Depot-Core/DpConfig.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpConfig, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'url' 6 | ], 7 | #classVars : [ 8 | 'Config' 9 | ], 10 | #category : #'Depot-Core' 11 | } 12 | 13 | { #category : #'instance creation' } 14 | DpConfig class >> uniqueInstance [ 15 | ^ Config 16 | 17 | ] 18 | 19 | { #category : #'instance creation' } 20 | DpConfig class >> uniqueInstance: aDpConfig [ 21 | Config := aDpConfig 22 | 23 | ] 24 | 25 | { #category : #accessing } 26 | DpConfig >> url [ 27 | 28 | ^ url 29 | ] 30 | 31 | { #category : #accessing } 32 | DpConfig >> url: anObject [ 33 | 34 | url := anObject 35 | ] 36 | -------------------------------------------------------------------------------- /Depot-UI/DpListing.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpListing, 3 | #superclass : #DpComponent, 4 | #instVars : [ 5 | 'entityClass', 6 | 'items', 7 | 'viewName' 8 | ], 9 | #category : #'Depot-UI' 10 | } 11 | 12 | { #category : #initialization } 13 | DpListing >> initialize [ 14 | super initialize. 15 | viewName := 'summary' 16 | 17 | ] 18 | 19 | { #category : #accessing } 20 | DpListing >> items: aCollection [ 21 | items := aCollection. 22 | entityClass := items first class. 23 | ] 24 | 25 | { #category : #rendering } 26 | DpListing >> renderOn: h [ 27 | h div: { #class->'depot-listing' } with: [ 28 | items do: [ :item | item render: h view: viewName ] 29 | ] 30 | ] 31 | -------------------------------------------------------------------------------- /Depot-UI/DpComponent.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am the base class for all Depot UI components. 3 | " 4 | Class { 5 | #name : #DpComponent, 6 | #superclass : #LWStyledComponent, 7 | #category : #'Depot-UI' 8 | } 9 | 10 | { #category : #'as yet unclassified' } 11 | DpComponent >> entityLinkAttrs: anEntityClass [ 12 | | router route | 13 | router := self router. 14 | route := router matchState: (DpEntityListingState new entity: anEntityClass name). 15 | ^ { #href -> ('/',anEntityClass name) . 16 | #onclick -> (ctx goJS: route) } 17 | ] 18 | 19 | { #category : #'as yet unclassified' } 20 | DpComponent >> router [ 21 | ^ DpRouter new 22 | 23 | ] 24 | 25 | { #category : #'as yet unclassified' } 26 | DpComponent >> xt [ 27 | "Get an XTDB client instance." 28 | ^ XtClient new url: DpConfig uniqueInstance url 29 | ] 30 | -------------------------------------------------------------------------------- /Depot-UI/DpFiltersForm.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a form for editing filter values to search entities by. 3 | " 4 | Class { 5 | #name : #DpFiltersForm, 6 | #superclass : #LWEditForm, 7 | #instVars : [ 8 | 'onUpdate' 9 | ], 10 | #category : #'Depot-UI' 11 | } 12 | 13 | { #category : #accessing } 14 | DpFiltersForm >> entityClass: aClass [ 15 | entityClass := aClass 16 | ] 17 | 18 | { #category : #'as yet unclassified' } 19 | DpFiltersForm >> fieldDefinitions [ 20 | ^ entityClass dpMapping fieldMappings select: #searchable 21 | ] 22 | 23 | { #category : #accessing } 24 | DpFiltersForm >> onUpdate: anObject [ 25 | 26 | onUpdate := anObject 27 | ] 28 | 29 | { #category : #rendering } 30 | DpFiltersForm >> renderOn: h [ 31 | h div: [ 32 | super renderOn: h. 33 | h button: { #onclick -> [ onUpdate value: self entity ] } with: 'Fetch' 34 | ] 35 | ] 36 | -------------------------------------------------------------------------------- /Depot-Core/DpEntityRouteState.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am the base route state for all routes in the context of an 3 | entity type. 4 | 5 | I have the entity instance variable that names the entity class. 6 | " 7 | Class { 8 | #name : #DpEntityRouteState, 9 | #superclass : #DpRouteState, 10 | #instVars : [ 11 | 'entity' 12 | ], 13 | #category : #'Depot-Core' 14 | } 15 | 16 | { #category : #accessing } 17 | DpEntityRouteState >> entity [ 18 | 19 | ^ entity 20 | ] 21 | 22 | { #category : #accessing } 23 | DpEntityRouteState >> entity: anObject [ 24 | 25 | entity := anObject 26 | ] 27 | 28 | { #category : #accessing } 29 | DpEntityRouteState >> entityClass [ 30 | | cls | 31 | cls := Smalltalk classNamed: entity. 32 | cls ifNil: [ Error signal: 'No such class' ]. 33 | (cls superclass = DpEntity) ifFalse: [ Error signal: 'Invalid class' ]. 34 | ^ cls 35 | ] 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Tatu Tarvainen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Depot-UI/DpApp.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am the main LiveWeb component for a Depot application. 3 | 4 | I contain the application bar and all common things that are shared 5 | between different pages. 6 | 7 | I have a content that changes based on user navigation. 8 | " 9 | Class { 10 | #name : #DpApp, 11 | #superclass : #DpComponent, 12 | #instVars : [ 13 | 'appbar', 14 | 'content' 15 | ], 16 | #category : #'Depot-UI' 17 | } 18 | 19 | { #category : #accessing } 20 | DpApp >> children [ 21 | ^ ReadStream on: { appbar . content } 22 | 23 | ] 24 | 25 | { #category : #accessing } 26 | DpApp >> content [ 27 | ^ content child 28 | ] 29 | 30 | { #category : #initialization } 31 | DpApp >> initialize [ 32 | super initialize. 33 | appbar := DpAppBar new. 34 | content := LWSingleContainer new. 35 | ] 36 | 37 | { #category : #rendering } 38 | DpApp >> renderOn: h [ 39 | "We can render appbar and content div separately, this 40 | app root component is never rerendered completely during page lifecycle." 41 | appbar render: h. 42 | content render: h 43 | ] 44 | 45 | { #category : #accessing } 46 | DpApp >> setContent: aComponent [ 47 | content child: aComponent 48 | ] 49 | -------------------------------------------------------------------------------- /Depot-Example/Project.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #Project, 3 | #superclass : #DpEntity, 4 | #instVars : [ 5 | 'name', 6 | 'description', 7 | 'epics' 8 | ], 9 | #category : #'Depot-Example' 10 | } 11 | 12 | { #category : #'as yet unclassified' } 13 | Project class >> dpMapping [ 14 | ^ XtEntityMapping withAll: { 15 | (DpStringField of: #':project/name' -> #name) beSearchable. 16 | DpStringField of: #':project/description' -> #description 17 | } 18 | ] 19 | 20 | { #category : #accessing } 21 | Project >> description [ 22 | 23 | ^ description 24 | ] 25 | 26 | { #category : #accessing } 27 | Project >> description: anObject [ 28 | 29 | description := anObject 30 | ] 31 | 32 | { #category : #accessing } 33 | Project >> epics [ 34 | 35 | ^ epics 36 | ] 37 | 38 | { #category : #accessing } 39 | Project >> epics: anObject [ 40 | 41 | epics := anObject 42 | ] 43 | 44 | { #category : #accessing } 45 | Project >> name [ 46 | 47 | ^ name 48 | ] 49 | 50 | { #category : #accessing } 51 | Project >> name: anObject [ 52 | 53 | name := anObject 54 | ] 55 | 56 | { #category : #'as yet unclassified' } 57 | Project >> summaryView: h [ 58 | 59 | h div: [ 60 | h div: [ h b: 'Name:'; span: name ]; 61 | div: [ h b: 'Description: '; span: description ] 62 | ] 63 | 64 | ] 65 | -------------------------------------------------------------------------------- /Depot-Entity/DpField.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpField, 3 | #superclass : #XtFieldMapping, 4 | #instVars : [ 5 | 'searchable' 6 | ], 7 | #category : #'Depot-Entity' 8 | } 9 | 10 | { #category : #converting } 11 | DpField class >> asDpMappingSource [ 12 | "Return this as source code for building a mapping." 13 | 14 | ] 15 | 16 | { #category : #'instance creation' } 17 | DpField class >> of: attributeGetterAssociation [ 18 | ^ self new configureMapping: attributeGetterAssociation 19 | ] 20 | 21 | { #category : #converting } 22 | DpField >> asDpMappingSource [ 23 | "Return this as source code for building a mapping." 24 | ^ String streamContents: [ :out | out 25 | << '('; << self class name; << ' of: #'''; << attribute; << ''' -> #'; << getter; << ')'. 26 | searchable ifTrue: [ out << ' beSearchable' ] 27 | ] 28 | ] 29 | 30 | { #category : #'as yet unclassified' } 31 | DpField >> beSearchable [ 32 | searchable := true 33 | ] 34 | 35 | { #category : #initialization } 36 | DpField >> initialize [ 37 | super initialize. 38 | searchable := false 39 | ] 40 | 41 | { #category : #accessing } 42 | DpField >> key [ 43 | ^ getter 44 | ] 45 | 46 | { #category : #'as yet unclassified' } 47 | DpField >> search: entityBuilder value: filterValue [ 48 | self subclassResponsibility 49 | ] 50 | 51 | { #category : #accessing } 52 | DpField >> searchable [ 53 | 54 | ^ searchable 55 | ] 56 | -------------------------------------------------------------------------------- /Depot-Styles/DpDevStyleListener.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpDevStyleListener, 3 | #superclass : #Object, 4 | #classVars : [ 5 | 'allStrings', 6 | 'jsonFile', 7 | 'lastAnnouncement' 8 | ], 9 | #category : #'Depot-Styles' 10 | } 11 | 12 | { #category : #'as yet unclassified' } 13 | DpDevStyleListener class >> allStrings [ 14 | ^ allStrings 15 | ] 16 | 17 | { #category : #'as yet unclassified' } 18 | DpDevStyleListener class >> jsonFile: aFile [ 19 | jsonFile := aFile 20 | ] 21 | 22 | { #category : #accessing } 23 | DpDevStyleListener class >> lastAnnouncement [ 24 | ^ lastAnnouncement 25 | 26 | ] 27 | 28 | { #category : #'as yet unclassified' } 29 | DpDevStyleListener class >> listen [ 30 | SystemAnnouncer uniqueInstance 31 | when: MethodAnnouncement 32 | send: #methodAnnouncement: 33 | to: self 34 | 35 | ] 36 | 37 | { #category : #'as yet unclassified' } 38 | DpDevStyleListener class >> methodAnnouncement: anAnnouncement [ 39 | | visitor | 40 | lastAnnouncement := anAnnouncement. 41 | visitor := DpDevStyleVisitor new. 42 | anAnnouncement method ast acceptVisitor: visitor. 43 | allStrings ifNil: [ allStrings := Set new ]. 44 | allStrings addAll: visitor strings. 45 | self writeStrings. 46 | ] 47 | 48 | { #category : #writing } 49 | DpDevStyleListener class >> writeStrings [ 50 | jsonFile ifNil: [ ^ nil ]. 51 | jsonFile writeStreamDo: [ :out | out << (STON toJsonString: allStrings asArray) ] 52 | ] 53 | -------------------------------------------------------------------------------- /Depot-Core/DpPage.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpPage, 3 | #superclass : #LWStyledPage, 4 | #category : #'Depot-Core' 5 | } 6 | 7 | { #category : #public } 8 | DpPage class >> handleRequest: aRequest [ 9 | Transcript show: aRequest uri;cr. 10 | aRequest uri path = '__liveweb' ifTrue: [ ^ LWPageConnection value: aRequest ]. 11 | ^ self new value: aRequest. 12 | ] 13 | 14 | { #category : #accessing } 15 | DpPage class >> value: req [ 16 | ^ self new value: req 17 | ] 18 | 19 | { #category : #accessing } 20 | DpPage >> body: _args [ 21 | | state | 22 | state := self initialPushState. 23 | ^ DpApp new setContent: state bodyComponent 24 | ] 25 | 26 | { #category : #'push state handling' } 27 | DpPage >> initialPushState [ 28 | DpConfig uniqueInstance ifNil: [ ^ DpFirstRunState new ]. 29 | ^ DpRouter new match: args uri. 30 | ] 31 | 32 | { #category : #'as yet unclassified' } 33 | DpPage >> prepareCtx [ 34 | super prepareCtx. 35 | ctx at: #formMessages put: LWFormMessages new. 36 | 37 | ] 38 | 39 | { #category : #'push state handling' } 40 | DpPage >> pushStateChanged: newPushState [ 41 | | state | 42 | state := DpRouter new fromPushStateJSON: newPushState. 43 | body setContent: state bodyComponent 44 | ] 45 | 46 | { #category : #'as yet unclassified' } 47 | DpPage >> stylesheetProvider [ 48 | ^ LWStylesheetProvider provider: DpStyles 49 | 50 | ] 51 | 52 | { #category : #'push state handling' } 53 | DpPage >> usePushState [ 54 | ^ false 55 | ] 56 | -------------------------------------------------------------------------------- /Depot-Core/DpEntity.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I represent the base class for all entities stored to XTDB. 3 | Subclasses should merge their xtMapping with the base mapping. 4 | 5 | An entity can contain different HTML rendering views of the entity. 6 | These are methods that take a LiveWeb HTMLRenderer as argument 7 | and contain a dpView: pragma with the view name (eg. 'summary' or 'card'). 8 | 9 | " 10 | Class { 11 | #name : #DpEntity, 12 | #superclass : #Object, 13 | #instVars : [ 14 | '_xtId' 15 | ], 16 | #category : #'Depot-Core' 17 | } 18 | 19 | { #category : #accessing } 20 | DpEntity class >> builder [ 21 | ^ DpEntityBuilder new 22 | ] 23 | 24 | { #category : #'as yet unclassified' } 25 | DpEntity class >> dpMapping [ 26 | "Return the Depot specialized XTDB mapping for this class, subclasses must implement." 27 | self subclassResponsibility 28 | ] 29 | 30 | { #category : #'as yet unclassified' } 31 | DpEntity class >> viewNamed: aString [ 32 | ^ self views detect: [ :m | ((m pragmaAt: #dpView:) argumentAt: 1) = aString ] 33 | ] 34 | 35 | { #category : #'as yet unclassified' } 36 | DpEntity class >> views [ 37 | ^ self methods select: [ :m | m hasPragmaNamed: #dpView: ] 38 | ] 39 | 40 | { #category : #accessing } 41 | DpEntity class >> xtMapping [ 42 | ^ (XtEntityMapping withAll: { 43 | #':xt/id' -> #_xtId 44 | }), (self dpMapping) 45 | ] 46 | 47 | { #category : #accessing } 48 | DpEntity >> _xtId [ 49 | 50 | ^ _xtId 51 | ] 52 | 53 | { #category : #accessing } 54 | DpEntity >> _xtId: anObject [ 55 | 56 | _xtId := anObject 57 | ] 58 | 59 | { #category : #rendering } 60 | DpEntity >> render: h view: viewName [ 61 | self perform: (self class viewNamed: viewName) selector with: h 62 | ] 63 | -------------------------------------------------------------------------------- /Depot-UI/DpEntityListing.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpEntityListing, 3 | #superclass : #DpComponent, 4 | #instVars : [ 5 | 'filtersComponent', 6 | 'listingComponent', 7 | 'entityClass' 8 | ], 9 | #category : #'Depot-UI' 10 | } 11 | 12 | { #category : #initialization } 13 | DpEntityListing >> children [ 14 | ^ ReadStream on: { filtersComponent . listingComponent } 15 | ] 16 | 17 | { #category : #accessing } 18 | DpEntityListing >> entityClass: aClass [ 19 | entityClass := aClass. 20 | filtersComponent entityClass: entityClass 21 | ] 22 | 23 | { #category : #initialization } 24 | DpEntityListing >> initialize [ 25 | super initialize. 26 | filtersComponent := DpFiltersForm new onUpdate: [ :newFilters | self performQuery: newFilters ]. 27 | listingComponent := LWSingleContainer new. 28 | 29 | ] 30 | 31 | { #category : #'as yet unclassified' } 32 | DpEntityListing >> performQuery: newFilters [ 33 | listingComponent child: (LWBlockContainer new block: [ :h | h div: 'Loading...' ]). 34 | [ | results | 35 | results := (self xt q: entityClass where: [ :e | 36 | | where | 37 | where := nil. 38 | (entityClass dpMapping fieldMappings select: #searchable) do: [ :sm | 39 | (newFilters perform: sm getter) ifNotNil: [ :val | 40 | | clause | 41 | clause := sm search: (e perform: sm getter) value: val. 42 | where := where ifNil: [ clause ] ifNotNil: [ where & clause ] 43 | ]]. 44 | where 45 | ]) . 46 | results isEmpty 47 | ifTrue: [ listingComponent child: (LWBlockContainer new block: [:h | h div: 'No results' ]) ] 48 | ifFalse: [ listingComponent child: (DpListing new items: results) ] 49 | ] fork. 50 | Transcript show: 'do something with ', (STON toString: newFilters);cr. 51 | ] 52 | 53 | { #category : #rendering } 54 | DpEntityListing >> renderOn: h [ 55 | h div: [ 56 | filtersComponent render: h. 57 | listingComponent render: h. 58 | ] 59 | 60 | ] 61 | -------------------------------------------------------------------------------- /Depot-Core/DpFirstRun.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am the first component the user sees when accessing a new Depot instance. 3 | 4 | I setup initial configuration: the XTDB URL and admin credentials. 5 | 6 | 7 | " 8 | Class { 9 | #name : #DpFirstRun, 10 | #superclass : #LWStyledComponent, 11 | #instVars : [ 12 | 'url', 13 | 'adminUsername', 14 | 'adminPassword1', 15 | 'adminPassword2', 16 | 'urlOk', 17 | 'urlError' 18 | ], 19 | #category : #'Depot-Core' 20 | } 21 | 22 | { #category : #initialization } 23 | DpFirstRun >> initialize [ 24 | super initialize. 25 | url := '' 26 | ] 27 | 28 | { #category : #rendering } 29 | DpFirstRun >> renderOn: h [ 30 | h div: [ 31 | h div: { self style centeredDialogPanel } with: [ 32 | h div: { self style flexTopToBottom } with: [ 33 | h h3: 'Welcome to Depot!'; 34 | div: { self style flexLeftToRight } with: [ 35 | h label: { #for -> #url } with: 'XTDB URL'; 36 | input: { #id->#url . #name->#url. #type->#text. #value->url }; 37 | button: { #onClick -> (Js call: [ :newUrl | self testUrl: newUrl ] with: (Js inputValue: #url)) } 38 | with: 'Test'. 39 | ]. 40 | urlOk ifNotNil: [ 41 | h div: (urlOk ifTrue:'OK' ifFalse: 'ERROR'), ' ', urlError. 42 | h button: { #disabled -> urlOk not. #onClick -> [ self save ] } with: 'Save and continue'. 43 | ] 44 | ]]] 45 | 46 | ] 47 | 48 | { #category : #actions } 49 | DpFirstRun >> save [ 50 | DpConfig uniqueInstance: (DpConfig new url: url). 51 | ctx go: (DpRouter new matchState: (DpRootState new)) 52 | ] 53 | 54 | { #category : #tests } 55 | DpFirstRun >> testUrl: anUrl [ 56 | | client | 57 | url := anUrl. 58 | client := XtClient new url: anUrl. 59 | [ | status | 60 | status := client status. 61 | urlOk := true. 62 | urlError := 'XTDB version ', status version asString. 63 | self changed. 64 | ] on: Error do: [ :err | 65 | urlOk := false. 66 | urlError := err printString. 67 | self changed. ] 68 | ] 69 | -------------------------------------------------------------------------------- /Depot-Example/Issue.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #Issue, 3 | #superclass : #DpEntity, 4 | #instVars : [ 5 | 'key', 6 | 'project', 7 | 'type', 8 | 'summary', 9 | 'description', 10 | 'linksTo', 11 | 'linksFrom' 12 | ], 13 | #category : #'Depot-Example' 14 | } 15 | 16 | { #category : #accessing } 17 | Issue >> description [ 18 | 19 | ^ description 20 | ] 21 | 22 | { #category : #accessing } 23 | Issue >> description: anObject [ 24 | 25 | description := anObject 26 | ] 27 | 28 | { #category : #accessing } 29 | Issue >> key [ 30 | 31 | ^ key 32 | ] 33 | 34 | { #category : #accessing } 35 | Issue >> key: anObject [ 36 | 37 | key := anObject 38 | ] 39 | 40 | { #category : #accessing } 41 | Issue >> linksFrom [ 42 | 43 | ^ linksFrom 44 | ] 45 | 46 | { #category : #accessing } 47 | Issue >> linksFrom: anObject [ 48 | 49 | linksFrom := anObject 50 | ] 51 | 52 | { #category : #accessing } 53 | Issue >> linksTo [ 54 | 55 | ^ linksTo 56 | ] 57 | 58 | { #category : #accessing } 59 | Issue >> linksTo: anObject [ 60 | 61 | linksTo := anObject 62 | ] 63 | 64 | { #category : #accessing } 65 | Issue >> project [ 66 | 67 | ^ project 68 | ] 69 | 70 | { #category : #accessing } 71 | Issue >> project: anObject [ 72 | 73 | project := anObject 74 | ] 75 | 76 | { #category : #accessing } 77 | Issue >> summary [ 78 | 79 | ^ summary 80 | ] 81 | 82 | { #category : #accessing } 83 | Issue >> summary: anObject [ 84 | 85 | summary := anObject 86 | ] 87 | 88 | { #category : #'as yet unclassified' } 89 | Issue >> summaryView: h [ 90 | 91 | "make this a link" 92 | h span: { #class -> 'issue-summary' } with: [ 93 | h span: key; span: summary 94 | ] 95 | ] 96 | 97 | { #category : #accessing } 98 | Issue >> type [ 99 | 100 | ^ type 101 | ] 102 | 103 | { #category : #accessing } 104 | Issue >> type: anObject [ 105 | 106 | type := anObject 107 | ] 108 | -------------------------------------------------------------------------------- /Depot-UI/DpStyles.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #DpStyles, 3 | #superclass : #LWStylesheetProvider, 4 | #category : #'Depot-UI' 5 | } 6 | 7 | { #category : #'as yet unclassified' } 8 | DpStyles >> appbar [ 9 | ^ self style: [ :s | 10 | self padLeftRight: s of: 3 rem. 11 | s display: #flex; flexDirection: #row; 12 | flexWrap: #wrap; alignItems: #center; 13 | height: 74 px; lineHeight: 24 px; 14 | boxShadow: 'rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px;' 15 | ] 16 | ] 17 | 18 | { #category : #'as yet unclassified' } 19 | DpStyles >> appbarHeader [ 20 | ^ self style: [ :s | s 21 | fontSize: 1.25rem; 22 | fontWeight: 600; 23 | lineHeight: 1.75rem 24 | ] 25 | ] 26 | 27 | { #category : #'as yet unclassified' } 28 | DpStyles >> appbarLink [ 29 | ^ self style: [ :s | 30 | self padLeftRight: s of: 1 rem. 31 | self padTopBottom: s of: 0.5rem. 32 | s display: 'inline-block'; 33 | marginRight: 0.5rem. 34 | ] 35 | ] 36 | 37 | { #category : #'as yet unclassified' } 38 | DpStyles >> appbarMenu [ 39 | ^ self style: [ :s | 40 | self padLeftRight: s of: 0.75rem. 41 | s alignItems: #center; 42 | display: #flex. 43 | ] 44 | ] 45 | 46 | { #category : #accessing } 47 | DpStyles >> center: s [ 48 | s position: #absolute; 49 | left: 50 pct; 50 | top: 50 pct; 51 | transform: 'translateX(-50%) translateY(-50%)' 52 | 53 | ] 54 | 55 | { #category : #'as yet unclassified' } 56 | DpStyles >> centeredDialogPanel [ 57 | ^ self style: [ :s | 58 | self center: s. 59 | s borderRadius: 0.5 rem; 60 | borderColor: 'blue'; 61 | borderWidth: 2 px; 62 | borderStyle: #solid; 63 | padding: 1 rem ] 64 | 65 | ] 66 | 67 | { #category : #'as yet unclassified' } 68 | DpStyles >> flexCol: s [ 69 | s display: 'flex'; flexDirection: 'column'. 70 | 71 | ] 72 | 73 | { #category : #'as yet unclassified' } 74 | DpStyles >> flexLeftToRight [ 75 | ^ self style: [ :s | s 76 | display: #flex; 77 | flexDirection: #row ] 78 | ] 79 | 80 | { #category : #'as yet unclassified' } 81 | DpStyles >> flexTopToBottom [ 82 | ^ self style: [ :s | s 83 | display: #flex; 84 | flexDirection: #column ] 85 | ] 86 | 87 | { #category : #'as yet unclassified' } 88 | DpStyles >> formField [ 89 | ^ self style: [ :s | s 90 | display: #block 91 | ] 92 | 93 | ] 94 | 95 | { #category : #'as yet unclassified' } 96 | DpStyles >> formFieldError [ 97 | ^ self style: [ :s | 98 | s child: 'input' with: [ :input | 99 | input borderColor: #red; borderWidth: 3px ]] 100 | 101 | ] 102 | 103 | { #category : #'as yet unclassified' } 104 | DpStyles >> formFieldLabel [ 105 | ^ self style: [ :s | s 106 | display: 'inline-block'; 107 | width: 15vw ] 108 | 109 | ] 110 | 111 | { #category : #'as yet unclassified' } 112 | DpStyles >> formInput: hasError [ 113 | ^ self style: [ :s | 114 | hasError 115 | ifTrue: [ s borderColor: #red; borderWidth: 1px ] 116 | ifFalse: [ s backgroundColor: #wheat ] 117 | ] 118 | 119 | ] 120 | 121 | { #category : #'as yet unclassified' } 122 | DpStyles >> formInputAndError [ 123 | ^ self style: [ :s | self flexCol: s ] 124 | 125 | ] 126 | 127 | { #category : #'as yet unclassified' } 128 | DpStyles >> headerCentered [ 129 | ^ self style: [ :s | s 130 | fontSize: 2 rem; 131 | width: 100 pct; 132 | textAlign: #center 133 | ] 134 | 135 | ] 136 | 137 | { #category : #'as yet unclassified' } 138 | DpStyles >> padLeftRight: s of: amount [ 139 | s paddingLeft: amount; paddingRight: amount. 140 | ^ s 141 | ] 142 | 143 | { #category : #'as yet unclassified' } 144 | DpStyles >> padTopBottom: s of: amount [ 145 | s paddingTop: amount; paddingBottom: amount. 146 | ^ s 147 | ] 148 | -------------------------------------------------------------------------------- /Depot-Entity/DpEntityBuilder.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a development time helper to conveniently build new entity 3 | classes with a fluid builder syntax. 4 | 5 | " 6 | Class { 7 | #name : #DpEntityBuilder, 8 | #superclass : #Object, 9 | #instVars : [ 10 | 'name', 11 | 'mapping', 12 | 'mappings' 13 | ], 14 | #category : #'Depot-Entity' 15 | } 16 | 17 | { #category : #adding } 18 | DpEntityBuilder >> addField: aMapping forName: accessorName [ 19 | | attrName | 20 | attrName := (String streamContents: [:n | n 21 | << ':'; 22 | << name; 23 | << '/'; 24 | << accessorName ]) asSymbol. 25 | aMapping configureMapping: attrName -> accessorName. 26 | ^ self addMapping: aMapping 27 | ] 28 | 29 | { #category : #adding } 30 | DpEntityBuilder >> addMapping: aMapping [ 31 | mappings ifNil: [ mappings := OrderedCollection new ]. 32 | mappings add: aMapping. 33 | ^ aMapping 34 | ] 35 | 36 | { #category : #building } 37 | DpEntityBuilder >> build [ 38 | | cls fields | 39 | fields := mappings collect: #getter. 40 | cls := DpEntity subclass: name instanceVariableNames: (' ' join: fields) classVariableNames: '' poolDictionaries: '' category: 'Depot-User'. 41 | self buildAccessors: cls. 42 | self buildDpMapping: cls. 43 | self buildDefaultSummary: cls. 44 | ] 45 | 46 | { #category : #building } 47 | DpEntityBuilder >> buildAccessors: cls [ 48 | self fields do: [:get | cls compile: get , ' 49 | ^ ' , get. 50 | cls compile: get , ': anObject 51 | ' , get , ' := anObject. 52 | ^ self' ] 53 | ] 54 | 55 | { #category : #'as yet unclassified' } 56 | DpEntityBuilder >> buildDefaultSummary: cls [ 57 | cls compile: (String streamContents: [:out | out 58 | << 'summaryView: h 59 | 60 | "FIXME: implement a summary view, this is a autogenerated placeholder." 61 | h div: (STON toString: self) 62 | ']) 63 | ] 64 | 65 | { #category : #'as yet unclassified' } 66 | DpEntityBuilder >> buildDpMapping: cls [ 67 | "Compile the dpMapping method" 68 | cls class compile: (String streamContents: [:out | out 69 | << 'dpMapping'; << Character cr; 70 | << ' ^ XtEntityMapping withAll: { 71 | '. 72 | mappings doWithIndex: [ :f :i | 73 | i > 1 ifTrue:[out << '. 74 | ']. 75 | out << f asDpMappingSource. 76 | ]. 77 | out << '}' 78 | ]) 79 | ] 80 | 81 | { #category : #'as yet unclassified' } 82 | DpEntityBuilder >> child: accessorName to: aChildClass [ 83 | ^ self addField: (DpChildMapping new entityClass: aChildClass; yourself) 84 | forName: accessorName 85 | ] 86 | 87 | { #category : #'as yet unclassified' } 88 | DpEntityBuilder >> childMany: accessorName to: aChildClass [ 89 | | m | 90 | m := self child: accessorName to: aChildClass. 91 | m beMany. 92 | ^ m 93 | ] 94 | 95 | { #category : #accessing } 96 | DpEntityBuilder >> date: accessorName [ 97 | ^ self addField: DpDateField new forName: accessorName 98 | ] 99 | 100 | { #category : #accessing } 101 | DpEntityBuilder >> date: accessorName with: aBlock [ 102 | aBlock value: (self date: accessorName) 103 | ] 104 | 105 | { #category : #accessing } 106 | DpEntityBuilder >> fields [ 107 | ^ mappings collect: #getter 108 | ] 109 | 110 | { #category : #accessing } 111 | DpEntityBuilder >> float: accessorName [ 112 | ^ self addField: DpFloatField new forName: accessorName 113 | ] 114 | 115 | { #category : #accessing } 116 | DpEntityBuilder >> float: accessorName with: aBlock [ 117 | aBlock value: (self float: accessorName) 118 | ] 119 | 120 | { #category : #accessing } 121 | DpEntityBuilder >> integer: accessorName [ 122 | ^ self addField: DpIntegerField new forName: accessorName 123 | ] 124 | 125 | { #category : #accessing } 126 | DpEntityBuilder >> integer: accessorName with: aBlock [ 127 | aBlock value: (self integer: accessorName) 128 | ] 129 | 130 | { #category : #'as yet unclassified' } 131 | DpEntityBuilder >> link: accessorName to: aLinkClass [ 132 | ^ self addField: (XtLinkMapping new entityClass: aLinkClass; yourself) 133 | forName: accessorName 134 | ] 135 | 136 | { #category : #'as yet unclassified' } 137 | DpEntityBuilder >> linkMany: accessorName to: aLinkClass [ 138 | | m | 139 | m := self link: accessorName to: aLinkClass. 140 | m beMany. 141 | ^ m 142 | ] 143 | 144 | { #category : #accessing } 145 | DpEntityBuilder >> name: aClassName [ 146 | name := aClassName. 147 | ] 148 | 149 | { #category : #accessing } 150 | DpEntityBuilder >> scaledDecimal: accessorName [ 151 | ^self addField: DpScaledDecimalField new forName: accessorName 152 | ] 153 | 154 | { #category : #accessing } 155 | DpEntityBuilder >> scaledDecimal: accessorName with: aBlock [ 156 | aBlock value: (self scaledDecimal: accessorName) 157 | ] 158 | 159 | { #category : #accessing } 160 | DpEntityBuilder >> string: accessorName [ 161 | ^self addField: DpStringField new forName: accessorName 162 | ] 163 | 164 | { #category : #accessing } 165 | DpEntityBuilder >> string: accessorName with: aBlock [ 166 | aBlock value: (self string: accessorName) 167 | ] 168 | 169 | { #category : #accessing } 170 | DpEntityBuilder >> time: accessorName [ 171 | ^ self addField: DpTimeField new forName: accessorName 172 | ] 173 | 174 | { #category : #accessing } 175 | DpEntityBuilder >> time: accessorName with: aBlock [ 176 | aBlock value: (self time: accessorName) 177 | ] 178 | --------------------------------------------------------------------------------