A service here is not a class you must inherit from or a folder you must arrange. It is whichever of five shapes fits what you are actually doing — and you can mix all five in one application, because they all answer the same way.
Every one of these is reachable at the same address, called the same way, and gives back the same kind of answer. You are choosing how to write it, never how it behaves.
func add a, b return a + b
ringserv serve calc.ring — and every function in the file is
callable, its arguments matched to your request by name. Nothing else in
the file.
:orders = [ :place = func aReq { return Reply(:ok, [ :id = 7 ]) } ]
Named actions under a named service. The shortest form that still takes a request and decides what to do with it.
class OrdersService func placeAction aReq return Reply(:ok, ...) func helper # private
Only methods ending in Action can be reached from outside.
Everything else stays yours — privacy by naming, not by configuration.
:menu = [ :table = "menu" ]
One line, and you have list, get, create, update and delete — with paging and filters. Write any one of them yourself and yours wins; the rest keep working.
:receipt = [ :js = "receipt.js" ]
A .js file, running inside the same one file, under the same rules
— and it can call your Ring services by name, and they can call it.
What the
JavaScript side can and cannot do.
Write the rule once and it is checked before your code ever runs, so your function never receives something it did not ask for:
Contract([ :orders = [ :place = [ :client = [ :type = :text, :required = 1 ], :qty = [ :type = :number, :min = 1 ] ] ] ])
A request that breaks the rules is refused with every problem listed at once — not the first one, then the next one after you fix it. Anyone who has filled in a form three times to find three separate mistakes knows why that matters.
Declare your tables in the same place you declare your services, and the database is created for you the first time it runs. It is a real SQL database inside the same file — nothing to install and nothing to connect to.
Some things must never be quietly edited: a sale, a payment, a decision. For those there is a second kind of store — a record that only ever grows. Nothing in it can be changed or removed, each entry is sealed against the one before it, and the server can tell you at any moment whether the chain is still whole. A cancellation is a new entry saying "cancelled, and why", never an erasure. That is what makes the record worth keeping.
$ ringserv check app.ring
Reads your application without running it and tells you what will not work: a mistake in the code, a rule that names an action you never wrote, an action nothing can ever reach. Cheaper than finding out from a user.
And ringserv test runs your own tests against a scratch
database that is thrown away afterwards, so testing can never touch real data.
Nothing above says whether a service runs on the server or inside the browser. That is a separate decision, written separately — and changing it is one word, with no change to the code that does the work. One model, two players shows how.
When it is ready: put it up.