- https://medium.com/edonec/implement-redux-saga-in-your-react-typescript-project-1d79c4a2d726
- https://github.com/maprihoda/react-redux-crud
- https://blog.crunchydata.com/blog/generating-json-directly-from-postgres
- https://claritydev.net/blog/speed-up-your-react-developer-workflow-with-code-g/
- https://ejs.co/#install
- https://www.jondjones.com/frontend/react/npm-packages-for-react-developers/how-to-build-a-form-in-react-using-react-json-schema-form/
To generate sagas crud through the following pipeline:
- User generates postgresql database from
rails db:create
- Database structure is exported as JSON. (Article 3.)
- JSON is ejs input. Ejs generates sagas crud (Articles 1., 2.).
I was going to use either plop
or yeoman
, but I decided not to because promps are an unnecessary hinderance.
I will write the templates and file distribution scripts myself.
- Found the articles to show that it's all possible.
- Trying to geneate a single table from
rails c
that contains all possible data types. - All possible data types: https://guides.rubyonrails.org/v3.2/migrations.html#supported-types
- How to generate: https://riptutorial.com/ruby-on-rails/example/9630/rails-generate-scaffold
- Came up with
$ rails g scaffold testtypes binary:binary boolean:boolean date:date datetime:datetime decimal:decimal float:float integer:integer string:string text:text time:time timestamp:timestamp
- Remember to switch to
postgresql
indatabase.yml
and addinggem 'pg'
to yourGemfile
:
# SQLite. Versions 3.8.0 and up are supported.
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
# gem 'sqlite3'
#
default: &default
adapter: postgresql
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
development:
<<: *default
database: db/development
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: db/test
production:
<<: *default
database: db/production
- From Article 3, using
psql -d db/development
on Ubuntu:
db/development=# \o database.json
db/development=#
WITH rows AS (
SELECT c.relname, a.attname, a.attnotnull, a.attnum, t.typname
FROM pg_class c
JOIN pg_attribute a
ON c.oid = a.attrelid and a.attnum >= 0
JOIN pg_type t
ON t.oid = a.atttypid
JOIN pg_namespace n
ON c.relnamespace = n.oid
WHERE n.nspname = 'public'
AND c.relkind = 'r'
),
agg AS (
SELECT rows.relname, json_agg(rows ORDER BY attnum) AS attrs
FROM rows
GROUP BY rows.relname
)
SELECT json_object_agg(agg.relname, agg.attrs)
FROM agg;
- Then you can clean up the json with
$ cat database.json | sed '1,2d' | sed '$d' | sed '$d' | sed 's/+//g' > db.json
- Result:
{
"ar_internal_metadata":[
{
"relname":"ar_internal_metadata",
"attname":"key",
"attnotnull":true,
"attnum":1,
"typname":"varchar"
},
{
"relname":"ar_internal_metadata",
"attname":"value",
"attnotnull":false,
"attnum":2,
"typname":"varchar"
},
{
"relname":"ar_internal_metadata",
"attname":"created_at",
"attnotnull":true,
"attnum":3,
"typname":"timestamp"
},
{
"relname":"ar_internal_metadata",
"attname":"updated_at",
"attnotnull":true,
"attnum":4,
"typname":"timestamp"
}
],
"schema_migrations":[
{
"relname":"schema_migrations",
"attname":"version",
"attnotnull":true,
"attnum":1,
"typname":"varchar"
}
],
"testtypes":[
{
"relname":"testtypes",
"attname":"id",
"attnotnull":true,
"attnum":1,
"typname":"int8"
},
{
"relname":"testtypes",
"attname":"updown",
"attnotnull":false,
"attnum":2,
"typname":"bytea"
},
{
"relname":"testtypes",
"attname":"yesno",
"attnotnull":false,
"attnum":3,
"typname":"bool"
},
{
"relname":"testtypes",
"attname":"appointment",
"attnotnull":false,
"attnum":4,
"typname":"date"
},
{
"relname":"testtypes",
"attname":"event",
"attnotnull":false,
"attnum":5,
"typname":"timestamp"
},
{
"relname":"testtypes",
"attname":"number",
"attnotnull":false,
"attnum":6,
"typname":"numeric"
},
{
"relname":"testtypes",
"attname":"precise",
"attnotnull":false,
"attnum":7,
"typname":"float8"
},
{
"relname":"testtypes",
"attname":"natural",
"attnotnull":false,
"attnum":8,
"typname":"int4"
},
{
"relname":"testtypes",
"attname":"name",
"attnotnull":false,
"attnum":9,
"typname":"varchar"
},
{
"relname":"testtypes",
"attname":"story",
"attnotnull":false,
"attnum":10,
"typname":"text"
},
{
"relname":"testtypes",
"attname":"due",
"attnotnull":false,
"attnum":11,
"typname":"time"
},
{
"relname":"testtypes",
"attname":"limit",
"attnotnull":false,
"attnum":12,
"typname":"timestamp"
},
{
"relname":"testtypes",
"attname":"created_at",
"attnotnull":true,
"attnum":13,
"typname":"timestamp"
},
{
"relname":"testtypes",
"attname":"updated_at",
"attnotnull":true,
"attnum":14,
"typname":"timestamp"
}
]
}
- Now we can transform this into a React form (and later a React functional component with hooks), by using
react-json-schema-form
(article 6.). I start by writing an .ejs script to generate the widget correspondence hash:
const formCorrespondence = {<% testtypes.forEach((i, t) => { %>
'<%= i.attname %>': { 'type':'<%= i.typname %>', 'form':'template.ejs' },<% }); %>
};
- The result of
npx ejs string.ejs -f db.json
is:
const formCorrespondence = {
'id': { 'type':'int8', 'form':'template.ejs' },
'binary': { 'type':'bytea', 'form':'template.ejs' },
'boolean': { 'type':'bool', 'form':'template.ejs' },
'date': { 'type':'date', 'form':'template.ejs' },
'datetime': { 'type':'timestamp', 'form':'template.ejs' },
'decimal': { 'type':'numeric', 'form':'template.ejs' },
'float': { 'type':'float8', 'form':'template.ejs' },
'integer': { 'type':'int4', 'form':'template.ejs' },
'string': { 'type':'varchar', 'form':'template.ejs' },
'text': { 'type':'text', 'form':'template.ejs' },
'time': { 'type':'time', 'form':'template.ejs' },
'timestamp': { 'type':'timestamp', 'form':'template.ejs' },
'created_at': { 'type':'timestamp', 'form':'template.ejs' },
'updated_at': { 'type':'timestamp', 'form':'template.ejs' },
};
- This is the
formify.ejs
generator that turns db.json into areact-json-schema-form
form object:
[
<% const formCorrespondence = {
'int8': { 'ARtype':'id', 'form':'integer' },
'bytea': { 'ARtype':'binary', 'form':'radio' },
'bool': { 'ARtype':'boolean', 'form':'checkbox' },
'date': { 'ARtype':'date', 'form':'date' },
'timestamp': { 'ARtype':'datetime', 'form':'datetime' },
'numeric': { 'ARtype':'decimal', 'form':'number' },
'float8': { 'ARtype':'float', 'form':'number' },
'int4': { 'ARtype':'integer', 'form':'integer' },
'varchar': { 'ARtype':'string', 'form':'string' },
'text': { 'ARtype':'text', 'form':'text' },
'time': { 'ARtype':'time', 'form':'time' },
};
%>
<% for(const table in data) { %>
{
"title": "<%= table %>",
"description": "A simple form example.",
"type": "object",
"properties": {
<% data[table].forEach((f, i)=>{ %>
<%- include(`./${formCorrespondence[f.typname].form}`, {name: f.attname}) %>
<% }); %>
}
},
<% } %>
]
- The output of
npx ejs formify.ejs -f db2.json > jsonforms.json
is
[
{
"title":"ar_internal_metadata",
"description":"A simple form example.",
"type":"object",
"properties":{
"key":{
"type":"string",
"title":"key"
},
"value":{
"type":"string",
"title":"value"
},
"created_at":{
"type":"string",
"format":"date-time",
"title":"created_at"
},
"updated_at":{
"type":"string",
"format":"date-time",
"title":"updated_at"
}
}
},
{
"title":"schema_migrations",
"description":"A simple form example.",
"type":"object",
"properties":{
"version":{
"type":"string",
"title":"version"
}
}
},
{
"title":"testtypes",
"description":"A simple form example.",
"type":"object",
"properties":{
"id":{
"title":"id",
"type":"integer"
},
"binary":{
"type":"boolean",
"title":"binary",
"description":""
},
"boolean":{
"type":"boolean",
"title":"boolean",
"description":""
},
"date":{
"type":"string",
"format":"date",
"title":"date"
},
"datetime":{
"type":"string",
"format":"date-time",
"title":"datetime"
},
"decimal":{
"title":"decimal",
"type":"number"
},
"float":{
"title":"float",
"type":"number"
},
"integer":{
"title":"integer",
"type":"integer"
},
"string":{
"type":"string",
"title":"string"
},
"text":{
"type":"string",
"title":"text"
},
"time":{
"type":"string",
"format":"date-time",
"title":"time"
},
"timestamp":{
"type":"string",
"format":"date-time",
"title":"timestamp"
},
"created_at":{
"type":"string",
"format":"date-time",
"title":"created_at"
},
"updated_at":{
"type":"string",
"format":"date-time",
"title":"updated_at"
}
}
}
]