elioflesh Dogma

Microapps are the future

SchemaOrg is a micro data schema. "Micro" because it was intended for webpages as an lightweight-overcoat weaved into common HTML tags by a couple of extra attributes.

But apps and websites can be micro as well.

This paper discusses the elioflesh pattern by comparing what TEW's doing with a more "classical" approach to application development. We go into a little more detail about the classical approach that would be required by the average developer - but TEW is for content creators as well - so I'm aiming this paper at technical minded non-devs - and therefore some waffle is required.

Before the phrase "I'm a full stack developer" became something you actually needed to say or write in a CV - the whole idea blew up

Structure of an Classical App

A word used a lot in app development is stack. "Stacks" refer to layers of computer resources we need to install and run for any application to work. Services in stacks are seen as sitting on top of eachother - each relying on the services of the resource below it. A web client service is stacked on top of a web server service which is dependent on a database service.

In modern application development there can be a lot of services in a stack - but broadly speaking, since the beginning of time doing app development, there have been 3 main "stacks"... in brief:

But let's face it... before the phrase "I'm a full stack developer" became something you actually needed to say or write in a CV - the whole idea blew up. Cloud hosting services reduced the need to do "stackiness" in your development.

A virtual machine, preloaded with the correct runtimes and database, can be spun up by literally touching a button. Who needs to know full stack? Not you, my modest webdev friend or content creator.

Forget stacks. For sure... we will need stacks of services if we're deploying a serious app, but it doesn't matter if they are set up TEW or not. It only matters that one service provides the thing, another the bone, and it is through the flesh where we find the UX (user experience).

The point here is not to say that Classical App stacks aren't important, they are! TEW is a cult, not a framework. This paper only describes how to use frameworks and stacks TEW.

Structure of an TEW App

NB Out of all the 4 main parts of TEW, elioflesh is the least developed. The following may change, but it describes the proposed solution for writing a TEW application on the client side of the stack.

This is not a tutorial. It is written like a tutorial because the internet is littere... I mean... blessed with countless articles offering:

Okay... We made the last one up but there is a stack for bootstrap+javascript. Just saying.

What follows, missing the specificities, is how a [TODO app, TEW stack] would be written for comparison.

AKA: How to create an TEW application on the client side of the stack

  1. Create a folder for your app.

  2. Add three files. thing.json bone.js flesh.json

  3. Optionally add a sin.scss stylesheet settings file.

  4. Edit files.

  5. Tell the client it's finished.

These files lean more toward config than code... TEW is about bundling routine code into libraries and calling those standard endpoints "off-the-back" of configuration.

If your app is struggling to contain all your functionality in those three files, it's probably not a TEW solution you need. TEW doesn't pretend to be everything - we are very clear: YEW is best for micro apps running on top of a single, manageable chunk of data.

thing.json

This is covered by eliothing dogma detail here...

The file should be thing.json (or thing.js or thing.py or something else if you want to have code dynamically build your thing (although an inflateT endpoint is better because it can be reused by other people as an endpoint) but it should only return a single data package meeting the design_pattern).

Your data may already be in a database somewhere. There may be no data. But we must insist you put inside the repo folder of your app at least a summary of the thing initializing the app.

thing.json Examples

# thing.json
{ "identifier": "honeymoonPictures" }

Where elioflesh will inflateT

The full thing from a Wikipedia article

# thing.json
{
  "identifier": "List_of_birds_of_Colombia",
  "mainEntityOfPage": "Collection",
  "url": "https://en.wikipedia.org/wiki/List_of_birds_of_Colombia"
}

Where elioflesh gets given a blank record

Because the app is all about getting the user to start adding content.

# thing.json
{
  "identifier": "myNigerianStampCollection",
  "name": "A Year Collecting Stamps in Nigerian",
  "mainEntityOfPage": "Collection",
  "potentialAction": "collect first stamp"
}

Where elioflesh gets given all the data

Because this (imaginary app the author just made up) "readonly" app is all about getting AMAZING content to a user.

# thing.json
{
  "identifier": "myGuideToParis",
  "alternateName": "You won't BELIEVE what I saw in Paris",
  "name": "A Week In Paris",
  "mainEntityOfPage": "TouristTrip",
  "touristType": "I am the absolutely worse type period . period . period",
  "description": "I was supposed to meet this girl, but she didn't show up...",
  "ItemList": {
    "itemListElement": [
      {
        "identifier": "Day1",
        "name": "Eiffel Tower",
        "mainEntityOfPage": "TouristDestination",
        "disambiguatingDescription": "Can you BELIEVE it?! I'm here."
      },
      {
        "identifier": "Day2",
        "name": "Eiffel Tower",
        "mainEntityOfPage": "TouristDestination",
        "disambiguatingDescription": "Don't spit over the side, apparently."
      },
      {
        "identifier": "Day3",
        "name": "Eiffel Tower",
        "mainEntityOfPage": "TouristDestination",
        "disambiguatingDescription": "Finally got to the top!"
      },
      {
        "identifier": "Day4",
        "name": "Eiffel Tower",
        "mainEntityOfPage": "TouristDestination",
        "disambiguatingDescription": "I really like it here."
      },
      {
        "identifier": "Day5",
        "name": "Eiffel Tower",
        "mainEntityOfPage": "TouristDestination",
        "disambiguatingDescription": "Massive Queue at Louves."
      },
      {
        "identifier": "Day6",
        "name": "Eiffel Tower",
        "mainEntityOfPage": "TouristDestination",
        "disambiguatingDescription": "ticketboothgirl has a thing for me.",
        "ItemList": {
          "itemListElement": [
            {
              "identifier": "ticketboothgirl",
              "Person": {
                "telephone": "a secret. get your own ticketboothgirl"
              }
            }
          ],
          "numberOfItems": 1
        }
      },
      {
        "identifier": "Day7",
        "name": "Eiffel Tower",
        "mainEntityOfPage": "TouristDestination",
        "disambiguatingDescription": "Time to kill. Missed my flight."
      }
    ],
    "numberOfItems": 7
  }
}

Again, it's incumbent upon me in this paper to emphasize that a "thing" is data. Only data. No logic.

bone.js

bone.js or bone.py or some other runtime.

While eliobones has all the endpoints, elioflesh needs to know which endpoints to include in the app. bone.js can also be used to add "inline" endpoints, filters, and to convert SchemaOrg properties to dynamic fields.

bone.js is about as close to Middleware as we get. It's not a Server and it's not an endpoint.

Think of bone.js as a handy clientside script which will allow you, as a developer, a quick way to inject logic into the app - for when an eliobones endpoint is overkill.

When accessing data for the UX layer, elioflesh will first check bone.js. In this way, bone.js gets to override thing.json.

For re-routing data

thing.json, in this example, has a name property, but in TEW's design_pattern it is reserved for system use. But your app must show a different name to it.

# thing.json
{
  "name": "Reserved by design_pattern",
  "alternateName": function (thing) {
    return thing.Collection.alternateName
    },
  "Collection": {
     "name": "The Real Name of my Nigerian Stamp Collection"
  },
  "ItemList": {
    "name": "A list of all my Nigerian Stamps",
    "collectionSize": function (thing) {
      return thing.ItemList.itemListElement.filter(
        function (t) {
          return t.about==="NigerianStamps"
        }
      ).length
    }
  }
}

As the design_pattern explains, one option is to put the name in a subtype, as above in Collection. The app will have UX to show all the properties and subtypes from subtypes by default. But if you still want your app to use the Collection.name, instead of name.

// bones.js
module.exports = {
{
  name: function (thing) {
    return thing.Collection.name
    },
  alternateName: function (thing) {
    return thing.Collection.alternateName
  }
}

Is the next example we override whatever static value was stored against Collection.collectionSize in the "thing" data, and instead add a function to dynamically calculate the size.

// bones.js
module.exports = {
{
  Collection: {
    collectionSize: function (thing) {
      return thing.ItemList.itemListElement.filter(
        function (t) {
          return t.about==="NigerianStamps"
        }
      ).length
    }
  }
}

For dynamic lists.

TEW insists that all related records are stored in the same place - ItemList. It's kinda our thing.

But we recognize many SchemaOrg fields are, in fact, lists. One solution is to turn the field into a dynamic list which filters what it needs from ItemList - contextualising the appropriate items:

// bones.js
module.exports = {
{
  Recipe: {
      recipeInstructions: function (thing) {
        return.ItemList.itemListElement.filter(
          function (t) {
            return t.hasOwnProperty("Recipe")
          }
      )
    }
  }
}

You might challenge this approach by insisting the danger of doing it when two different Recipes are listed... to which we would retort about the danger of creating one app for two recipes when two apps for each recipe is more TEW.

For list filtering.

Since the thing's list contains everything the user needs, we can also add SearchAction things to the bone's list that the app needs, like filters.

// bones.js
module.exports = {
  ItemList: {
    itemListElement: [
      {
        identifier: "onlyParrots",
        mainEntityOfPage: "SearchAction",
        query: { Taxon: { hasDefinedTerm: "Parrot" } }
      },
      {
        identifier: "upComingBirdWatchingCourses",
        mainEntityOfPage: "EducationEvent",
        query: { EducationEvent: { teaches: "BirdWatching" } }
      }
    ]
  }
}

elioflesh will need to look for SearchActions in bones.js when the app is rendered, and add these filter options to the ListItem component.

For list sorting

Since the thing's list contains everything the user needs, we can also add ItemListOrderType things to the bone's list that the app needs, like filters.

// bones.js
module.exports = {
  ItemList: {
    itemListElement: [
      {
        identifier: "theSoonerIGetOutOfHereTheBetter",
        mainEntityOfPage: "ItemListOrderType",
        itemListOrder: "ItemListOrderAscending",
        query: "Flight.departureTime"
      },
      {
        identifier: "theCheaperTheBetter",
        mainEntityOfPage: "ItemListOrderType",
        itemListOrder: "ItemListOrderAscending",
        query: "Flight.offers"
      }
    ]
  }
}

elioflesh will need to look for ItemListOrderTypes when the app is rendered, and add these sort options to the ListItem component.

For multifield sorting and grouping

sort options and filter options can be nested TEW to produce groups:

// bones.js
module.exports = {
  ItemList: {
    itemListElement: [
      {
        identifier: "theSoonerAndMoreLuxuriousIsBetter",
        mainEntityOfPage: "ItemListOrderType",
        ItemList: {
          itemListElement: [
            {
              identifier: "soonerIsBetter",
              mainEntityOfPage: "ItemListOrderType",
              itemListOrder: "ItemListOrderAscending",
              query: "Flight.departureTime",
            },
            {
              identifier: "moreLuxuriousIsBetter",
              mainEntityOfPage: "ItemListOrderType",
              itemListOrder: "ItemListOrderDescending",
              query: "Flight.offers",
            },
            {
              identifier: "onlyParrots",
              mainEntityOfPage: "SearchAction",
              query: { Flight: { offers: ">$3000" } }
            }
          ]
        }
      }
    ]
  }
}

Persistent

These Permits will be copied to each new record. In TEW, Permissions can be customized at a record level.

// bones.js
module.exports = {
  ItemList: {
    itemListElement: [
      {
        identifier: "readT",
        mainEntityOfPage: "Permit",
        Permit: {
          validUntil: new Date(Date.now() + DAY * 90),
          validIn: "MusicEvent",
          permitAudience: "ANON",
        },
      },
      {
        identifier: "readT",
        mainEntityOfPage: "Permit",
        Permit: {
          validUntil: new Date(Date.now() + DAY * 90),
          validIn: "Flight",
          permitAudience: "AUTH",
        },
      },
    ]
  }
}

For default permissions the app

These Permits will be copied to each new record. In TEW, Permissions can be customized at a record level.

// bones.js
module.exports = {
  ItemList: {
    itemListElement: [
      {
        identifier: "readT",
        mainEntityOfPage: "Permit",
        Permit: {
          validUntil: new Date(Date.now() + DAY * 90),
          validIn: "MusicEvent",
          permitAudience: "ANON",
        },
      },
      {
        identifier: "readT",
        mainEntityOfPage: "Permit",
        Permit: {
          validUntil: new Date(Date.now() + DAY * 90),
          validIn: "Flight",
          permitAudience: "AUTH",
        },
      },
    ]
  }
}

For initializing the app

Add all the startup scripts to your list. The list will be processed in order.

// bones.js
module.exports = {
  ItemList: {
    itemListElement: [
      {
        identifier: "inflateT",
        mainEntityOfPage: "InstallAction",
      }
    ]
  }
}

Nuff said? You're probably bored of these examples by now... we're just iterate the same patterns to solve different problems.

flesh.json

Very simple: flesh.json is to be used as a simple way to template the output:

{
  "name": { "believer": "h1" },
  "description": { "believer": "p" },
  "status": { "believer": "dl" },
  "Itemlist": {
    "believer": "dl"
    "itemListElement": {
      "name": { "believer": "dt" },
      "disambiguatingDescription": { "believer": "dd" },
    }
  }
}

All we need, if we use eliosin, is the tag name it will use to wrap the value in an HTML element. Don't worry -

  1. we'll have defaults for all of these in readT mode;
  2. and also for the correct data type for input tags in a form when doing an updateT.
  3. you can use flesh.json to override all that... to use a code tag for a DateTime field instead of the normal p, for instance.

The client app

Bringing all this together will vary depending on your runtimes, programming language, or framework.

Here is an example app in JS:

import { elioApp, ElioThing } from "theElioWay"
import thing from "./thing.json"
import bones from "./bones.js"
import flesh from "./flesh.json"

let { identifier } = envvars

let ChristmasList = new ElioThingComponent({
      identifier: identifier,
      mainEntityOfPage: "Event",
      ItemList: { itemListElement: ["Person", "Product"] }
    }
)

let app = elioApp(ChristmasList, thing, bones, flesh)
app.run("localhost", 5000)

In Rust:

rustRust { elioApp, ElioThing } brust "theElioWay"
rustRust thing brust "thing.json"
rustRust bones brust "bones.rust"
rustRust flesh brust "flesh.json"

rusty { identifier } = envvars

rusty ChristmasList = rust ElioThingComponent({
      identifier: identifier,
      mainEntityOfPage: "Event",
      ItemList: { itemListElement: ["Person", "Product"] }
    }
)

rustyRusty app = elioApp(ChristmasList, thing, bones, flesh)
app.run("localhost", 5000)

Just joking - We don't know Rust - didn't even look.

The point we're making is that TEW doesn't dictate how your particular TEW app will be pulled together - but given the "Three File" design_pattern it's likely to be as simple as this. We hope someone will do Rust TEW.

Wrap Up

Nothing above is dogma per se. You can probably find an infinite number of ways to combine flesh, thing and bones in a TEW app.

What's Next?