diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..3a0dfbf --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,26 @@ +name: Unit Tests + +on: [push] + +jobs: + unit-tests: + runs-on: ubuntu-latest + strategy: + matrix: + smalltalk: [Pharo64-10, Pharo64-11] + name: ${{ matrix.smalltalk }} + steps: + - uses: actions/checkout@v2 + - uses: hpi-swa/setup-smalltalkCI@v1 + with: + smalltalk-image: ${{ matrix.smalltalk }} + - name: Load Image and Run Tests + run: smalltalkci -s ${{ matrix.smalltalk }} .smalltalkci/.unit-tests.ston + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + timeout-minutes: 15 + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v1 + with: + name: ${{matrix.os}}-${{matrix.smalltalk}} + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.smalltalk.ston b/.smalltalk.ston new file mode 100644 index 0000000..448c009 --- /dev/null +++ b/.smalltalk.ston @@ -0,0 +1,13 @@ +SmalltalkCISpec { + #loading : [ + SCIMetacelloLoadSpec { + #baseline : 'Ride', + #directory : '', + #load : [ 'Core', + 'Tests' + ], + #platforms : [ #pharo + ] + } + ] +} \ No newline at end of file diff --git a/.smalltalkci/.unit-tests.ston b/.smalltalkci/.unit-tests.ston new file mode 100644 index 0000000..c35e86a --- /dev/null +++ b/.smalltalkci/.unit-tests.ston @@ -0,0 +1,16 @@ +SmalltalkCISpec { + #loading : [ + SCIMetacelloLoadSpec { + #baseline : 'Ride', + #directory : '../', + #load : [ 'Core', 'Tests' ], + #platforms : [ #pharo ] + } + ], + #testing : { + #coverage : { + #packages : [ 'Ride', 'Ride-Builder', 'Ride-Pharo' ], + #format: #lcov + } + } +} \ No newline at end of file diff --git a/AddAuthToYourRideBasedWebApplication.md b/AddAuthToYourRideBasedWebApplication.md new file mode 100644 index 0000000..ffaaade --- /dev/null +++ b/AddAuthToYourRideBasedWebApplication.md @@ -0,0 +1,3 @@ +# Add auth to your Ride-based web application + +To be done. \ No newline at end of file diff --git a/BaselineOfRide/BaselineOfRide.class.st b/BaselineOfRide/BaselineOfRide.class.st index d7f4937..25ace48 100644 --- a/BaselineOfRide/BaselineOfRide.class.st +++ b/BaselineOfRide/BaselineOfRide.class.st @@ -11,7 +11,7 @@ Class { BaselineOfRide >> baseline: spec [ - spec for: #common do: [ + spec for: #common do: [ self setUpDependencies: spec. self setUpPackages: spec. spec @@ -19,8 +19,7 @@ BaselineOfRide >> baseline: spec [ group: 'Builder' with: #( 'Ride-Builder' ); group: 'Tests' with: #( 'Ride-Tests' ); group: 'Examples' with: #( 'Ride-Examples' ); - group: 'Tools' with: #( 'Ride-Tools' ); - group: 'default' with: #( 'Core' ) ] + group: 'default' with: #( 'Core' 'Builder' ) ] ] { #category : #actions } @@ -39,7 +38,7 @@ BaselineOfRide >> mapless: spec [ spec baseline: 'Mapless' with: [ spec repository: - 'github://sebastianconcept/Mapless:v0.6.0/src'; + 'github://sebastianconcept/Mapless:v0.7.0/src'; loads: #( 'Core' 'Memory' 'SQLite' ) ] ] @@ -77,9 +76,11 @@ BaselineOfRide >> setUpPackages: spec [ 'Mapless' 'SingularizePluralize' 'STTemplate' ) ]. spec package: 'Ride-Builder' with: [ spec requires: #( 'Ride' ) ]. - spec package: 'Ride-Tests' with: [ spec requires: #( 'Ride' ) ]. + spec + package: 'Ride-Tests' + with: [ spec requires: #( 'Ride' ) ]. spec package: 'Ride-Examples' with: [ spec requires: #( 'Ride' ) ]. - spec package: 'Ride-Tools' with: [ spec requires: #( 'Ride' ) ] + spec package: 'Ride-Tests' with: [ spec requires: #( 'Ride' ) ] ] { #category : #dependencies } @@ -96,7 +97,7 @@ BaselineOfRide >> sst: spec [ spec baseline: 'STTemplate' with: [ spec - repository: 'github://sebastianconcept/STTemplate:v0.0.3'; + repository: 'github://sebastianconcept/STTemplate:v0.2.0'; loads: #( 'Core' ) ] ] @@ -105,7 +106,7 @@ BaselineOfRide >> teapot: spec [ spec baseline: 'Teapot' with: [ spec - repository: 'github://zeroflag/Teapot/source'; + repository: 'github://zeroflag/Teapot:v2.7.0/source'; loads: #( 'Deployment' ) ] ] @@ -114,6 +115,6 @@ BaselineOfRide >> zinc: spec [ spec baseline: 'ZincHTTPComponents' with: [ spec - repository: 'github://svenvc/zinc/repository'; + repository: 'github://svenvc/zinc:v5/repository'; loads: #( 'default' 'WebSocket' ) ] ] diff --git a/BuildingATwitterLikeApplicationWithRide.md b/BuildingATwitterLikeApplicationWithRide.md new file mode 100644 index 0000000..65a22c8 --- /dev/null +++ b/BuildingATwitterLikeApplicationWithRide.md @@ -0,0 +1,3 @@ +# Building a Twitter like application with Ride + + To be done. \ No newline at end of file diff --git a/CHANGES.md b/CHANGES.md index 4da3074..71d3c7b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,9 @@ +Jan 23, 2024 +=================================== +- Added smalltalkCI +- Added Codecov +- Added which Pharo versions are supported (currently only Pharo10) + Jan 2, 2024 =================================== - Added more extensions to `BockClosure` based on `logLevel:` so the application can log content that will be computed only if the log level is allowing that. This is specially convenient for cases when you want to do some handy but costly log in debug mode: `[ 'Prints only when LOGLEVEL=#DEBUG' ] logDebug`. diff --git a/DeployingYourRideBasedWebApplicationOnTheCloud.md b/DeployingYourRideBasedWebApplicationOnTheCloud.md new file mode 100644 index 0000000..f499c0a --- /dev/null +++ b/DeployingYourRideBasedWebApplicationOnTheCloud.md @@ -0,0 +1,3 @@ +# Deploying your Ride-based web application on the cloud + +To be done. \ No newline at end of file diff --git a/MakeABlogWithCommentsUsingRideInEightMinutes.md b/MakeABlogWithCommentsUsingRideInEightMinutes.md new file mode 100644 index 0000000..0f1d619 --- /dev/null +++ b/MakeABlogWithCommentsUsingRideInEightMinutes.md @@ -0,0 +1,3 @@ +# Make Blog with comments using Ride in 8 minutes + + To be done. \ No newline at end of file diff --git a/README.md b/README.md index e3c0b0a..125dc57 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,166 @@ # Ride +The Smalltalk web application framework with productivity as a feature. -### Install in a Pharo image +[![Release](https://img.shields.io/github/v/tag/sebastianconcept/ride?label=release)](https://github.com/sebastianconcept/ride/releases) +[![Unit Tests](https://github.com/sebastianconcept/ride/actions/workflows/build.yml/badge.svg)](https://github.com/sebastianconcept/ride/actions/workflows/build.yml) -Essential core: +[![Coverage Status](https://codecov.io/github/sebastianconcept/ride/coverage.svg?branch=main)](https://codecov.io/gh/sebastianconcept/ride/branch/master) -```Smalltalk -Metacello new - baseline: 'Ride'; - repository: 'github://sebastianconcept/ride'; - load. +[![Pharo 11](https://img.shields.io/badge/Pharo-11-%23383932.svg)](https://pharo.org/download) +[![Pharo 10](https://img.shields.io/badge/Pharo-10-%23383932.svg)](https://pharo.org/download) + +[![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE.txt) +[![Social](https://img.shields.io/github/stars/sebastianconcept/ride?style=social)]() + +[![Commits](https://img.shields.io/github/commit-activity/m/sebastianconcept/ride)](https://github.com/sebastianconcept/ride/graphs/commit-activity) + +## Features + +- MVP - Model-View-Presenter architecture. +- Declarative routing. +- Multiple View options for rendering HTML, JSON, XML and more. +- Default Views based in smart efficient templates using [STTemplate](https://github.com/sebastianconcept/STTemplate). +- Versatile and comfortable persistence without lock-in using [Mapless](https://github.com/sebastianconcept/Mapless). +- Intuitive conventions. +- Made with scalability and performance in mind. +- [RESTful](https://restfulapi.net/). +- Builder to scaffold useful code fast. +- ~~Basic validation~~. To be done. +- Optional custom JavaScript for presenters. +- AJAX via [htmx](https://htmx.org/). +- ~~Able to deploy containerized from day one~~. To be done. + +--- + +1. [Description](#description) +2. [Examples](#examples) +3. [Hello World](#hello-world) +4. [Install](#install) +5. [Guides](#guides) +6. [Talks](#talks) + +## Description + +Ride is a framework designed for building Web Applications and [RESTful](https://restfulapi.net/) APIs, optimizing for developer productivity while preserving production efficiency and scalability. Ride focuses on eliminating the hurdles to quickly build and deploy real projects without compromising your architecture's ability to scale in production. + +It comes equipped with a builder that streamlines the process of scaffolding models, views, presenters, [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) operations and their unit tests for the models you need. Ride removes many technical obstacles, allowing you to progress rapidly in your application development journey from proof of concept and startup product to a growing business. + +## Install + +#### Fetch a fresh Pharo image + +``` +mkdir my-ride-app +cd my-ride-app +curl get.pharo.org/64/100 | bash +curl get.pharo.org/64/vm100 | bash ``` -All optionals: +And run it + +``` +./pharo-ui Pharo.image +``` + +#### Install Ride in it + ```Smalltalk Metacello new baseline: 'Ride'; repository: 'github://sebastianconcept/ride'; - load: #('Core' 'Examples' 'Tools'). + onConflict: [ :ex | ex useIncoming ]; + load. ``` -### Snippets -Handy to start stop the Ride in the Welcome example app: +## Examples + ```Smalltalk +"Stop anything Ride that is currently running in the image and reset caches" Ride stop; reset. -RideWelcomeApp install. +"A Ride-based application has its own helper RideService subclass. Like BlogApp in this example:" + +"Install BlogApp as the Ride service" +BlogApp install. -Ride stop. -Ride start. +"Start and stop the service" +BlogApp start. +BlogApp stop. -Ride restart. +"Service restart" +BlogApp restart. ``` -### Enhancing developer productivity -Ride offers you a convenient way to quickly draft the basic elements needed for implementing a feature based in a new model. For example, given a CoffeeSystem web application a new `CoffeeInvoice` model, you could: +#### Enhanced developer productivity + +Ride offers you a convenient way to quickly create the basic elements needed for implementing basic operations in a new model. For example, given a `Blog` web application to publish `Post` and `Comment` models, you could find the following snippets helpful for pushing useful things fast: ```smalltalk -"Set the default package that will be the destination of the generated code" -Ride draft defaultPackageName: 'CoffeeSystem'. +"Set the default package name that will be the destination +of the code generated by Ride's builder" +Ride create app setPackageNameTo: #Blog. -"Create a RideModel subclass CoffeeInvoice used as app domain state" -Ride draft model for: #CoffeeInvoice. +"Create the foundational Smalltalk code and files +of the project using the given root word 'Blog' " +Ride create app for: #Blog. -"Create a RidePresenter subclass CoffeeInvoicePresenter" -Ride draft presenter for: #CoffeeInvoice. +"Create the Smalltalk code and files for having +basic CRUD features in a model named `Post`" +Ride create mvp crud for: #Post. -"Create the template templates/views/coffee_invoices/index.mustache" -Ride draft view mustache for: #CoffeeInvoice. +"Same for a model named `Comment`" +Ride create mvp crud for: #Comment. ``` -## MVP +#### MVP + +Ride also can help you create the raw elements of the Model-View-Presenter code separately for a given model: + +```smalltalk +"Create the model and presenter classes and STTemplate file at +`views/templates/posts/index.html.stt` for Post" +Ride create mvp for: #Post. + +"Same as before but PostsPresenter loads custom JavaScript" +Ride create mvp withJs for: #Post. +``` -Using these basic code generators, Ride also can create the elemental Model-View-Presenter code for a given model: +Or even as its individual parts: ```smalltalk -"Create the model and presenter classes and mustache template file for CoffeeInvoice" -Ride draft mvp for: #CoffeeInvoice. +"Create a RideModel subclass Subscription used to +store people subscribed to the blog." +Ride create model for: #Subscription. -"Same as before but CoffeeInvoicePresenter loads custom JavaScript" -Ride draft mvp withJs for: #CoffeeInvoice. +"Create a RidePresenter subclass SubscriptionPresenter" +Ride create presenter for: #Subscription. -"Have an unstyled but functioning CRUD for the CoffeeInvoice models based on MVP" -Ride draft mvp crud for: #CoffeeInvoice. +"Create the template correponding to Subscription at +templates/views/subscriptions/index.stt" +Ride create view stt for: #Subscription. ``` -## API -If you need an API and you like to keep concerns organized with MVC, this is how you can generate its boilerplate code: +#### API + +If you need an API and you like to keep concerns organized with MVP, this is how you can generate its boilerplate code: + ```smalltalk -Ride draft api crud for: #CoffeeInvoice. -``` \ No newline at end of file +Ride create api crud for: #Post. +``` + +## Guides + +- [Make a Blog with comments using Ride in 8 minutes](./MakeABlogWithCommentsUsingRideInEightMinutes.md). +- [Building a Twitter like application with Ride](./BuildingATwitterLikeApplicationWithRide.md). +- [Ride-based SaaS template](./RideBasedSaaSTemplate.md). +- [Add auth to your Ride-based web application](AddAuthToYourRideBasedWebApplication.md). +- [Deploying your Ride-based web application on the cloud](DeployingYourRideBasedWebApplicationOnTheCloud.md). + +## Talks + +Ride was presented for the first time at [Smalltalks 2023](https://smalltalks2023.fast.org.ar/) organized by [F.A.S.T.](https://www.fast.org.ar/) at [Universidad Nacional de Quilmes](https://www.unq.edu.ar/). + +#### November 2023 + +[RAD with templates, htmx and stateless Smalltalk images by Sebastian Sastre](https://www.youtube.com/watch?v=4_gmvN0pimI) diff --git a/Ride-Builder/RideAPICRUDHelper.class.st b/Ride-Builder/RideAPICRUDHelper.class.st index aaf4477..832730c 100644 --- a/Ride-Builder/RideAPICRUDHelper.class.st +++ b/Ride-Builder/RideAPICRUDHelper.class.st @@ -6,11 +6,3 @@ Class { #superclass : #RideCRUDHelper, #category : #'Ride-Builder' } - -{ #category : #'instance creation' } -RideAPICRUDHelper >> for: aSymbol [ - - super for: aSymbol. - doer presenter for: aSymbol. - self addCRUDMethodsTo: (self presenterForModel: aSymbol) for: aSymbol -] diff --git a/Ride-Builder/RideAPIHelper.class.st b/Ride-Builder/RideAPIHelper.class.st index e27f4ae..6c07eb2 100644 --- a/Ride-Builder/RideAPIHelper.class.st +++ b/Ride-Builder/RideAPIHelper.class.st @@ -11,7 +11,7 @@ Class { RideAPIHelper >> crud [ ^ RideAPICRUDHelper new - doer: doer; + doer: builder; yourself ] diff --git a/Ride-Builder/RideAbstractTemplateHelper.class.st b/Ride-Builder/RideAbstractTemplateHelper.class.st index dce1575..6302d44 100644 --- a/Ride-Builder/RideAbstractTemplateHelper.class.st +++ b/Ride-Builder/RideAbstractTemplateHelper.class.st @@ -10,6 +10,12 @@ Class { #category : #'Ride-Builder' } +{ #category : #accessing } +RideAbstractTemplateHelper class >> getPathFor: aSymbol [ + + ^ Ride resource viewsDirectory / aSymbol asSnakeCase +] + { #category : #initialization } RideAbstractTemplateHelper >> initialize [ diff --git a/Ride-Builder/RideAbstractWebApplicationPresenterClassHelper.class.st b/Ride-Builder/RideAbstractWebApplicationPresenterClassHelper.class.st index e1f95d9..afe86cf 100644 --- a/Ride-Builder/RideAbstractWebApplicationPresenterClassHelper.class.st +++ b/Ride-Builder/RideAbstractWebApplicationPresenterClassHelper.class.st @@ -10,7 +10,7 @@ Class { { #category : #accessing } RideAbstractWebApplicationPresenterClassHelper >> classNameFor: aSymbol [ - ^ '{1}WebApplicationPresenter' format: { aSymbol } + ^ '{1}Presenter' format: { aSymbol } ] { #category : #actions } @@ -29,5 +29,5 @@ RideAbstractWebApplicationPresenterClassHelper >> targetPackageNameTag [ { #category : #accessing } RideAbstractWebApplicationPresenterClassHelper >> targetedSuperclassFor: aSymbol [ - ^ RideWebApplicationPresenter + ^ RidePresenter ] diff --git a/Ride-Builder/RideAppHelper.class.st b/Ride-Builder/RideAppHelper.class.st index ed377fc..cf154d2 100644 --- a/Ride-Builder/RideAppHelper.class.st +++ b/Ride-Builder/RideAppHelper.class.st @@ -91,6 +91,16 @@ MW_NOTES="This is a maintenance window that we had programmed to keep all workin aSymbol asSnakeCase } ] +{ #category : #accessing } +RideAppHelper class >> feedbackContentFor: aSymbol [ + + ^ '
+

Feedback

+

This UI needs to get this feedback displayed.

+
+' format: { aSymbol } +] + { #category : #accessing } RideAppHelper class >> fiveHundredContentFor: aSymbol [ @@ -580,6 +590,9 @@ RideAppHelper >> ensureTemplatesSharedOn: directory for: aSymbol [ directory / '500.html.stt' writeStreamDo: [ :stream | stream nextPutAll: (self class fiveHundredContentFor: aSymbol) ]. + directory / 'feedback.html.stt' writeStreamDo: [ :stream | + stream nextPutAll: (self class feedbackContentFor: aSymbol) ]. + directory / 'maintenance.html.stt' writeStreamDo: [ :stream | stream nextPutAll: (self class maintenanceContentFor: aSymbol) ] ] diff --git a/Ride-Builder/RideAppHomePresenterClassHelper.class.st b/Ride-Builder/RideAppHomePresenterClassHelper.class.st index 6d84a77..91bfc64 100644 --- a/Ride-Builder/RideAppHomePresenterClassHelper.class.st +++ b/Ride-Builder/RideAppHomePresenterClassHelper.class.st @@ -18,7 +18,7 @@ RideAppHomePresenterClassHelper class >> indexMethodFor: aSymbol [ ^ 'index - ^ self render: ''index.html'' + ^ self render ' format: { aSymbol } ] @@ -121,7 +121,7 @@ RideAppHomePresenterClassHelper >> targetPackageNameTag [ RideAppHomePresenterClassHelper >> targetedSuperclassFor: aSymbol [ | targetClassName | - targetClassName := '{1}WebApplicationPresenter' format: { aSymbol }. + targetClassName := '{1}Presenter' format: { aSymbol }. ^ Smalltalk at: targetClassName asSymbol ifAbsent: [ RideBoilerplateError signal: ('{1} was not found in the image' format: { targetClassName }) ] diff --git a/Ride-Builder/RideBuilder.class.st b/Ride-Builder/RideBuilder.class.st index a0f2c71..e256f01 100644 --- a/Ride-Builder/RideBuilder.class.st +++ b/Ride-Builder/RideBuilder.class.st @@ -1,5 +1,33 @@ " -I'm a Ride helper dedicated to keep types of actions being done by the specialists concerned in each subject. For example, model, presenter, crud, database, etc +I'm a Ride helper dedicated to access types of building actions needed for a Web App or API. + +It might be doing something to the database or it could be creating a new class to use as model or a presenter for it. + +It could also mean to use these as the step to build something that requires several steps. + +Example: + +``` +""Create the RideModel subclass for Article"" +Ride create model for: #Article. + +""Create the RidePresenter subclass for Article"" +Ride create presenter for: #Article. +``` + +``` +""Create the model and presenter classes for Article and one template as view +so you have html out of ArticlePresenter >> index."" + +Ride create mvp for: #Article. +``` + +``` +""Create the model and presenter classes for Article and the templates +that allow you to Create, Read, Update and Delete Article models."" + +Ride create mvp crud for: #Article. +``` " Class { #name : #RideBuilder, @@ -94,6 +122,12 @@ RideBuilder >> packageName: anObject [ packageName := anObject ] +{ #category : #accessing } +RideBuilder >> pluralPresenter [ + + ^ RidePluralPresenterHelper new +] + { #category : #accessing } RideBuilder >> presenter [ diff --git a/Ride-Builder/RideCRUDHelper.class.st b/Ride-Builder/RideCRUDHelper.class.st index 7958ec3..fe78fd9 100644 --- a/Ride-Builder/RideCRUDHelper.class.st +++ b/Ride-Builder/RideCRUDHelper.class.st @@ -7,6 +7,17 @@ Class { #category : #'Ride-Builder' } +{ #category : #accessing } +RideCRUDHelper class >> allowedParamsMethodFor: aSymbol [ + + ^ 'allowedParams + + "Answer the names of the parameters that are allowed to be read from the current request." + + ^ #() +' +] + { #category : #accessing } RideCRUDHelper class >> createMethodFor: aSymbol [ @@ -14,11 +25,18 @@ RideCRUDHelper class >> createMethodFor: aSymbol [ "POST /{2}" - model := {1} fromRequest: self currentRequest. + | request | + request := self currentRequest. + + model := {1} new. + + self allowedFields keysAndValuesDo: [ :k :v | model at: k put: v ]. + + model saveOrSignal. - [ model save ] on: Error do: [ :x | - RideUnprocessableEntity signal: x messageText ]. - self redirectTo: #{2}_url notice: ''{1} was successfully created.''. + self addNoticeSuccess: ''{1} was successfully created.''. + + self redirectToIndex ' format: { aSymbol. aSymbol asLowercase asPlural asSnakeCase } @@ -29,17 +47,16 @@ RideCRUDHelper class >> destroyMethodFor: aSymbol [ ^ 'destroy - "DELETE /{2}" + "DELETE /{2}/" - model ifNil: [ - model := {1} findId: self currentRequest uri segments last. - model ifNotNil: [ - [ model destroy ] - on: Error - do: [ :x | RideUnprocessableEntity signal: x messageText ]. - self - redirectTo: #{2}_url - notice: ''{1} was successfully destroyed.'' ] ] + model := {1} findId: (self currentRequestParam: #id). + model ifNil: self modelNotFoundHandler. + + model destroyOrSignal. + + self addNoticeSuccess: ''{1} was successfully deleted.''. + + self redirectToIndex ' format: { aSymbol. aSymbol asLowercase asPlural asSnakeCase } @@ -50,10 +67,12 @@ RideCRUDHelper class >> editMethodFor: aSymbol [ ^ 'edit - "GET /{2}/edit" + "GET /{2}//edit" + + model := {1} findId: (self currentRequestParam: #id). + model ifNil: self modelNotFoundHandler. - model ifNil: [ - model := {1} findId: (self currentRequest uri segments reversed second) ] + ^ self renderUsing: ''edit.html'' ' format: { aSymbol. aSymbol asLowercase asPlural asSnakeCase } @@ -66,10 +85,14 @@ RideCRUDHelper class >> indexMethodFor: aSymbol [ "GET /{2}" - model := {1} findAll + model := self {3}. + + ^ self render + ' format: { aSymbol. - aSymbol asLowercase asPlural asSnakeCase } + aSymbol asLowercase asPlural asSnakeCase. + aSymbol uncapitalized asPlural } ] { #category : #accessing } @@ -79,21 +102,42 @@ RideCRUDHelper class >> newModelMethodFor: aSymbol [ "GET /{2}/new" - model := {1} new + model := {1} new. + + ^ self renderUsing: ''new.html'' ' format: { aSymbol. aSymbol asLowercase asPlural asSnakeCase } ] { #category : #accessing } -RideCRUDHelper class >> showMethodFor: aSymbol [ +RideCRUDHelper class >> pluralIndexMethodFor: aSymbol [ - ^ 'show + ^ 'index + + "GET /{2}" + + model := self {3}. + + ^ self render + +' format: { + aSymbol. + aSymbol asLowercase asPlural asSnakeCase. + aSymbol uncapitalized asPlural } +] + +{ #category : #accessing } +RideCRUDHelper class >> singularIndexMethodFor: aSymbol [ + + ^ 'index "GET /{2}/" - model ifNil: [ - model := {1} findId: (self currentRequest uri segments last) ] + model := {1} findId: (self currentRequestParam: #id). + model ifNil: self modelNotFoundHandler. + + ^ self renderUsing: ''index.html'' ' format: { aSymbol. aSymbol asLowercase asPlural asSnakeCase } @@ -104,22 +148,35 @@ RideCRUDHelper class >> updateMethodFor: aSymbol [ ^ 'update - "PATCH/PUT /{2}" + "POST/PATCH/PUT /{2}/" + + model := {1} findId: (self currentRequestParam: #id). + model ifNil: self modelNotFoundHandler. + + self updateModelFromRequest: self currentRequest. - model updateFromRequest: self currentRequest. + model saveOrSignal. + + self addNoticeSuccess: ''{1} was successfully updated.''. - [ model save ] on: Error do: [ :x | - RideUnprocessableEntity signal: x messageText ]. - self redirectTo: #{2}_url notice: ''{1} was successfully updated.'' + self redirectFor: model ' format: { aSymbol. aSymbol asLowercase asPlural asSnakeCase } ] { #category : #actions } -RideCRUDHelper >> addCRUDMethodsTo: aClass for: aSymbol [ +RideCRUDHelper >> addAllowedParamsMethodTo: aClass for: aSymbol [ + + aClass compile: (self class allowedParamsMethodFor: aSymbol). + aClass organization classify: #allowedParams under: 'accessing' +] +{ #category : #actions } +RideCRUDHelper >> addCRUDMethodsTo: aClass for: aSymbol [ +self deprecated: 'singular or plural'. self addIndexMethodTo: aClass for: aSymbol. + self addModelsAccessorMethodTo: aClass for: aSymbol. self addShowMethodTo: aClass for: aSymbol. self addNewModelMethodTo: aClass for: aSymbol. self addEditMethodTo: aClass for: aSymbol. @@ -128,6 +185,19 @@ RideCRUDHelper >> addCRUDMethodsTo: aClass for: aSymbol [ self addDestroyMethodTo: aClass for: aSymbol ] +{ #category : #actions } +RideCRUDHelper >> addCRUDTemplatesTo: aClass for: aSymbol [ +self deprecated: 'silgular or plural'. + self addIndexTemplateFor: aSymbol. + self addShowTemplateFor: aSymbol. + self addNewTemplateFor: aSymbol. + self addEditTemplateFor: aSymbol. + self addFormNewTemplateFor: aSymbol. + self addFormEditTemplateFor: aSymbol. + self addFormFieldsTemplateFor: aSymbol. + self addModelDisplayContentFor: aSymbol +] + { #category : #actions } RideCRUDHelper >> addCreateMethodTo: aClass for: aSymbol [ @@ -150,12 +220,75 @@ RideCRUDHelper >> addEditMethodTo: aClass for: aSymbol [ ] { #category : #actions } -RideCRUDHelper >> addIndexMethodTo: aClass for: aSymbol [ +RideCRUDHelper >> addEditTemplateFor: aSymbol [ + + self subclassResponsibility +] + +{ #category : #actions } +RideCRUDHelper >> addFormEditTemplateFor: aSymbol [ + + self subclassResponsibility +] + +{ #category : #actions } +RideCRUDHelper >> addFormFieldsTemplateFor: aSymbol [ + self subclassResponsibility +] + +{ #category : #actions } +RideCRUDHelper >> addFormNewTemplateFor: aSymbol [ + + self subclassResponsibility +] + +{ #category : #actions } +RideCRUDHelper >> addFormTemplateFor: aSymbol [ + + self deprecated: 'use either formNew or formEdit'. + self subclassResponsibility +] + +{ #category : #actions } +RideCRUDHelper >> addIndexMethodTo: aClass for: aSymbol [ + self deprecated: ' plural or singular'. aClass compile: (self class indexMethodFor: aSymbol). aClass organization classify: #index under: 'actions' ] +{ #category : #actions } +RideCRUDHelper >> addIndexTemplateFor: aSymbol [ + + self subclassResponsibility +] + +{ #category : #actions } +RideCRUDHelper >> addModelDisplayContentFor: aSymbol [ + + self subclassResponsibility +] + +{ #category : #actions } +RideCRUDHelper >> addModelsAccessorMethodTo: aClass for: aSymbol [ + + | selector source | + selector := aSymbol uncapitalized asPlural asSymbol. + + source := '{3} + + "Answers all {1} in the system." + + ^ {1} findAll +' format: { + aSymbol. + aSymbol asLowercase asPlural asSnakeCase. + selector }. + + aClass compile: source. + aClass organization classify: selector under: 'accessing' +] + { #category : #actions } RideCRUDHelper >> addNewModelMethodTo: aClass for: aSymbol [ @@ -163,11 +296,109 @@ RideCRUDHelper >> addNewModelMethodTo: aClass for: aSymbol [ aClass organization classify: #newModel under: 'actions' ] +{ #category : #actions } +RideCRUDHelper >> addNewTemplateFor: aSymbol [ + + self subclassResponsibility +] + +{ #category : #actions } +RideCRUDHelper >> addPluralCRUDMethodsTo: aClass for: aSymbol [ + + self addPluralIndexMethodTo: aClass for: aSymbol. + "self addIndexMethodTo: aClass for: aSymbol." + self addModelsAccessorMethodTo: aClass for: aSymbol. + "self addShowMethodTo: aClass for: aSymbol. + self addNewModelMethodTo: aClass for: aSymbol. + self addEditMethodTo: aClass for: aSymbol. + self addCreateMethodTo: aClass for: aSymbol. + self addUpdateMethodTo: aClass for: aSymbol. + self addDestroyMethodTo: aClass for: aSymbol" +] + +{ #category : #actions } +RideCRUDHelper >> addPluralCRUDTemplatesTo: aClass for: aSymbol [ + + self addPluralIndexTemplateFor: aSymbol. + "self addShowTemplateFor: aSymbol. + self addNewTemplateFor: aSymbol. + self addEditTemplateFor: aSymbol. + self addFormNewTemplateFor: aSymbol. + self addFormEditTemplateFor: aSymbol. + self addFormFieldsTemplateFor: aSymbol." + self addPluralModelDisplayContentFor: aSymbol +] + +{ #category : #actions } +RideCRUDHelper >> addPluralIndexMethodTo: aClass for: aSymbol [ + + aClass compile: (self class pluralIndexMethodFor: aSymbol). + aClass organization classify: #index under: 'actions' +] + +{ #category : #actions } +RideCRUDHelper >> addPluralIndexTemplateFor: aClass for: aSymbol [ + + aClass compile: (self class pluralIndexMethodFor: aSymbol). + aClass organization classify: #index under: 'actions' +] + +{ #category : #actions } +RideCRUDHelper >> addPluralModelDisplayContentFor: aSymbol [ + + self subclassResponsibility +] + { #category : #actions } RideCRUDHelper >> addShowMethodTo: aClass for: aSymbol [ +self deprecated: 'no'. + aClass compile: (self class singularIndexMethodFor: aSymbol). + aClass organization classify: #index under: 'actions' +] + +{ #category : #actions } +RideCRUDHelper >> addShowTemplateFor: aSymbol [ +self deprecated: 'no'. + self subclassResponsibility +] + +{ #category : #actions } +RideCRUDHelper >> addSingularCRUDMethodsTo: aClass for: aSymbol [ + + self addAllowedParamsMethodTo: aClass for: aSymbol. + self addSingularIndexMethodTo: aClass for: aSymbol. + self addNewModelMethodTo: aClass for: aSymbol. + self addEditMethodTo: aClass for: aSymbol. + self addCreateMethodTo: aClass for: aSymbol. + self addUpdateMethodTo: aClass for: aSymbol. + self addDestroyMethodTo: aClass for: aSymbol +] - aClass compile: (self class showMethodFor: aSymbol). - aClass organization classify: #show under: 'actions' +{ #category : #actions } +RideCRUDHelper >> addSingularCRUDTemplatesTo: aClass for: aSymbol [ + + self addSingularIndexTemplateFor: aSymbol. + "self addShowTemplateFor: aSymbol." + self addNewTemplateFor: aSymbol. + self addEditTemplateFor: aSymbol. + self addFormNewTemplateFor: aSymbol. + self addFormEditTemplateFor: aSymbol. + self addFormFieldsTemplateFor: aSymbol. + self addModelDisplayContentFor: aSymbol +] + +{ #category : #actions } +RideCRUDHelper >> addSingularIndexMethodTo: aClass for: aSymbol [ + + aClass compile: (self class singularIndexMethodFor: aSymbol). + aClass organization classify: #index under: 'actions' +] + +{ #category : #actions } +RideCRUDHelper >> addSingularIndexTemplateFor: aClass for: aSymbol [ + + aClass compile: (self class singularIndexMethodFor: aSymbol). + aClass organization classify: #index under: 'actions' ] { #category : #actions } @@ -180,5 +411,14 @@ RideCRUDHelper >> addUpdateMethodTo: aClass for: aSymbol [ { #category : #actions } RideCRUDHelper >> for: aSymbol [ - doer model for: aSymbol + builder model for: aSymbol. + builder presenter for: aSymbol. + self addSingularCRUDMethodsTo: (self presenterForModel: aSymbol) for: aSymbol. + self addSingularCRUDTemplatesTo: (self presenterForModel: aSymbol) for: aSymbol. + + builder pluralPresenter for: aSymbol. + self addPluralCRUDMethodsTo: (self pluralPresenterForModel: aSymbol) for: aSymbol. + self addPluralCRUDTemplatesTo: (self pluralPresenterForModel: aSymbol) for: aSymbol + + ] diff --git a/Ride-Builder/RideCompositeHelper.class.st b/Ride-Builder/RideCompositeHelper.class.st index 0531b64..082811f 100644 --- a/Ride-Builder/RideCompositeHelper.class.st +++ b/Ride-Builder/RideCompositeHelper.class.st @@ -6,7 +6,7 @@ Class { #superclass : #Object, #instVars : [ 'js', - 'doer' + 'builder' ], #category : #'Ride-Builder' } @@ -14,13 +14,13 @@ Class { { #category : #accessing } RideCompositeHelper >> builder [ - ^ doer + ^ builder ] { #category : #accessing } RideCompositeHelper >> doer: anObject [ - doer := anObject + builder := anObject ] { #category : #actions } @@ -41,22 +41,28 @@ RideCompositeHelper >> initialize [ RideCompositeHelper >> mustacheTemplateFor: aSymbol [ | view | - view := doer view mustache. + view := builder view mustache. view js: js. view for: aSymbol ] +{ #category : #accessing } +RideCompositeHelper >> pluralPresenterForModel: aSymbol [ + + ^ Smalltalk at: (builder pluralPresenter classNameFor: aSymbol) asSymbol +] + { #category : #accessing } RideCompositeHelper >> presenterForModel: aSymbol [ - ^ Smalltalk at: (doer presenter classNameFor: aSymbol) asSymbol + ^ Smalltalk at: (builder presenter classNameFor: aSymbol) asSymbol ] { #category : #actions } RideCompositeHelper >> sttTemplateFor: aSymbol [ | view | - view := doer view stt. + view := builder view stt. view js: js. view for: aSymbol ] diff --git a/Ride-Builder/RideMVPCRUDHelper.class.st b/Ride-Builder/RideMVPCRUDHelper.class.st index d2a634b..6ba82b1 100644 --- a/Ride-Builder/RideMVPCRUDHelper.class.st +++ b/Ride-Builder/RideMVPCRUDHelper.class.st @@ -7,19 +7,90 @@ Class { #category : #'Ride-Builder' } -{ #category : #actions } -RideMVPCRUDHelper >> for: aSymbol [ - - super for: aSymbol. - doer presenter for: aSymbol. - self addCRUDMethodsTo: (self presenterForModel: aSymbol) for: aSymbol. - doer view stt - js: js; - crudIndexFor: aSymbol; - crudModelFor: aSymbol; - crudShowFor: aSymbol; - crudNewModelFor: aSymbol; - crudEditFor: aSymbol; - crudFormFor: aSymbol; - yourself +{ #category : #adding } +RideMVPCRUDHelper >> addEditTemplateFor: aSymbol [ + + builder view stt + js: js; + crudEditFor: aSymbol +] + +{ #category : #adding } +RideMVPCRUDHelper >> addFormEditTemplateFor: aSymbol [ + + builder view stt + js: js; + crudFormEditFor: aSymbol +] + +{ #category : #adding } +RideMVPCRUDHelper >> addFormFieldsTemplateFor: aSymbol [ + + builder view stt + js: js; + crudFormFieldsFor: aSymbol +] + +{ #category : #adding } +RideMVPCRUDHelper >> addFormNewTemplateFor: aSymbol [ + + builder view stt + js: js; + crudFormNewFor: aSymbol +] + +{ #category : #adding } +RideMVPCRUDHelper >> addIndexTemplateFor: aSymbol [ +self deprecated: 'singular or plural'. + builder view stt + js: js; + crudIndexFor: aSymbol +] + +{ #category : #adding } +RideMVPCRUDHelper >> addModelDisplayContentFor: aSymbol [ + + builder view stt + js: js; + crudModelDisplayContentFor: aSymbol +] + +{ #category : #adding } +RideMVPCRUDHelper >> addNewTemplateFor: aSymbol [ + + builder view stt + js: js; + crudNewFor: aSymbol +] + +{ #category : #adding } +RideMVPCRUDHelper >> addPluralIndexTemplateFor: aSymbol [ + + builder view stt + js: js; + crudPluralIndexFor: aSymbol asPlural +] + +{ #category : #adding } +RideMVPCRUDHelper >> addPluralModelDisplayContentFor: aSymbol [ + + builder view stt + js: js; + crudPluralModelDisplayContentFor: aSymbol +] + +{ #category : #adding } +RideMVPCRUDHelper >> addShowTemplateFor: aSymbol [ + + builder view stt + js: js; + crudShowFor: aSymbol +] + +{ #category : #adding } +RideMVPCRUDHelper >> addSingularIndexTemplateFor: aSymbol [ + + builder view stt + js: js; + crudIndexFor: aSymbol ] diff --git a/Ride-Builder/RideMVPHelper.class.st b/Ride-Builder/RideMVPHelper.class.st index c69e60f..960bfad 100644 --- a/Ride-Builder/RideMVPHelper.class.st +++ b/Ride-Builder/RideMVPHelper.class.st @@ -11,7 +11,7 @@ Class { RideMVPHelper >> crud [ ^ RideMVPCRUDHelper new - doer: doer; + doer: builder; yourself ] @@ -19,9 +19,9 @@ RideMVPHelper >> crud [ RideMVPHelper >> for: aSymbol [ | view | - doer model for: aSymbol. - doer presenter for: aSymbol. - view := doer view stt. + builder model for: aSymbol. + builder presenter for: aSymbol. + view := builder view stt. view js: js. view for: aSymbol ] diff --git a/Ride-Builder/RideModelHelper.class.st b/Ride-Builder/RideModelHelper.class.st index 4631c64..2b346a2 100644 --- a/Ride-Builder/RideModelHelper.class.st +++ b/Ride-Builder/RideModelHelper.class.st @@ -30,8 +30,7 @@ RideModelHelper >> targetedSuperclassFor: aSymbol [ | className | className := '{1}Model' format: { RideBuilder defaultPackageName }. - ^ Smalltalk - at: className asSymbol - ifAbsent: [ - RideBoilerplateError signal: '{1} class is missing in this image' ] + ^ Smalltalk at: className asSymbol ifAbsent: [ + RideBoilerplateError signal: + ('{1} class is missing in this image' format: { className }) ] ] diff --git a/Ride-Builder/RideMustacheHelper.class.st b/Ride-Builder/RideMustacheHelper.class.st index cd31788..6529104 100644 --- a/Ride-Builder/RideMustacheHelper.class.st +++ b/Ride-Builder/RideMustacheHelper.class.st @@ -64,12 +64,6 @@ RideMustacheHelper class >> formMustacheTemplateContent: aSymbol [ ' format: { aSymbol asPlural asSnakeCase } ] -{ #category : #accessing } -RideMustacheHelper class >> getPathFor: aSymbol [ - - ^ Ride resource viewsDirectory / aSymbol asPlural asSnakeCase -] - { #category : #accessing } RideMustacheHelper class >> indexMustacheTemplateContent: aSymbol [ diff --git a/Ride-Builder/RidePluralPresenterHelper.class.st b/Ride-Builder/RidePluralPresenterHelper.class.st new file mode 100644 index 0000000..27ed760 --- /dev/null +++ b/Ride-Builder/RidePluralPresenterHelper.class.st @@ -0,0 +1,17 @@ +Class { + #name : #RidePluralPresenterHelper, + #superclass : #RidePresenterHelper, + #category : #'Ride-Builder' +} + +{ #category : #accessing } +RidePluralPresenterHelper >> classNameFor: aSymbol [ + + ^ '{1}Presenter' format: { aSymbol asPlural } +] + +{ #category : #actions } +RidePluralPresenterHelper >> defaultCommentsFor: aSymbol [ + + ^ 'I am the presenter for lists of {1} models' format: { aSymbol asString } +] diff --git a/Ride-Builder/RidePresenterHelper.class.st b/Ride-Builder/RidePresenterHelper.class.st index 36589d8..c061579 100644 --- a/Ride-Builder/RidePresenterHelper.class.st +++ b/Ride-Builder/RidePresenterHelper.class.st @@ -7,10 +7,33 @@ Class { #category : #'Ride-Builder' } +{ #category : #accessing } +RidePresenterHelper class >> indexMethodFor: aSymbol [ + + ^ 'index + + "GET /{1}/" + + model := {2} findId: (self currentRequestParam: #id). + model ifNil: self modelNotFoundHandler. + + ^ self render + ' format: { + aSymbol asPlural asSnakeCase. + aSymbol } +] + +{ #category : #accessing } +RidePresenterHelper >> addIndexMethodTo: aClass for: aSymbol [ + + aClass compile: (self class indexMethodFor: aSymbol). + aClass organization classify: #index under: 'accessing' +] + { #category : #accessing } RidePresenterHelper >> classNameFor: aSymbol [ - ^ '{1}Presenter' format: { aSymbol asPlural } + ^ '{1}Presenter' format: { aSymbol } ] { #category : #actions } @@ -19,6 +42,15 @@ RidePresenterHelper >> defaultCommentsFor: aSymbol [ ^ 'I am the presenter for {1} models' format: { aSymbol asString } ] +{ #category : #actions } +RidePresenterHelper >> for: aSymbol [ + + | appClass | + appClass := super for: aSymbol. + + self addIndexMethodTo: appClass for: aSymbol +] + { #category : #accessing } RidePresenterHelper >> targetPackageNameTag [ @@ -28,10 +60,10 @@ RidePresenterHelper >> targetPackageNameTag [ { #category : #accessing } RidePresenterHelper >> targetedSuperclassFor: aSymbol [ - | className | - className := '{1}Presenter' format: { RideBuilder defaultPackageName }. - ^ Smalltalk - at: className asSymbol - ifAbsent: [ - RideBoilerplateError signal: '{1} class is missing in this image' ] + | targetClassName | + targetClassName := '{1}Presenter' format: + { Ride builder packageName }. + ^ Smalltalk at: targetClassName asSymbol ifAbsent: [ + RideBoilerplateError signal: + ('{1} was not found in the image' format: { targetClassName }) ] ] diff --git a/Ride-Builder/RideSTTemplateHelper.class.st b/Ride-Builder/RideSTTemplateHelper.class.st index e91df82..03eb50e 100644 --- a/Ride-Builder/RideSTTemplateHelper.class.st +++ b/Ride-Builder/RideSTTemplateHelper.class.st @@ -6,3 +6,406 @@ Class { #superclass : #RideAbstractTemplateHelper, #category : #'Ride-Builder' } + +{ #category : #accessing } +RideSTTemplateHelper class >> defaultViewTemplate [ + + ^ '
+

Find the source of the <%= self class name%> template in

<%= (Ride resource viewsDirectory / self class getModelName asSnakeCase) pathString %>

+
' +] + +{ #category : #accessing } +RideSTTemplateHelper class >> defaultViewTemplateWithJs [ + + ^ self defaultViewTemplate + , '' +] + +{ #category : #accessing } +RideSTTemplateHelper class >> editContentFor: aSymbol [ + + ^ '<% STT yield: self using: ''shared/feedback.html'' %> +
+

Editing {3}

+
+ <%= self render: self using: ''formEdit.html''%> +
+ Show this {3} + Back to {2} +
+' format: { + aSymbol asPlural. + aSymbol asPlural uncapitalized. + aSymbol uncapitalized. + aSymbol asPlural asSnakeCase } +] + +{ #category : #accessing } +RideSTTemplateHelper class >> formEditContentFor: aSymbol [ + + ^ '
+ + + <%= self render: model using: ''formFields.html''%> + +
+' format: { + aSymbol asPlural. + aSymbol asPlural uncapitalized. + aSymbol uncapitalized. + aSymbol asPlural asSnakeCase. + aSymbol } +] + +{ #category : #accessing } +RideSTTemplateHelper class >> formFieldsContentFor: aSymbol [ + + ^ ' + + +' format: { + aSymbol asPlural. + aSymbol asPlural uncapitalized. + aSymbol uncapitalized. + aSymbol asPlural asSnakeCase. + aSymbol } +] + +{ #category : #accessing } +RideSTTemplateHelper class >> formNewContentFor: aSymbol [ + + ^ '
+ + + + <%= self render: model using: ''formFields.html''%> + +
+' format: { + aSymbol asPlural. + aSymbol asPlural uncapitalized. + aSymbol uncapitalized. + aSymbol asPlural asSnakeCase. + aSymbol } +] + +{ #category : #accessing } +RideSTTemplateHelper class >> indexContentFor: aSymbol [ + + ^ '<% STT yield: self using: ''shared/feedback.html'' %> +
+

{1}

+ <%= self render: model using: ''{3}.html''%> +
+ +
+ Back to {2} +
+' format: { + aSymbol. + aSymbol asPlural uncapitalized. + aSymbol uncapitalized. + aSymbol asPlural asSnakeCase } +] + +{ #category : #accessing } +RideSTTemplateHelper class >> modelDisplayContentFor: aSymbol [ + + ^ ' +

Title:
<%= self title%>

+' format: { + aSymbol asPlural. + aSymbol asPlural uncapitalized. + aSymbol uncapitalized. + aSymbol asPlural asSnakeCase. + aSymbol } +] + +{ #category : #accessing } +RideSTTemplateHelper class >> newContentFor: aSymbol [ + + ^ '<% STT yield: self using: ''shared/feedback.html'' %> +
+

New {3}

+
+ <%= self renderUsing: ''formNew.html'' %> +
+ Back to {2} +
+' format: { + aSymbol asPlural. + aSymbol asPlural uncapitalized. + aSymbol uncapitalized. + aSymbol asPlural asSnakeCase } +] + +{ #category : #accessing } +RideSTTemplateHelper class >> pluralIndexContentFor: aSymbol [ + + ^ '<% STT yield: self using: ''shared/feedback.html'' %> +
+

{1}

+
+ New {3} +
    + <% self {2} do: [ :{3} | %> +
  • + <%= self render: {3} using: ''{3}.html''%> + +
  • + <% ] %> +
+
+
+' format: { + aSymbol. + aSymbol asSnakeCase. + aSymbol uncapitalized asSingular } +] + +{ #category : #accessing } +RideSTTemplateHelper class >> showContentFor: aSymbol [ +self deprecated: 'no used anymore'. + ^ '<%= self render: self using: ''shared/feedback.html'' %> +
+

{1}

+ <%= self render: model %> +
+ +
+ Back to {2} +
+' format: { + aSymbol. + aSymbol asPlural uncapitalized. + aSymbol uncapitalized. + aSymbol asPlural asSnakeCase. + aSymbol asPlural } +] + +{ #category : #actions } +RideSTTemplateHelper >> crudEditFor: aSymbol [ + + "Creates the template file for editing a model named aSymbol. + Find it as edit.html.stt" + + self + template: #edit + for: aSymbol + contents: (self class editContentFor: aSymbol) +] + +{ #category : #actions } +RideSTTemplateHelper >> crudFormEditFor: aSymbol [ + + "Creates the template file for the view showing the form that updates an existing model named aSymbol. + Find it as formEdit.html.stt" + + self + template: #formEdit + for: aSymbol + contents: (self class formEditContentFor: aSymbol) +] + +{ #category : #actions } +RideSTTemplateHelper >> crudFormFieldsFor: aSymbol [ + + "Creates the template file for the view showing the common fields in the forms + typically used in creating and editing aSymbol models. + Find it as formFields.html.stt" + + self + template: #formFields + for: aSymbol + contents: (self class formFieldsContentFor: aSymbol) +] + +{ #category : #actions } +RideSTTemplateHelper >> crudFormNewFor: aSymbol [ + + "Creates the template file for the view showing the form that creates a new model named aSymbol. + Find it as formNew.html.stt" + + self + template: #formNew + for: aSymbol + contents: (self class formNewContentFor: aSymbol) +] + +{ #category : #actions } +RideSTTemplateHelper >> crudIndexFor: aSymbol [ + + "Creates the template file for the default view of the model named aSymbol. + Find it as index.html.stt" + + self + template: #index + for: aSymbol + contents: (self class indexContentFor: aSymbol) +] + +{ #category : #actions } +RideSTTemplateHelper >> crudModelDisplayContentFor: aSymbol [ + + "Creates the template file for the view showing one model named aSymbol. + Find it as .html.stt" + + self + template: aSymbol uncapitalized asSnakeCase + for: aSymbol + contents: (self class modelDisplayContentFor: aSymbol) +] + +{ #category : #actions } +RideSTTemplateHelper >> crudNewFor: aSymbol [ + + "Creates the template file for the view showing many models named aSymbol. + Find it as show.html.stt" + + self + template: #new + for: aSymbol + contents: (self class newContentFor: aSymbol) +] + +{ #category : #actions } +RideSTTemplateHelper >> crudPluralIndexFor: aSymbol [ + + "Creates the template file for the default view of the model named aSymbol. + Find it as index.html.stt" + + self + template: #index + for: aSymbol + contents: (self class pluralIndexContentFor: aSymbol) +] + +{ #category : #actions } +RideSTTemplateHelper >> crudPluralModelDisplayContentFor: aSymbol [ + + "Creates the template file for the view showing + one model named with the value of aSymbol. + Find it as .html.stt" + + + | targetPath | + "Find the right path for the view template" + targetPath := (self class getPathFor: aSymbol asPlural) + asFileReference + / ('{1}.html.stt' format: { aSymbol asSnakeCase }). + self + template: aSymbol asSnakeCase + path: targetPath + contents: (self class modelDisplayContentFor: aSymbol) +] + +{ #category : #actions } +RideSTTemplateHelper >> crudShowFor: aSymbol [ + + "Creates the template file for the view showing many models named aSymbol. + Find it as show.html.stt" + + self + template: #show + for: aSymbol + contents: (self class showContentFor: aSymbol) +] + +{ #category : #'instance creation' } +RideSTTemplateHelper >> for: aSymbol [ + + | targetPath rendered | + "Find the right path for the view template" + targetPath := (self class getPathFor: aSymbol) asFileReference. + + "Verify lack of preexistence to protect possible existing work." + targetPath exists ifTrue: [ + RideBoilerplateError signal: + ('The template for {1} already exists in {2}' format: { + aSymbol. + targetPath pathString }) ]. + + "Create the template file using the default template for a model" + rendered := js + ifTrue: [ self class defaultViewTemplateWithJs ] + ifFalse: [ self class defaultViewTemplate ]. + + (targetPath / 'index.html.stt') ensureCreateFile writeStreamDo: [ + :stream | stream nextPutAll: rendered ]. + + js ifTrue: [ + (targetPath / 'index.js') ensureCreateFile writeStreamDo: [ :stream | + stream nextPutAll: (self class defaultJavaScriptFor: aSymbol) ] ] +] + +{ #category : #actions } +RideSTTemplateHelper >> template: aTemplateSelectorName for: aSymbol contents: aString [ + + | targetPath | + "Find the right path for the view template" + targetPath := (self class getPathFor: aSymbol) asFileReference + / ('{1}.html.stt' format: { aTemplateSelectorName }). + + self + template: aTemplateSelectorName + path: targetPath + contents: aString +] + +{ #category : #actions } +RideSTTemplateHelper >> template: aTemplateSelectorName path: targetPath contents: aString [ + + | templateContentString | + "Verify lack of preexistence to protect possible existing work." + targetPath exists ifTrue: [ + RideBoilerplateError signal: + ('The template {1} already exists in {2}' format: { + aTemplateSelectorName. + targetPath pathString }) ]. + + "Create the template file adding js if it has a related one and asked for it." + templateContentString := aString. + js ifTrue: [ + templateContentString := String streamContents: [ :s | + s + nextPutAll: templateContentString; + cr; + nextPutAll: + self class loadCustomJsScriptElement ] ]. + + targetPath ensureCreateFile writeStreamDo: [ :stream | + stream nextPutAll: templateContentString ]. + + js ifTrue: [ + (targetPath / ('{1}.js' format: { aTemplateSelectorName })) + ensureCreateFile writeStreamDo: [ :stream | + stream nextPutAll: + (self class defaultJavaScriptFor: aTemplateSelectorName) ] ] +] diff --git a/Ride-Builder/RideSTTemplateHelperReview.class.st b/Ride-Builder/RideSTTemplateHelperReview.class.st deleted file mode 100644 index 7ddfae3..0000000 --- a/Ride-Builder/RideSTTemplateHelperReview.class.st +++ /dev/null @@ -1,335 +0,0 @@ -" -I help with the boilerplate of views made out of STTemplate -" -Class { - #name : #RideSTTemplateHelperReview, - #superclass : #RideAbstractTemplateHelper, - #category : #'Ride-Builder' -} - -{ #category : #accessing } -RideSTTemplateHelperReview class >> defaultJavaScriptFor: aSymbol [ - - ^ 'window.addEventListener("load", (event) => \{ - console.log("{1} initialized"); -\}); -' format: { aSymbol } -] - -{ #category : #accessing } -RideSTTemplateHelperReview class >> defaultViewTemplate [ - - ^ '

{{title}}

' -] - -{ #category : #accessing } -RideSTTemplateHelperReview class >> defaultViewTemplateWithJs [ - - ^ '

{{title}}

-' -] - -{ #category : #accessing } -RideSTTemplateHelperReview class >> editMustacheTemplateContent: aSymbol [ - - ^ '

Editing {1}

-\{\{>form{1}\}\} -
- -' format: { - aSymbol. - aSymbol asPlural asSnakeCase. - aSymbol uncapitalized. - aSymbol asPlural uncapitalized } -] - -{ #category : #accessing } -RideSTTemplateHelperReview class >> formMustacheTemplateContent: aSymbol [ - - ^ '
- \{\{>userFeedback\}\} -
-
-
-
-