From 777a3b56e2d38eec34fb30dcd2e884995804939f Mon Sep 17 00:00:00 2001 From: wkh Date: Sun, 11 Oct 2026 22:51:43 +0200 Subject: [PATCH] =?UTF-8?q?record:=200=20und=201=20=3D=20erste=20Zeile,=20?= =?UTF-8?q?negative=20Nummern=20z=C3=A4hlen=20vom=20Ende?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 5 +++-- lib/rdb/cmd/relational.rb | 6 ++++-- man/record.man | 11 +++++++++++ test/rdb_test.rb | 19 ++++++++++++------- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f72e1c..7edda44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,9 @@ folgen [Semantic Versioning](https://semver.org/lang/de/). Die Version steht in ## [Unveröffentlicht] -### Behoben -- `record 0`, `record -1` und `record abc` lieferten Zeilen vom Ende der Tabelle (negativer Index in Ruby). Jetzt ein Fehler; eine Nummer hinter dem Ende gibt weiterhin eine leere Tabelle. +### Geändert +- `record`: 0 und 1 sind die erste Zeile, negative Zahlen zählen vom Ende (`-1` letzte, `-2` vorletzte ...). Außerhalb der Tabelle kommt eine leere Tabelle, Text und Dezimalzahlen sind ein Fehler. (Vorher lieferten 0, -1 und Text ungewollt Zeilen vom Ende.) + ### Hinzugefügt - Steuertabellen 2026 für `tax`: `demo/taxDE2026`, `taxAT2026`, `taxCH2026` (nur Bundessteuer), `taxLI2026` (nur Landessteuer), erzeugt mit `tools/gen_tax_tables.rb`, mit Tests. diff --git a/lib/rdb/cmd/relational.rb b/lib/rdb/cmd/relational.rb index b5aa12a..0f571f6 100644 --- a/lib/rdb/cmd/relational.rb +++ b/lib/rdb/cmd/relational.rb @@ -72,10 +72,12 @@ end Rdb.register('record', 'Find and output a record from a table', 'record number < table') do |args| die 'usage: record number < table' unless args.size == 1 - die "'#{args[0]}' is not a record number (1, 2, 3 ...)" unless args[0].match?(/\A\d+\z/) && args[0].to_i.positive? + die "'#{args[0]}' is not a record number (1, 2, 3 ... or -1 for the last)" unless args[0].match?(/\A[+-]?\d+\z/) + # 0 and 1 are the first record; -1 is the last, -2 the one before it ... t = table_in - row = t.rows[args[0].to_i - 1] + n = args[0].to_i + row = n.negative? ? (t.rows[n] if -n <= t.rows.size) : t.rows[[n, 1].max - 1] t.with(rows: row ? [row] : []).write end diff --git a/man/record.man b/man/record.man index 5591986..104937d 100644 --- a/man/record.man +++ b/man/record.man @@ -59,6 +59,17 @@ you see output in the following format: .DM Item 1 is rubber gloves .DE +.SH "Record Numbers" +.I "(Ruby re-creation only.)" +Records are counted from 1. +The numbers 0 and 1 both mean the first record. +A negative number counts from the end: +.B \-1 +is the last record, +.B \-2 +the one before it, and so on. +A number beyond either end gives a table without records. +Anything that is not a whole number is an error. .SH "See Also" .B row, diff --git a/test/rdb_test.rb b/test/rdb_test.rb index 95fdf50..04227b3 100644 --- a/test/rdb_test.rb +++ b/test/rdb_test.rb @@ -5,8 +5,18 @@ require 'pty' require_relative '../lib/rdb' class SelectionTest < RdbCase - def test_record_rejects_numbers_below_one_and_junk - %w[0 -1 abc 2.5].each do |n| + def test_record_zero_and_one_are_the_first_and_negatives_count_from_the_end + { '0' => '1', '1' => '1', '2' => '3', '3' => '6', '-1' => '6', '-2' => '3', '-3' => '1', '+2' => '3' }.each do |n, item| + assert_equal [[item]], data(rdb('record', n, file: 'owners')).map { |r| r.first(1) }, "record #{n}" + end + end + + def test_record_beyond_either_end_is_an_empty_table + %w[4 9 -4 -9].each { |n| assert_equal [], data(rdb('record', n, file: 'owners')), "record #{n}" } + end + + def test_record_rejects_junk + %w[abc 2.5 1e3].each do |n| out, err, st = run3('record', n, stdin: File.read(File.join(@dir, 'owners'))) refute st.success?, "record #{n} should fail" assert_equal '', out @@ -14,11 +24,6 @@ class SelectionTest < RdbCase end end - def test_record_beyond_the_end_is_an_empty_table - assert_equal [], data(rdb('record', '9', file: 'owners')) - assert_equal [%w[6 cy]], data(rdb('record', '3', file: 'owners')) - end - def test_row_numeric_condition out = rdb('row', 'Cost > 5', file: 'inventory') assert_equal %w[3 6 7], data(out).map(&:first)