polytypo 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +109 -0
  4. data/lib/polytypo/data/README.md +20 -0
  5. data/lib/polytypo/data/UNICODE +1 -0
  6. data/lib/polytypo/data/VERSION +1 -0
  7. data/lib/polytypo/data/fixtures/de-CH.json +501 -0
  8. data/lib/polytypo/data/fixtures/de-DE.json +547 -0
  9. data/lib/polytypo/data/fixtures/el.json +239 -0
  10. data/lib/polytypo/data/fixtures/en-GB.json +1274 -0
  11. data/lib/polytypo/data/fixtures/en-US.json +1807 -0
  12. data/lib/polytypo/data/fixtures/fi.json +1306 -0
  13. data/lib/polytypo/data/fixtures/fr-CA.json +268 -0
  14. data/lib/polytypo/data/fixtures/fr.json +603 -0
  15. data/lib/polytypo/data/fixtures/locale-resolution.json +209 -0
  16. data/lib/polytypo/data/fixtures/ru.json +688 -0
  17. data/lib/polytypo/data/fixtures/sv.json +1290 -0
  18. data/lib/polytypo/data/locales/de-CH.json +77 -0
  19. data/lib/polytypo/data/locales/de-DE.json +76 -0
  20. data/lib/polytypo/data/locales/el.json +90 -0
  21. data/lib/polytypo/data/locales/en-GB.json +115 -0
  22. data/lib/polytypo/data/locales/en-US.json +133 -0
  23. data/lib/polytypo/data/locales/fi.json +136 -0
  24. data/lib/polytypo/data/locales/fr-CA.json +78 -0
  25. data/lib/polytypo/data/locales/fr.json +84 -0
  26. data/lib/polytypo/data/locales/registry.json +9 -0
  27. data/lib/polytypo/data/locales/ru.json +112 -0
  28. data/lib/polytypo/data/locales/sv.json +124 -0
  29. data/lib/polytypo/data/rules/dashes.md +1238 -0
  30. data/lib/polytypo/data/rules/order.json +78 -0
  31. data/lib/polytypo/data/schema/fixtures.schema.json +79 -0
  32. data/lib/polytypo/data/schema/locale.schema.json +235 -0
  33. data/lib/polytypo/data/schema/registry.schema.json +29 -0
  34. data/lib/polytypo/data/schema/resolution.schema.json +50 -0
  35. data/lib/polytypo/engine/codepoints.rb +24 -0
  36. data/lib/polytypo/engine/edits.rb +64 -0
  37. data/lib/polytypo/engine/locale.rb +138 -0
  38. data/lib/polytypo/engine/pipeline.rb +61 -0
  39. data/lib/polytypo/engine/registry.rb +47 -0
  40. data/lib/polytypo/engine/rules/apostrophe.rb +127 -0
  41. data/lib/polytypo/engine/rules/dash_shared.rb +342 -0
  42. data/lib/polytypo/engine/rules/dashes.rb +125 -0
  43. data/lib/polytypo/engine/rules/ellipsis.rb +100 -0
  44. data/lib/polytypo/engine/rules/hyphen.rb +207 -0
  45. data/lib/polytypo/engine/rules/nbsp.rb +616 -0
  46. data/lib/polytypo/engine/rules/quote_ambiguity.rb +241 -0
  47. data/lib/polytypo/engine/rules/quotes.rb +420 -0
  48. data/lib/polytypo/engine/rules/ranges.rb +124 -0
  49. data/lib/polytypo/engine/rules/spaces.rb +232 -0
  50. data/lib/polytypo/engine/rules/symbols.rb +291 -0
  51. data/lib/polytypo/engine/rules.rb +19 -0
  52. data/lib/polytypo/engine/sentinels.rb +23 -0
  53. data/lib/polytypo/engine/unicode_util.rb +390 -0
  54. data/lib/polytypo/errors.rb +24 -0
  55. data/lib/polytypo/modes/html.rb +233 -0
  56. data/lib/polytypo/modes/markdown.rb +187 -0
  57. data/lib/polytypo/modes/parse_error.rb +19 -0
  58. data/lib/polytypo/modes/runner.rb +57 -0
  59. data/lib/polytypo/modes/spans.rb +132 -0
  60. data/lib/polytypo/version.rb +5 -0
  61. data/lib/polytypo.rb +91 -0
  62. data/polytypo.gemspec +37 -0
  63. metadata +122 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 05bb3c57adf30c19b96896ce40858128d7fee5102bee1c3b533c84fcc55d852e
4
+ data.tar.gz: '0088837892eb60339829d77c2f2204adc23f44fdd6587bdf9294e17885a44271'
5
+ SHA512:
6
+ metadata.gz: 14d6279d9fdae3d660709dc7c0a4f5c75711befd10c74bcd8f66a4b0026cc7ddec5d83ffb67e7c6609b496322021c29c1097bf6ee24c804e233685e3a9be27c8
7
+ data.tar.gz: ceaf4caf52d16a2e3189916caa72272d24513e71ea2f8262bb254a1d975247be66d5eac8e46dde0f46b5a62383ee6aeaf043a1a61acc5ceab4168136c466d4d3
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Iurii Rogulia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,109 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/polytypo/polytypo/main/brand/logo/polytypo-lockup-stacked.svg" alt="polytypo" width="260">
3
+ </p>
4
+
5
+ <h1 align="center">polytypo</h1>
6
+
7
+ <p align="center">
8
+ <a href="https://rubygems.org/gems/polytypo"><img src="https://img.shields.io/gem/v/polytypo.svg" alt="Gem version"></a>
9
+ <a href="https://github.com/polytypo/polytypo-ruby/actions/workflows/ci.yml"><img src="https://github.com/polytypo/polytypo-ruby/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
10
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT"></a>
11
+ </p>
12
+
13
+ <p align="center">
14
+ Locale-correct quotes, dashes, ellipses, apostrophes, symbols and no-break spaces —<br>
15
+ one portable spec, designed for byte-identical output across runtimes.
16
+ </p>
17
+
18
+ <p align="center">
19
+ <strong>Try it live, no install: <a href="https://polytypo.dev/">polytypo.dev</a></strong>
20
+ </p>
21
+
22
+ This is the Ruby implementation. The full spec — all locales, all rules, worked examples in
23
+ each — lives in [polytypo/polytypo](https://github.com/polytypo/polytypo). This runtime supports
24
+ the `text` and `html` modes fully, and `markdown` for the `commonmark` dialect only — `mdx`
25
+ returns `POLYTYPO_INVALID_DIALECT` (no MDX/JSX parser is available for Ruby; see
26
+ [Supported dialects](#supported-dialects)).
27
+
28
+ ## Install
29
+
30
+ ```sh
31
+ gem install polytypo
32
+ ```
33
+
34
+ Or in a Gemfile:
35
+
36
+ ```ruby
37
+ gem "polytypo"
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ```ruby
43
+ require "polytypo"
44
+
45
+ Polytypo.transform(%q{She said, "it's fine" -- but I wasn't sure...}, locale: "en-US")
46
+ # => "She said, “it’s fine”—but I wasn’t sure…"
47
+ ```
48
+
49
+ Same input, one locale changed — quotes, dash spacing and all follow the target locale, not a
50
+ single hardcoded style:
51
+
52
+ ```ruby
53
+ Polytypo.transform(%q{Sie sagte: "Alles gut" -- aber ich war mir nicht sicher...}, locale: "de-DE")
54
+ # => "Sie sagte: „Alles gut“ – aber ich war mir nicht sicher…"
55
+ ```
56
+
57
+ HTML and Markdown are first-class modes, not an afterthought — tags, attributes and fenced code
58
+ are left alone; only text content is touched:
59
+
60
+ ```ruby
61
+ Polytypo.transform(%q{<a title="test... wait">Wait... she said "go on."</a>}, locale: "en-US", mode: "html")
62
+ # => "<a title=\"test... wait\">Wait… she said “go on.”</a>"
63
+ ```
64
+
65
+ `locale:` has no default anywhere and must always be passed explicitly — there is no silent
66
+ fallback to English. `mode:` defaults to `"text"` if omitted.
67
+
68
+ ```ruby
69
+ Polytypo.transform(input, locale: "fr", mode: "markdown", dialect: "commonmark")
70
+ ```
71
+
72
+ `require "polytypo"` never loads the native `commonmarker` extension unless you actually call
73
+ `Polytypo.transform` with `mode: "markdown"` — the require happens lazily, inside the markdown
74
+ pipeline only. A single `Polytypo.transform` module method (not separate
75
+ `polytypo/text`/`html`/`markdown` gems) is the idiomatic Ruby shape for this — Ruby's dynamic
76
+ `require` already gets you the same dependency isolation JS/Python's subpath split exists for,
77
+ without a separate namespace per mode.
78
+
79
+ ### Errors
80
+
81
+ Every error `Polytypo.transform` raises is a `Polytypo::Error` carrying one of seven stable
82
+ codes — check `#code`, not the message text, which is English and informative but not part of
83
+ the contract:
84
+
85
+ ```ruby
86
+ begin
87
+ Polytypo.transform("x", locale: "xx-ZZ")
88
+ rescue Polytypo::Error => e
89
+ e.code # => "POLYTYPO_UNKNOWN_LOCALE"
90
+ end
91
+ ```
92
+
93
+ ## Supported dialects
94
+
95
+ `markdown` mode requires a `dialect:`, exactly as the spec requires (no default, detection is
96
+ forbidden). This runtime supports `dialect: "commonmark"` (CommonMark plus GFM — tables,
97
+ strikethrough, task lists, autolink literals). `dialect: "mdx"` is a real dialect the spec names,
98
+ but this runtime has no MDX/JSX parser for it and raises `Polytypo::Error` with
99
+ `POLYTYPO_INVALID_DIALECT` immediately rather than silently mishandling it — a narrower, honest
100
+ conformance claim, not a port defect.
101
+
102
+ ## Thread safety
103
+
104
+ `Polytypo.transform` is a pure module method with no class-level mutable state: safe to call
105
+ concurrently from any number of `Thread`s with no external synchronization.
106
+
107
+ ## Licence
108
+
109
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,20 @@
1
+ # Vendored spec subset
2
+
3
+ This directory is a manually-synced copy of the subset of `polytypo/polytypo`'s canonical `spec/`
4
+ that this gem needs at runtime: `locales/`, `fixtures/`, `rules/order.json`, `rules/dashes.md`,
5
+ `schema/`, `VERSION`, `UNICODE`. It is **not** the canonical spec — the rest of the normative
6
+ prose (`spec/rules/*.md` beyond `dashes.md`) and `validate-spec.mjs` live only in
7
+ `polytypo/polytypo`.
8
+
9
+ Named `lib/polytypo/data/`, not `vendor/polytypo-spec/` like the JS/Python ports: a Ruby
10
+ gemspec's `files` list can include any path directly, so there is no need for a separate
11
+ `vendor/` staging copy plus a second, separately-generated package-data copy the way Python
12
+ needed (its build backend does not include arbitrary files without explicit config). This is the
13
+ one copy, shipped as part of the gem's own `lib/` payload and loaded via `File.read` relative to
14
+ `__dir__` at runtime — never a network or external-filesystem read.
15
+
16
+ Editing a file here does not change the spec; it only drifts this copy from canonical. When
17
+ canonical's `spec/` changes, re-copy the affected files here. How this vendoring will work
18
+ long-term (submodule, per-ecosystem spec package, or something else) is an open decision tracked
19
+ in `polytypo/polytypo`'s roadmap; this is the interim, manually-synced form — the same status
20
+ every other port's vendored copy has.
@@ -0,0 +1 @@
1
+ 17.0
@@ -0,0 +1 @@
1
+ 1.0.0