diff --git a/CHANGELOG.md b/CHANGELOG.md index 3da476e..af6fd51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ folgen [Semantic Versioning](https://semver.org/lang/de/). Die Version steht in ## [Unveröffentlicht] +## [0.3.0] - 2026-10-11 + +### Hinzugefügt +- `demo/testall.cmds` enthält zu jedem der 134 Befehle ein ausführbares Beispiel, + gegliedert durch Überschriften (`##`-Zeilen werden mit ausgegeben, `#`-Zeilen + sind stille Kommentare). Ein Test prüft, dass kein Befehl fehlt. + +### Geändert +- `testall` läuft in einer temporären Kopie von `demo/`; schreibende Beispiele + (`append`, `delete`, `close` ...) verändern die Demodaten nicht mehr. Die + Befehle lesen von einer leeren Standardeingabe. +- `sale` nimmt das Datum aus `NEXTDATE` (wie `getjournal`, `close`), sonst von heute. +- `lock` führt die Sperrdatei pro Pfad statt pro Dateiname, so stören sich + gleichnamige Tabellen in verschiedenen Ordnern nicht. + ## [0.2.0] - 2026-10-11 ### Hinzugefügt diff --git a/README.md b/README.md index 1b34ec3..82fa721 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,21 @@ whatwill total Befehle, die zu einem Stichwort passen commands Tabelle aller Befehle ``` +## Beispiele zu allen Befehlen + +`demo/testall.cmds` enthält zu jedem Befehl ein Beispiel mit kurzer Erklärung. +`testall` führt es in einer Kopie von `demo/` aus und zeigt jede Zeile samt Ausgabe: + +``` +testall | less alle Beispiele ansehen +testall > testall.old Lauf als Vergleich festhalten +testall | diff testall.old - später prüfen, was sich geändert hat (mise run testall) +``` + +Eine Zeile mit `##` erscheint als Überschrift in der Ausgabe, eine mit `#` ist +ein stiller Kommentar. Neue Befehle brauchen hier ein Beispiel, sonst schlägt der Test +`test_testall_mentions_every_command` fehl. + ## Befehle | Gruppe | Befehle | @@ -138,7 +153,7 @@ lib/rdb/table.rb Tabellen und Listen lesen und schreiben lib/rdb/fast.rb Schnellzugriff über Byte-Offsets lib/rdb/cmd/*.rb die Befehle, nach Gruppen man/ Man-Pages (troff-Quellen aus den MWC-Archiven) -demo/ Beispieltabellen, testall.cmds und testall.old +demo/ Beispieltabellen; testall.cmds = je ein Beispiel pro Befehl, testall.old = letzter Lauf test/ Tests ``` diff --git a/demo/testall.cmds b/demo/testall.cmds index 5deba78..d7e7669 100644 --- a/demo/testall.cmds +++ b/demo/testall.cmds @@ -1,14 +1,259 @@ -# One command per line; testall runs them in demo/ and prints the output. -# Save a known-good run: testall > testall.old ; later: testall | diff testall.old - +# testall.cmds -- one shell command per line; `testall` runs them in a scratch copy of demo/. +# ## line is printed as a heading/explanation # line is a silent comment +# Save a known-good run: testall > testall.old later: testall | diff testall.old - +# Tables used here: inventory journal expense carexpense parent female male owners +# chart ledger parttable cash X83 colors orders ed.t maillist form name + +## +## ===== Selecting and arranging rows and columns ===== +## row (select): keep the rows that match a condition. Column names work like awk variables. row 'Cost > 5' < inventory -column Cost Amount Description < inventory +rdb select 'Description ~ /gloves/' < inventory +## column (project): pick columns in any order; a name that does not exist becomes a new empty column +column Description Cost < inventory +project Item Notes < owners +## sorttable: -n numeric, -r reverse, -f fold case sorttable -nr Cost < inventory +## compute: calculate columns from other columns (the new column must exist, so make it with addcol) +addcol Total < inventory | compute 'Total = Amount * Cost' | column Item Total compute 'Value = sprintf("%5.2f", Cost * Amount)' < inventory -total Amount Value < inventory -datatype -l < inventory -subtotal Date Debit Credit < journal -jointable parent female +## addcol / rename +addcol Supplier < owners +rename Owner Person < owners +## number / Number: number the rows (number: rows only, Number: rows and the head) +number < owners +Number < owners +## record: print one record (the 3rd row) +record 3 < inventory +## rmblank: drop empty rows; headon / headoff: add or strip the head +column Item Cost < inventory | rmblank +headoff < colors +headoff < colors | headon Hue +printf 'a\tb\n1\t2\n' | insertdash +## splittable: cut a table horizontally into pieces of n rows (writes colorsaa, colorsab ...) +splittable -3 colors ; ls colors* ; cat colorsab + +## ===== Combining tables ===== +## jointable: join on the first column; -a keeps unmatched rows, -j names the key columns +jointable inventory owners +jointable -a inventory owners +jointable -j1 Parent -j2 Name parent female +## union / intersect / difference union expense carexpense +intersect female male +difference female male +## paste.rdb: put tables side by side +paste.rdb female male + +## ===== Statistics: one result row per column ===== +total Amount Value < inventory +mean Cost < inventory +maximum Cost < inventory +minimum Cost < inventory +width < inventory +precision Cost < inventory +datatype -l < inventory +widest < inventory +subtotal Date Debit Credit < journal +dbdict < inventory +schema inventory owners +tableorlist inventory maillist + +## ===== Checking and displaying ===== +check.rdb < inventory +printf 'a\tb\n-\t-\n1\t2\t3\n4\n' | check.rdb +display < owners +see < owners +validate 'Cost > 40 { print "expensive:", Description }' < badinventory +column Colors < orders | domain colors +domain colors black x +## fd: does Description depend on Item? (prints true/false) +fd Item Description < inventory +fd Description Cost < inventory + +## ===== Text tools (arguments or standard input) ===== justify < inventory -tabletolist < inventory +justify -r Description < inventory +justify -c Cost < inventory +trim < inventory +compresss < inventory +pad -4 < owners +printf 'hello world\n' | cap +uppercase hello world +lowercase HELLO WORLD +printf 'one two\n' | uppercase +substitute gloves mittens colors ; cat colors +trimblank ' padded ' +padstring 8 abc +padstring -8 abc +length 'how long?' +filesize inventory +ascii aA1 +ascii ' ' +chr 72 105 +word < ed.t +cap < ed.t +blank < maillist +printf 'a\n' | prompt 'prompt text' +not false && echo 'false failed, so not succeeded' +path ls > /dev/null && echo 'ls found on the path' +## tabletom4 / tabletosed make translation files from ed.t; translate applies them +tabletom4 < ed.t +tabletosed < ed.t +tabletom4 < ed.t > ed ; echo 'I love you.' | translate ed +## uniondict builds the ed define-file out of ed.low and ed.allcap +printf 'English\tDeutsch\n-------\t-------\nlove\tliebe\n' > ed.low ; printf 'English\tDeutsch\n-------\t-------\nLOVE\tLIEBE\n' > ed.allcap ; uniondict ed + +## ===== Conversions ===== +tabletolist < owners +tabletolist -l < owners +tabletolist < owners | listtotable +listtotable < maillist +listtosh < maillist +tabletofix Item=4 Owner=6r < owners +tabletofix Item=4 Owner=6r < owners | fixtotable Item=1,4 Owner=5,6 +tabletostruct tag owners < owners +tabletotbl < owners +tabletofact parent +tabletorule ruletable + +## ===== Dates ===== +## julian: dates to day numbers (arithmetic); gregorian: back again (-e European, -u US, -c day count) julian Date < journal +julian Date < journal | gregorian Date +julian Date < journal | gregorian -e Date +## computedate: a date plus n days +computedate 890331 45 +computedate 891231 1 +test $(todaysdate | wc -c) -eq 7 && echo 'todaysdate prints YYMMDD' + +## ===== Names that sound alike ===== +soundex < name +## like: names that sound like Mann (needs the soundex table name.x, sorted by Soundex) +soundex < name > name.x ; like Mann name +hashkey 11 ABCD +hashkey 29 abcdefg + +## ===== Fast access: index, search, seek ===== +## search without an index scans the table (-ms); keys come from stdin +echo scales | search inventory Description +## -h leaves out the head line of the answer +printf 'Description\n-----------\nscales\n' | search -h inventory Description +## build indexes in each method: b binary, r record, i inverted, h hash +index -mr inventory ; ls inventory.r ; echo 3 | search -mr inventory +index -mi inventory Description ; ls inventory.i ; echo cl | search -mi -x inventory Description +index -mh inventory Item ; ls inventory.h ; echo 5 | search -mh inventory Item +## seek: where does the row start and end? (byte offsets) +echo 5 | seek inventory Item +echo 5 | seek -mh inventory Item +## searchtree: is Sally an ancestor of Jane? (needs a table of Parent, Child) +searchtree parent Sally Jane Parent Child +## vindex: look-up tables for ve +vindex owners Item : Owner ; vindex owners +tableorlist owners maillist + +## ===== Changing tables: append, replace, delete, enter ===== +## append adds a row and updates the indexes +## -h: the new row has no head line +printf '8\t1\t1.00\t1.00\tnew thing\n' | append -h -mh inventory Item ; echo 8 | search -mh inventory Item +## replace overwrites bytes from..to; take the offsets from seek +set -- $(echo 3 | seek inventory Item) ; printf '3\t6\t8.00\t48.00\tclamps\n' | replace inventory $1 $2 ; echo 3 | search inventory Item +## delete blanks the row and fixes the index +echo 4 | delete -mh inventory Item ; echo 4 | search -mh inventory Item ; echo 5 | search -mh inventory Item +## enter reads rows field by field from the terminal; -1 means one row +printf '9\n2\n3\n4\nnewer thing\n' | enter -1 inventory > /dev/null +tail -1 inventory +## lock / unlock: the second process may not lock the same bytes +lock inventory 111 10 20 0 0 ; echo "first lock status $?" +lock inventory 222 15 25 0 0 ; echo "second lock status $?" +unlock inventory 111 10 20 0 0 ; echo "unlock status $?" +lock inventory 222 15 25 0 0 ; echo "second lock now status $?" ; unlock inventory 222 15 25 0 0 +## vilock edits a table under a lock; update.rdb edits records interactively +echo | vilock inventory ; echo "vilock status $?" +## update.rdb with a key on stdin and no changes (an empty line ends the dialog) +printf '3\n\n' | update.rdb inventory Item | head -8 +## ve is a line editor in this version +printf 'q\n' | ve inventory | head -3 + +## ===== Reports and forms ===== +## report fills a form with the fields of each list record () +cat form +report form < maillist +## label prints mailing labels, letter prints form letters +label < maillist +letter form < maillist | head -5 +## screen turns a form into a shell script that asks for the fields +screen < form | head -6 +## reportwriter: a sample report with page headings (the date is cut out) +reportwriter -3 Prices < inventory | grep -v 'Report Date' +## tabletom4 / tabletosed / tabletostruct were shown above + +## ===== Help and the terminal ===== +rdb --version +howmany +whatis row +whatwill total +helpme +commands | head -5 +man -g column | head -12 +termput cl | cat -v +termput cm | cat -v +tset vt100 | head -3 +cursor 3 8 | cat -v +clear.rdb | cat -v +rmcore ; echo "rmcore status $?" +mkdir t1 ; cp owners t1 ; cpdir.rdb t1 t2 ; ls t2 +menu < /dev/null | head -3 +timesearch > /dev/null ; echo "timesearch status $?" +testsearch | tail -3 +act + +## ===== /act: a small accounting system ===== +## (the commands work on tables in the current directory; NEXTDATE fixes the date they use) +act +## cashflow: compute the Balance column of a cash table +cashflow < cash +## tax: look up the tax for an income in a tax table +tax 20000 < X83 +## explode: parts list -> all subparts with counts (part, amount) +explode 10001 2 parttable +## chartdup: look for duplicate account numbers and short names in chart +printf 'Account\tShort\n-------\t-----\n1\ta\n1\tb\n2\ta\n' > chart ; chartdup +## foot: subtotals of Debit and Credit per account in the ledger +foot +## adjust, balance, close: need a trial balance and an adjusting journal +printf 'Account\tDebit\tCredit\tFormat\tTaxline\tShort\tName\n-------\t-----\t------\t------\t-------\t-----\t----\n1010\t11178\t\tB-A\t4L01\tcash\tCash\n3010\t\t13500\tI-I\t101a\tsales\tSales\n' > trialbalance +printf 'Account\tDebit\tCredit\tFormat\tTaxline\tName\n-------\t-----\t------\t------\t-------\t----\n1010\t100\t\tB-A\t4L01\tCash\n' > journaladjust +adjust ; cat adjustedtrial +balance ; sed 2d balancesheet +NEXTDATE=870101 close ; cat journalnext +## fillform: sum the adjusted trial balance per tax line into a tax form +printf 'Taxline\tDescription\n-------\t-----------\n4L01\tCash on hand\n101a\tGross sales\n' > taxform.f1 +fillform f1 ; cat f1 +## calculate: compute columns of a form; f1.calc has one 'Column = expression' per line +echo 'Amount = Amount * 2' > f1.calc ; calculate f1 ; cat f1 +## getjournal / consolidate: collect subsidiary journals into the general journal +printf 'Date\tAmount\tAccount\tRef\tDescription\n----\t------\t-------\t---\t-----------\n870101\t100\t4010\ts1\tshop sale\n' > journalsales +NEXTDATE=870131 getjournal journalsales +printf 'Date\tAmount\tAccount\tRef\tDescription\n----\t------\t-------\t---\t-----------\n870102\t40\t5010\tp1\tpaper\n' > journalexpense +rm journal ; consolidate 870131 ; cat journal +## payroll: calcpay sorts journalpay into ledgerpay, w2 totals per employee +printf 'Number\tGross\tFederal\tState\tNet\n------\t-----\t-------\t-----\t---\n2\t1000\t100\t50\t850\n1\t900\t90\t40\t770\n2\t1000\t100\t50\t850\n' > journalpay +calcpay ; cat ledgerpay +w2 + +## ===== /act: sales and stock ===== +printf 'Code\tName\tPrice\n----\t----\t-----\n10010\twidget\t2.50\n10020\tgadget\t10\n' > product +printf 'Number\tCompany\tLast\n------\t-------\t----\n7\tAcme\tRoe\n' > customer +## sale: enter a sale order (customer, then code and quantity per item, then y to confirm) +printf '7\n10010\n4\n10020\n1\n\ny\n\n' | NEXTDATE=870105 sale +cat salesorder +## invoice / cstate: print an invoice or customer statement for what was sold +printf '1\n\n' | invoice +printf '7\n\n' | cstate +## bom: parts needed for everything sold (needs the table part with Code, Subpart, Count) +printf 'Code\tSubpart\tCount\n----\t-------\t-----\n10010\ts1\t3\n10020\ts1\t1\n10020\ts2\t2\n' > part +bom ; cat bom +## update.inv: change the stock of one item, with record locking (INV names the table) +cp inventory inv ; printf '7\n9\n\n' | INV=inv update.inv | tail -n +2 +grep '^7' inv diff --git a/demo/testall.old b/demo/testall.old index 93281cc..8599f9a 100644 --- a/demo/testall.old +++ b/demo/testall.old @@ -1,19 +1,34 @@ +## +## ===== Selecting and arranging rows and columns ===== +## row (select): keep the rows that match a condition. Column names work like awk variables. $ row 'Cost > 5' < inventory Item Amount Cost Value Description ---- ------ ---- ----- ----------- 3 5 8.00 40.00 clamps 6 89 14.75 1312.75 bunsen burners 7 5 175.00 875.00 scales -$ column Cost Amount Description < inventory -Cost Amount Description ----- ------ ----------- -5.00 3 rubber gloves -0.50 100 test tubes -8.00 5 clamps -1.98 23 plates -2.45 99 cleaning cloth -14.75 89 bunsen burners -175.00 5 scales +$ rdb select 'Description ~ /gloves/' < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +1 3 5.00 15.00 rubber gloves +## column (project): pick columns in any order; a name that does not exist becomes a new empty column +$ column Description Cost < inventory +Description Cost +----------- ---- +rubber gloves 5.00 +test tubes 0.50 +clamps 8.00 +plates 1.98 +cleaning cloth 2.45 +bunsen burners 14.75 +scales 175.00 +$ project Item Notes < owners +Item Notes +---- ----- +1 +3 +6 +## sorttable: -n numeric, -r reverse, -f fold case $ sorttable -nr Cost < inventory Item Amount Cost Value Description ---- ------ ---- ----- ----------- @@ -24,6 +39,17 @@ Item Amount Cost Value Description 5 99 2.45 242.55 cleaning cloth 4 23 1.98 45.54 plates 2 100 0.50 50.00 test tubes +## compute: calculate columns from other columns (the new column must exist, so make it with addcol) +$ addcol Total < inventory | compute 'Total = Amount * Cost' | column Item Total +Item Total +---- ----- +1 15 +2 50 +3 40 +4 45.54 +5 242.55 +6 1312.75 +7 875 $ compute 'Value = sprintf("%5.2f", Cost * Amount)' < inventory Item Amount Cost Value Description ---- ------ ---- ----- ----------- @@ -34,10 +60,154 @@ Item Amount Cost Value Description 5 99 2.45 242.55 cleaning cloth 6 89 14.75 1312.75 bunsen burners 7 5 175.00 875.00 scales +## addcol / rename +$ addcol Supplier < owners +Item Owner Supplier +---- ----- -------- +1 ann +3 bob +6 cy +$ rename Owner Person < owners +Item Person +---- ------ +1 ann +3 bob +6 cy +## number / Number: number the rows (number: rows only, Number: rows and the head) +$ number < owners +number Item Owner +------ ---- ----- +1 1 ann +2 3 bob +3 6 cy +$ Number < owners +Number Item Owner +------ ---- ----- +1 1 ann +2 3 bob +3 6 cy +## record: print one record (the 3rd row) +$ record 3 < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +3 5 8.00 40.00 clamps +## rmblank: drop empty rows; headon / headoff: add or strip the head +$ column Item Cost < inventory | rmblank +Item Cost +---- ---- +1 5.00 +2 0.50 +3 8.00 +4 1.98 +5 2.45 +6 14.75 +7 175.00 +$ headoff < colors +black +blue +carmel +green +purple +red +silver +white +$ headoff < colors | headon Hue +Hue +--- +black +blue +carmel +green +purple +red +silver +white +$ printf 'a\tb\n1\t2\n' | insertdash +a b +- - +1 2 +## splittable: cut a table horizontally into pieces of n rows (writes colorsaa, colorsab ...) +$ splittable -3 colors ; ls colors* ; cat colorsab +colors +colorsaa +colorsab +colorsac +Colors +------ +green +purple +red +## ===== Combining tables ===== +## jointable: join on the first column; -a keeps unmatched rows, -j names the key columns +$ jointable inventory owners +Item Amount Cost Value Description Owner +---- ------ ---- ----- ----------- ----- +1 3 5.00 15.00 rubber gloves ann +3 5 8.00 40.00 clamps bob +6 89 14.75 1312.75 bunsen burners cy +$ jointable -a inventory owners +Item Amount Cost Value Description Owner +---- ------ ---- ----- ----------- ----- +1 3 5.00 15.00 rubber gloves ann +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps bob +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners cy +7 5 175.00 875.00 scales +$ jointable -j1 Parent -j2 Name parent female +Parent Child +------ ----- +Sally Fred +## union / intersect / difference +$ union expense carexpense +Date Amount Account Ref Description +---- ------ ------- --- ----------- +820107 14.00 meal v meal with jones +820113 101.62 car v car repairs +820114 81.80 insur c car insurance allstate +820113 101.62 car v car repairs +820114 81.80 insur c car insurance allstate +820114 93.00 car c car registration dmv +$ intersect female male +Name +---- +$ difference female male +Name +---- +Jane +Sally +## paste.rdb: put tables side by side +$ paste.rdb female male +Name Name +---- ---- +Jane Fred +Sally Mike +## ===== Statistics: one result row per column ===== $ total Amount Value < inventory Item Amount Cost Value Description ---- ------ ---- ----- ----------- 324 2580.84 +$ mean Cost < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- + 29.67 +$ maximum Cost < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- + 175.00 +$ minimum Cost < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- + 0.50 +$ width < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +4 6 6 7 14 +$ precision Cost < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- + 2 $ datatype -l < inventory Item Amount Cost Value Description ---- ------ ---- ----- ----------- @@ -50,25 +220,81 @@ Item Amount Cost Value Description 7 5 175.00 875.00 scales ---- ------ ---- ----- ----------- 0 0 2 2 -1 +$ widest < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +4 6 6 7 14 $ subtotal Date Debit Credit < journal Date Account Debit Credit Description ---- ------- ----- ------ ----------- 861222 25000 25000 861223 10000 10000 861224 30000 30000 -$ jointable parent female -Parent Child ------- ----- -Sally Fred -$ union expense carexpense -Date Amount Account Ref Description ----- ------ ------- --- ----------- -820107 14.00 meal v meal with jones -820113 101.62 car v car repairs -820114 81.80 insur c car insurance allstate -820113 101.62 car v car repairs -820114 81.80 insur c car insurance allstate -820114 93.00 car c car registration dmv +$ dbdict < inventory +field name +----- ---- +1 Item +2 Amount +3 Cost +4 Value +5 Description +$ schema inventory owners +Table Field Name +----- ----- ---- +inventory 1 Item +inventory 2 Amount +inventory 3 Cost +inventory 4 Value +inventory 5 Description +owners 1 Item +owners 2 Owner +$ tableorlist inventory maillist +table +list +## ===== Checking and displaying ===== +$ check.rdb < inventory + +$ printf 'a\tb\n-\t-\n1\t2\t3\n4\n' | check.rdb +check.rdb: Columns (3) do not equal headline columns (2) in row 1 (line 3). +1 2 3 +check.rdb: Columns (1) do not equal headline columns (2) in row 2 (line 4). +4 +$ display < owners +Item Owner +---- ----- +1 ann +3 bob +6 cy +$ see < owners +Item^IOwner$ +----^I-----$ +1^Iann$ +3^Ibob$ +6^Icy$ +$ validate 'Cost > 40 { print "expensive:", Description }' < badinventory +Item# Onhand Cost Value Description +----- ------ ---- ----- ----------- +expensive: rubber gloves +1 3 50 150 rubber gloves +expensive: test tubes +2 100 X 500 test tubes +expensive: clamps +3 -5 80 -400 clamps +4 23 19 437 plates +5 -99 24 -2376 cleaning cloth +$ column Colors < orders | domain colors +Colors +------ +farble +yellow +$ domain colors black x +x +## fd: does Description depend on Item? (prints true/false) +$ fd Item Description < inventory +true +$ fd Description Cost < inventory +true +## ===== Text tools (arguments or standard input) ===== $ justify < inventory Item Amount Cost Value Description ---- ------ ------ ------- -------------- @@ -79,50 +305,233 @@ Item Amount Cost Value Description 5 99 2.45 242.55 cleaning cloth 6 89 14.75 1312.75 bunsen burners 7 5 175.00 875.00 scales -$ tabletolist < inventory +$ justify -r Description < inventory +Item Amount Cost Value Description +---- ------ ---- ----- -------------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +$ justify -c Cost < inventory +Item Amount Cost Value Description +---- ------ ------ ----- ----------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +$ trim < inventory +Item Amount Cost Value Description +---- ------ ------ ------- -------------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +$ compresss < inventory +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +1 3 5.00 15.00 rubber gloves +2 100 0.50 50.00 test tubes +3 5 8.00 40.00 clamps +4 23 1.98 45.54 plates +5 99 2.45 242.55 cleaning cloth +6 89 14.75 1312.75 bunsen burners +7 5 175.00 875.00 scales +$ pad -4 < owners +Item Owner +---- ----- +1 ann +3 bob +6 cy +$ printf 'hello world\n' | cap +Hello World +$ uppercase hello world +HELLO WORLD +$ lowercase HELLO WORLD +hello world +$ printf 'one two\n' | uppercase +ONE TWO +$ substitute gloves mittens colors ; cat colors +Colors +------ +black +blue +carmel +green +purple +red +silver +white +$ trimblank ' padded ' +padded +$ padstring 8 abc + abc +$ padstring -8 abc +abc +$ length 'how long?' +9 +$ filesize inventory +263 +$ ascii aA1 +97 65 49 +$ ascii ' ' +32 +$ chr 72 105 +Hi +$ word < ed.t +Deutch +English +I +Ich +dich +liebe +love +widgit +you +$ cap < ed.t +English Deutch +------- ------ +I Ich +Love Liebe +You Dich +Widgit +$ blank < maillist + +Number +Name +Company +Street +City +State +ZIP +Phone + +Number +Name +Company +Street +City +State +ZIP +Phone + +$ printf 'a\n' | prompt 'prompt text' +prompt text +$ not false && echo 'false failed, so not succeeded' +false failed, so not succeeded +$ path ls > /dev/null && echo 'ls found on the path' +ls found on the path +## tabletom4 / tabletosed make translation files from ed.t; translate applies them +$ tabletom4 < ed.t +define(I,Ich) +define(love,liebe) +define(you,dich) +$ tabletosed < ed.t +s/I/Ich/g +s/love/liebe/g +s/you/dich/g +$ tabletom4 < ed.t > ed ; echo 'I love you.' | translate ed +Ich liebe dich. +## uniondict builds the ed define-file out of ed.low and ed.allcap +$ printf 'English\tDeutsch\n-------\t-------\nlove\tliebe\n' > ed.low ; printf 'English\tDeutsch\n-------\t-------\nLOVE\tLIEBE\n' > ed.allcap ; uniondict ed +define(love,liebe) +define(Love,Liebe) +define(LOVE,LIEBE) +## ===== Conversions ===== +$ tabletolist < owners Item 1 -Amount 3 -Cost 5.00 -Value 15.00 -Description rubber gloves - -Item 2 -Amount 100 -Cost 0.50 -Value 50.00 -Description test tubes +Owner ann Item 3 -Amount 5 -Cost 8.00 -Value 40.00 -Description clamps - -Item 4 -Amount 23 -Cost 1.98 -Value 45.54 -Description plates - -Item 5 -Amount 99 -Cost 2.45 -Value 242.55 -Description cleaning cloth +Owner bob Item 6 -Amount 89 -Cost 14.75 -Value 1312.75 -Description bunsen burners +Owner cy -Item 7 -Amount 5 -Cost 175.00 -Value 875.00 -Description scales +$ tabletolist -l < owners +Item 1 +Owner ann +Item 3 +Owner bob + +Item 6 +Owner cy + +$ tabletolist < owners | listtotable +Item Owner +---- ----- +1 ann +3 bob +6 cy +$ listtotable < maillist +Number Name Company Street City State ZIP Phone +------ ---- ------- ------ ---- ----- --- ----- +1 Ronald McDonald McDonalds 123 Mac Attack Memphis TENN 30000 (111) 222-3333 +2 Chiquita Banana United Brands Uno Avenido de la Reforma San Jose Costa Rica 123456789 1234 +$ listtosh < maillist +Number='1' +Name='Ronald McDonald' +Company='McDonalds' +Street='123 Mac Attack' +City='Memphis' +State='TENN' +ZIP='30000' +Phone='(111) 222-3333' +Number='2' +Name='Chiquita Banana' +Company='United Brands' +Street='Uno Avenido de la Reforma' +City='San Jose' +State='Costa Rica' +ZIP='123456789' +Phone='1234' +$ tabletofix Item=4 Owner=6r < owners +1 ann +3 bob +6 cy +$ tabletofix Item=4 Owner=6r < owners | fixtotable Item=1,4 Owner=5,6 +Item Owner +---- ----- +1 ann +3 bob +6 cy +$ tabletostruct tag owners < owners +struct tag +{ +char *Item; +char *Owner; +} owners [] = +{ "1","ann" }, +{ "3","bob" }, +{ "6","cy" } +; +$ tabletotbl < owners +.TS +n l. +Item Owner +_ +1 ann +3 bob +6 cy +.TE +$ tabletofact parent +parent(Sally,Fred). +parent(Mike,Fred). +parent(Fred,Jane). +$ tabletorule ruletable +mother(X,Y) :- female(X), parent(X,Y). +## ===== Dates ===== +## julian: dates to day numbers (arithmetic); gregorian: back again (-e European, -u US, -c day count) $ julian Date < journal Date Account Debit Credit Description ---- ------- ----- ------ ----------- @@ -134,3 +543,499 @@ Date Account Debit Credit Description 1752829 130 30000 inventory - parts from CCPSC 1752829 201.1 15000 accounts payable to CCPSC 1752829 101 15000 cash payment to CCPSC for parts +$ julian Date < journal | gregorian Date +Date Account Debit Credit Description +---- ------- ----- ------ ----------- +861222 101 25000 cash from loan +861222 211.1 25000 loan number #378-14 Bank Amerigold +861223 150.1 10000 test equipment from Zarkoff +861223 101 5000 cash payment +861223 211.2 5000 note payable to Zarkoff Equipment +861224 130 30000 inventory - parts from CCPSC +861224 201.1 15000 accounts payable to CCPSC +861224 101 15000 cash payment to CCPSC for parts +$ julian Date < journal | gregorian -e Date +Date Account Debit Credit Description +---- ------- ----- ------ ----------- +22/12/86 101 25000 cash from loan +22/12/86 211.1 25000 loan number #378-14 Bank Amerigold +23/12/86 150.1 10000 test equipment from Zarkoff +23/12/86 101 5000 cash payment +23/12/86 211.2 5000 note payable to Zarkoff Equipment +24/12/86 130 30000 inventory - parts from CCPSC +24/12/86 201.1 15000 accounts payable to CCPSC +24/12/86 101 15000 cash payment to CCPSC for parts +## computedate: a date plus n days +$ computedate 890331 45 +890515 +$ computedate 891231 1 +900101 +$ test $(todaysdate | wc -c) -eq 7 && echo 'todaysdate prints YYMMDD' +todaysdate prints YYMMDD +## ===== Names that sound alike ===== +$ soundex < name +Name Soundex +---- ------- +Abraham A165 +Abraham A165 +Adams A352 +Ackerman A265 +Manis M520 +Mann M500 +Miller M460 +Moon M500 +## like: names that sound like Mann (needs the soundex table name.x, sorted by Soundex) +$ soundex < name > name.x ; like Mann name +Name +---- +Mann +Moon +$ hashkey 11 ABCD +2 +$ hashkey 29 abcdefg +4 +## ===== Fast access: index, search, seek ===== +## search without an index scans the table (-ms); keys come from stdin +$ echo scales | search inventory Description +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +7 5 175.00 875.00 scales +## -h leaves out the head line of the answer +$ printf 'Description\n-----------\nscales\n' | search -h inventory Description +7 5 175.00 875.00 scales +## build indexes in each method: b binary, r record, i inverted, h hash +$ index -mr inventory ; ls inventory.r ; echo 3 | search -mr inventory +inventory.r +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +3 5 8.00 40.00 clamps +$ index -mi inventory Description ; ls inventory.i ; echo cl | search -mi -x inventory Description +inventory.i +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +3 5 8.00 40.00 clamps +5 99 2.45 242.55 cleaning cloth +$ index -mh inventory Item ; ls inventory.h ; echo 5 | search -mh inventory Item +inventory.h +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +5 99 2.45 242.55 cleaning cloth +## seek: where does the row start and end? (byte offsets) +$ echo 5 | seek inventory Item +172 204 0 8 +$ echo 5 | seek -mh inventory Item +172 204 113 122 +## searchtree: is Sally an ancestor of Jane? (needs a table of Parent, Child) +$ searchtree parent Sally Jane Parent Child +TRUE +## vindex: look-up tables for ve +$ vindex owners Item : Owner ; vindex owners +== Item-A +Item +---- +1 +3 +6 +== Item-B +Item Owner +---- ----- +1 ann +3 bob +6 cy +$ tableorlist owners maillist +table +list +## ===== Changing tables: append, replace, delete, enter ===== +## append adds a row and updates the indexes +## -h: the new row has no head line +$ printf '8\t1\t1.00\t1.00\tnew thing\n' | append -h -mh inventory Item ; echo 8 | search -mh inventory Item +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +8 1 1.00 1.00 new thing +## replace overwrites bytes from..to; take the offsets from seek +$ set -- $(echo 3 | seek inventory Item) ; printf '3\t6\t8.00\t48.00\tclamps\n' | replace inventory $1 $2 ; echo 3 | search inventory Item +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +3 6 8.00 48.00 clamps +## delete blanks the row and fixes the index +$ echo 4 | delete -mh inventory Item ; echo 4 | search -mh inventory Item ; echo 5 | search -mh inventory Item +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +Item Amount Cost Value Description +---- ------ ---- ----- ----------- +5 99 2.45 242.55 cleaning cloth +## enter reads rows field by field from the terminal; -1 means one row +$ printf '9\n2\n3\n4\nnewer thing\n' | enter -1 inventory > /dev/null + +$ tail -1 inventory +9 2 3 4 newer thing +## lock / unlock: the second process may not lock the same bytes +$ lock inventory 111 10 20 0 0 ; echo "first lock status $?" +first lock status 0 +$ lock inventory 222 15 25 0 0 ; echo "second lock status $?" +lock: inventory: bytes 15-25 are locked by process 111 +second lock status 1 +$ unlock inventory 111 10 20 0 0 ; echo "unlock status $?" +unlock status 0 +$ lock inventory 222 15 25 0 0 ; echo "second lock now status $?" ; unlock inventory 222 15 25 0 0 +second lock now status 0 +## vilock edits a table under a lock; update.rdb edits records interactively +$ echo | vilock inventory ; echo "vilock status $?" +vilock status 0 +## update.rdb with a key on stdin and no changes (an empty line ends the dialog) +$ printf '3\n\n' | update.rdb inventory Item | head -8 +> -- record 3 of 8 +1 Item 3 +2 Amount 6 +3 Cost 8.00 +4 Value 48.00 +5 Description clamps +> -- record 4 of 8 +1 Item 5 +## ve is a line editor in this version +$ printf 'q\n' | ve inventory | head -3 +-- inventory: record 1 of 9 +1 Item 1 +2 Amount 3 +## ===== Reports and forms ===== +## report fills a form with the fields of each list record () +$ cat form + + + +< City >, +Hi < Name >: + +$ report form < maillist +Ronald McDonald +McDonalds +123 Mac Attack +Memphis, TENN 30000 +Hi Ronald McDonald: + +Chiquita Banana +United Brands +Uno Avenido de la Reforma +San Jose, Costa Rica 123456789 +Hi Chiquita Banana: + +## label prints mailing labels, letter prints form letters +$ label < maillist +Ronald McDonald +McDonalds +123 Mac Attack +Memphis, TENN 30000 +Chiquita Banana +United Brands +Uno Avenido de la Reforma +San Jose, Costa Rica 123456789 +$ letter form < maillist | head -5 +Ronald McDonald +McDonalds +123 Mac Attack +Memphis, TENN 30000 + +## screen turns a form into a shell script that asks for the fields +$ screen < form | head -6 +: paint crt screen +exec 1>/dev/tty +cat < /dev/null ; echo "timesearch status $?" +timesearch status 0 +$ testsearch | tail -3 +-mh: ok (200 rows found) +-mi: ok (200 rows found) +-mr: ok (200 rows found) +$ act +act adjust balance bom calcpay +calculate cashflow chartdup close consolidate +cstate explode fillform foot getjournal +invoice sale tax update.inv w2 +## ===== /act: a small accounting system ===== +## (the commands work on tables in the current directory; NEXTDATE fixes the date they use) +$ act +act adjust balance bom calcpay +calculate cashflow chartdup close consolidate +cstate explode fillform foot getjournal +invoice sale tax update.inv w2 +## cashflow: compute the Balance column of a cash table +$ cashflow < cash +Date Amount Balance Description +---- ------ ------- ----------- +890101 512 512 current balance +890101 -450 62 rent check +890101 1000 1062 pay check +890115 -300 762 estimated tax payment +## tax: look up the tax for an income in a tax table +$ tax 20000 < X83 + 3369 +## explode: parts list -> all subparts with counts (part, amount) +$ explode 10001 2 parttable +Part Amount +---- ------ +10001 2 +10010 6 +10020 8 +10100 52 +## chartdup: look for duplicate account numbers and short names in chart +$ printf 'Account\tShort\n-------\t-----\n1\ta\n1\tb\n2\ta\n' > chart ; chartdup +Duplicate Account numbers in chart. See chart.Account +1 a +1 b +Duplicate Short names in chart. See chart.Short +1 a +2 a +## foot: subtotals of Debit and Credit per account in the ledger +$ foot +Account Date Debit Credit +------- ---- ----- ------ +101 820102 25000 +101 820103 5000 +101 820104 15000 +------- ---- ----- ------ +101 15000 30000 +130 820104 30000 +------- ---- ----- ------ +130 30000 0 +150.1 820103 10000 +------- ---- ----- ------ +150.1 10000 0 +201.1 820104 15000 +------- ---- ----- ------ +201.1 15000 0 +211.1 820102 25000 +------- ---- ----- ------ +211.1 0 25000 +211.2 820103 5000 +------- ---- ----- ------ +211.2 0 5000 +## adjust, balance, close: need a trial balance and an adjusting journal +$ printf 'Account\tDebit\tCredit\tFormat\tTaxline\tShort\tName\n-------\t-----\t------\t------\t-------\t-----\t----\n1010\t11178\t\tB-A\t4L01\tcash\tCash\n3010\t\t13500\tI-I\t101a\tsales\tSales\n' > trialbalance + +$ printf 'Account\tDebit\tCredit\tFormat\tTaxline\tName\n-------\t-----\t------\t------\t-------\t----\n1010\t100\t\tB-A\t4L01\tCash\n' > journaladjust + +$ adjust ; cat adjustedtrial +Account Debit Credit Format Taxline Short Name +------- ----- ------ ------ ------- ----- ---- +1010 11278 B-A 4L01 cash Cash +3010 13500 I-I 101a sales Sales +$ balance ; sed 2d balancesheet + Company + Balancesheet +11278 Cash +----- ------------------------ +11278 Total Assets + 13500 Retained Earnings (Profit) +------ -------------------------- + 13500 Total Liabilities and Equity +$ NEXTDATE=870101 close ; cat journalnext +Account Date Debit Credit Ref Description +------- ---- ----- ------ --- ----------- +1010 870101 11278 jl brought forward +4997 870101 11278 jl brought forward +## fillform: sum the adjusted trial balance per tax line into a tax form +$ printf 'Taxline\tDescription\n-------\t-----------\n4L01\tCash on hand\n101a\tGross sales\n' > taxform.f1 + +$ fillform f1 ; cat f1 +Taxline Amount Description +------- ------ ----------- +4L01 11278 Cash on hand +101a 13500 Gross sales +## calculate: compute columns of a form; f1.calc has one 'Column = expression' per line +$ echo 'Amount = Amount * 2' > f1.calc ; calculate f1 ; cat f1 +Taxline Amount Description +------- ------ ----------- +4L01 22556 Cash on hand +101a 27000 Gross sales +## getjournal / consolidate: collect subsidiary journals into the general journal +$ printf 'Date\tAmount\tAccount\tRef\tDescription\n----\t------\t-------\t---\t-----------\n870101\t100\t4010\ts1\tshop sale\n' > journalsales + +$ NEXTDATE=870131 getjournal journalsales +Date Amount Account Ref Description +---- ------ ------- --- ----------- +870131 100.00 s1 sales from journalsales +$ printf 'Date\tAmount\tAccount\tRef\tDescription\n----\t------\t-------\t---\t-----------\n870102\t40\t5010\tp1\tpaper\n' > journalexpense + +$ rm journal ; consolidate 870131 ; cat journal +Date Account Debit Credit Ref Description +---- ------- ----- ------ --- ----------- +870102 5010 40 p1 paper +870101 4010 100 s1 shop sale +870131 1010 40 double-entry +870131 1010 100 double-entry +## payroll: calcpay sorts journalpay into ledgerpay, w2 totals per employee +$ printf 'Number\tGross\tFederal\tState\tNet\n------\t-----\t-------\t-----\t---\n2\t1000\t100\t50\t850\n1\t900\t90\t40\t770\n2\t1000\t100\t50\t850\n' > journalpay + +$ calcpay ; cat ledgerpay +Number Gross Federal State Net +------ ----- ------- ----- --- +1 900 90 40 770 +2 1000 100 50 850 +2 1000 100 50 850 +$ w2 +Number Gross Federal State Net +------ ----- ------- ----- --- +1 900 90 40 770 +2 2000 200 100 1700 +## ===== /act: sales and stock ===== +$ printf 'Code\tName\tPrice\n----\t----\t-----\n10010\twidget\t2.50\n10020\tgadget\t10\n' > product + +$ printf 'Number\tCompany\tLast\n------\t-------\t----\n7\tAcme\tRoe\n' > customer + +## sale: enter a sale order (customer, then code and quantity per item, then y to confirm) +$ printf '7\n10010\n4\n10020\n1\n\ny\n\n' | NEXTDATE=870105 sale +Enter Customer Number (? for new, /string search, Return to exit) Mailing Address for Customer Number 7 +Roe +Acme +Code (Return when done): Qty: Code (Return when done): Qty: Code (Return when done): Number Cust Date Shipped Gross Tax Total +------ ---- ---- ------- ----- --- ----- +1 7 870105 0 20.00 1.00 21.00 +Is this correct? (y, n) Enter Customer Number (? for new, /string search, Return to exit) +$ cat salesorder +Number Cust Date Shipped Gross Tax Total +------ ---- ---- ------- ----- --- ----- +1 7 870105 0 20.00 1.00 21.00 +## invoice / cstate: print an invoice or customer statement for what was sold +$ printf '1\n\n' | invoice +Please enter order number (or Return to exit): Invoice + ------- +Company +Order Number 1 +Date Ordered 870105 +Date Shipped 0 +Mailing Address for Customer Number 7 +Roe +Acme +Order Number Code Backord Qty Price Total Name +----- ------ ---- ------- --- ----- ----- ---- +1 1 10010 10 4 2.50 10.00 widget +1 2 10020 10 1 10 10.00 gadget + Gross 20.00 + Tax 1.00 + Total 21.00 +Please enter order number (or Return to exit): +$ printf '7\n\n' | cstate +Please enter customer number (or all or Return to exit): Customer Statement + ------------------ +Mailing Address for Customer Number 7 +Roe +Acme +Now Due and Payable is your balance of: 21.00 +Details of Your Orders and Payments +Cust Date Amount Ref +---- ---- ------ --- +7 870105 -21.00 1 + -21.00 +Please enter customer number (or all or Return to exit): +## bom: parts needed for everything sold (needs the table part with Code, Subpart, Count) +$ printf 'Code\tSubpart\tCount\n----\t-------\t-----\n10010\ts1\t3\n10020\ts1\t1\n10020\ts2\t2\n' > part + +$ bom ; cat bom +Code Qty +---- --- +s1 13 +s2 2 +## update.inv: change the stock of one item, with record locking (INV names the table) +$ cp inventory inv ; printf '7\n9\n\n' | INV=inv update.inv | tail -n +2 +Item Number: (or hit Return key to exit.) Item=7 Amount=5 Cost=175.00 Value=875.00 Description=scales +Amount Amount: Item Number: (or hit Return key to exit.) +$ grep '^7' inv +7 9 175.00 1575 scales diff --git a/lib/rdb/cmd/access.rb b/lib/rdb/cmd/access.rb index c196015..2c29fe1 100644 --- a/lib/rdb/cmd/access.rb +++ b/lib/rdb/cmd/access.rb @@ -165,10 +165,12 @@ Rdb.register('enter', 'Add rows to a table or list file without an editor', 'ent end end +require 'digest' + # ---- locks ------------------------------------------------------------------- def lock_file(name) - File.join(Dir.tmpdir, "L#{File.basename(name)}") + File.join(Dir.tmpdir, "L#{File.basename(name)}.#{Digest::MD5.hexdigest(File.expand_path(name))[0, 8]}") end Rdb.register('lock', 'Lock a record or field of a file', 'lock filename processid from to indexfrom indexto') do |args| diff --git a/lib/rdb/cmd/act.rb b/lib/rdb/cmd/act.rb index 678664b..c6d5389 100644 --- a/lib/rdb/cmd/act.rb +++ b/lib/rdb/cmd/act.rb @@ -365,7 +365,7 @@ Rdb.register('sale', 'Enter sale order, and item, update customer', 'sale') do | next if items.empty? tax = gross * 0.05 - order = [n.to_s, cust, Date.today.strftime('%y%m%d'), '0', format('%.2f', gross), format('%.2f', tax), format('%.2f', gross + tax)] + order = [n.to_s, cust, next_date, '0', format('%.2f', gross), format('%.2f', tax), format('%.2f', gross + tax)] puts Rdb::Table.new(%w[Number Cust Date Shipped Gross Tax Total], [order]).to_s if ask('Is this correct? (y, n) ').downcase.start_with?('y') File.write('salesorder', Rdb::Table.open('salesorder').tap { |t| t.rows << order }.to_s) diff --git a/lib/rdb/cmd/term.rb b/lib/rdb/cmd/term.rb index 9d77811..3e7b737 100644 --- a/lib/rdb/cmd/term.rb +++ b/lib/rdb/cmd/term.rb @@ -227,12 +227,24 @@ end Rdb.register('testall', 'Test all /rdb programs in directory $RDB/demo', 'testall [ > testall.new ]') do |_args| ENV['PATH'] = "#{File.join(Rdb::ROOT, 'bin')}:#{ENV['PATH']}" demo = File.join(ENV['RDB'] || Rdb::ROOT, 'demo') - Dir.chdir(demo) script = File.join(demo, 'testall.cmds') File.exist?(script) or die "#{script}: missing" - File.readlines(script, chomp: true).reject { |l| l.strip.empty? || l.start_with?('#') }.each do |cmd| - puts "$ #{cmd}" - $stdout.flush - puts `#{cmd} 2>&1` + # Run in a scratch copy: some commands (append, delete, close ...) change files. + Dir.mktmpdir('rdb-testall') do |dir| + FileUtils.cp_r(Dir[File.join(demo, '*')], dir) + Dir.chdir(dir) + File.readlines(script, chomp: true).each do |cmd| + next if cmd.strip.empty? + + if cmd.start_with?('##') # '##' lines are shown, a single '#' is a silent comment + puts cmd + next + end + next if cmd.start_with?('#') + + puts "$ #{cmd}" + $stdout.flush + puts Open3.capture2e('sh', '-c', cmd, stdin_data: '').first + end end end diff --git a/lib/rdb/version.rb b/lib/rdb/version.rb index c7b6b91..a6d6fb7 100644 --- a/lib/rdb/version.rb +++ b/lib/rdb/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Rdb - VERSION = '0.2.0' + VERSION = '0.3.0' end diff --git a/test/rdb_test.rb b/test/rdb_test.rb index 2bb9e0c..15aa290 100644 --- a/test/rdb_test.rb +++ b/test/rdb_test.rb @@ -445,6 +445,13 @@ class HelpTest < RdbCase assert_equal "\e[3;8H", rdb('cursor', '3', '8') end + def test_testall_mentions_every_command + script = File.read(File.join(DEMO, 'testall.cmds')) + names = data(rdb('commands')).map(&:first) + missing = names.reject { |n| script.match?(/(^|[\s|;(])#{Regexp.escape(n)}(\s|$)/) } + assert_empty missing, "testall.cmds lacks an example for: #{missing.join(' ')}" + end + def test_testall_runs_demo_script out = rdb('testall') assert_includes out, '$ row'