-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexamples.rb
More file actions
127 lines (112 loc) · 5.15 KB
/
Copy pathexamples.rb
File metadata and controls
127 lines (112 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# frozen_string_literal: true
# Runnable examples for the baseh Ruby gem.
# Run from ruby/: ruby -I lib examples/examples.rb
require "baseh"
def show(label)
puts "#{label} -> #{yield.inspect}"
rescue Baseh::BasehError => e
puts "#{label} -> raises BasehError [#{e.code}]: #{e.message}"
end
# 0. Zero configuration: the expandable default profile behind two module
# functions. Codes start short and grow one character as the id sequence
# climbs. Most applications never need a profile object at all.
puts "== zero config (expandable default) =="
show("Baseh.encode(123456)") { Baseh.encode(123456) }
# => 4 characters at this namespace size; grows as ids climb
show("Baseh.decode(code).id round trip") { Baseh.decode(Baseh.encode(123456)).id }
show('Baseh.decode("bogus") ') { Baseh.decode("ZZZZ-ZZZZ") }
# A keyed private-mapping variant mirrors the other -p tiers:
# Baseh.baseh_expandable_p_v1(key_bytes: ..., key_id: "prod-01")
# 1. The same expandable profile used explicitly, when you want the codec
# object in hand (e.g. to pass it around or check its generation).
puts "== expandable (explicit codec) =="
expandable = Baseh::Baseh.new(Baseh.baseh_expandable_v1)
show("expandable.encode(id: 123456)") { expandable.encode(id: 123456) }
show("expandable round trip") { expandable.decode(expandable.encode(id: 123456)).id }
# 2. A frozen preset: load baseh-medium-v1 and use the full codec.
puts "== preset =="
medium = Baseh::Baseh.new(Baseh.baseh_medium_v1)
show("encode(id: 123456789)") { medium.encode(id: 123456789) }
show('decode("C8XP-8J49").id') { medium.decode("C8XP-8J49").id }
show('decode("UORY-PDCA").id (typed aliases)') { medium.decode("UORY-PDCA").id }
show("encode(id: 813) (blocked word)") { medium.encode(id: 813) }
show('decode("C8XP-8J4X") (checksum typo)') { medium.decode("C8XP-8J4X").id }
show("capacity") { medium.capacity }
# 3. Customized: load a preset, extend the body and regroup the delimiter.
# For expandable profiles the same hash accepts :mode ("expandable" or
# "fixed"), :min_length (default 4) and :separator_min_length (the tier
# uses 6 — no hyphen until codes reach that length).
puts "== customized =="
custom = Baseh.baseh_medium_v1
custom[:profile_id] = "orders-v1"
custom[:body_length] = 7
custom[:grouping] = [5, 4]
orders = Baseh::Baseh.new(custom)
show("encode(id: 123456789)") { orders.encode(id: 123456789) }
show("decode(...) round trip") { orders.decode(orders.encode(id: 123456789)).id }
show('decode("ZC8VR-EMJ2") (bad check)') { orders.decode("ZC8VR-EMJ2").id }
show("capacity") { orders.capacity }
# 4. Typo correction: a spoken-confusion flip is amended back to the
# canonical code. The frozen tiers absorb every spoken pair as a typed
# alias, so correction is shown on a profile where both P and T can be
# issued and a misheard T only the checksum can catch.
puts "== typo correction =="
radio = Baseh::Baseh.new(
profile_id: "radio-v1",
body_alphabet: "0123456789ABCDEFGHJKMNPQRSTVWXYZ",
body_length: 6,
checksum_alphabet: "234679ACDEFGHJKMNPQRTUVWXY",
checksum_length: 1,
case_sensitive: false,
separator: "-",
grouping: [4, 3],
aliases: { "O" => "0", "I" => "1", "L" => "1" },
permutation: { enabled: false }
)
show('decode("0000-T0W") without correction') { radio.decode("0000-T0W").id }
result = radio.decode("0000-T0W", try_correction: true, confusion_profile: :light)
puts "Identifier: #{result.id}, corrected to #{result.canonical_code}"
# 5. A view helper for ERB: one shared codec built at boot, records rendered
# as codes at the edge. This module works as a Rails helper exactly as
# written; here it is exercised with a plain struct. The matching
# controller-side decode pattern is in docs/cookbook.md ("Framework view
# helpers").
puts "== view helper (ERB) =="
module BasehHelper
CODEC = Baseh::Baseh.new(Baseh.baseh_expandable_v1)
# <%= baseh_code(@order) %>
def baseh_code(record)
CODEC.encode(id: record.id)
end
end
Order = Struct.new(:id)
include BasehHelper
order = Order.new(123456)
puts "<%= baseh_code(@order) %> -> #{baseh_code(order)}"
show("controller-side decode") { BasehHelper::CODEC.decode(baseh_code(order)).id }
show("controller-side decode (bogus code)") { BasehHelper::CODEC.decode("ZZZZ-ZZZZ").id }
# 6. Issuing codes: the expandable issuance-counter pattern, runnable without
# a database. One shared codec wraps a single counter; each call increments
# and encodes, so issued codes never look sequential even though the ids
# are. In production the ivar below is swapped for a Postgres SEQUENCE or
# an atomically-incremented counters row — exactly one writer, and the
# counter is backed up with the database. See docs/cookbook.md ("Issuing
# codes").
puts "== issuing codes =="
class Issuer
def initialize(codec)
@codec = codec
@next_id = 0 # production: SELECT nextval('codes_seq') or an atomic UPDATE
end
def issue
@next_id += 1
@codec.encode(id: @next_id)
end
def decode(code)
@codec.decode(code).id
end
end
issuer = Issuer.new(Baseh::Baseh.new(Baseh.baseh_expandable_v1))
issued = Array.new(6) { issuer.issue }
puts "issue x6 -> #{issued.inspect}"
show("decode(#{issued.first}) back to its id") { issuer.decode(issued.first) }