duckling 0.3.0 → 0.4.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e13fe61846d4bc85faed37d388e0845de0bf0a6742d5b73796524987b0c85681
4
- data.tar.gz: b8fec101cf3f92a760ef275261cbbebe3411185cb4ea84bd19756bd25b09bef1
3
+ metadata.gz: '059bb11a0d905a85d7577db06b62d8971c2896bda2433d0bbeb8fd0a2393c9b2'
4
+ data.tar.gz: 9101b8b87ada67851e4468270d73131be44dca36ea3c1f72a99bc403f5ef46e7
5
5
  SHA512:
6
- metadata.gz: 93d4e3ce65576eb6ab27ec20b617b7bc4cdcab1a1be8bf61ccacbf33ede40daf5293e9ec10053a893986d424761012d3d5083ace5bf7681750a6cfb1fa40574e
7
- data.tar.gz: 722c57d3d75412edc428b2476a6312ff5b6162a28537e50b523da94d41b0289f7c5b86a46072aa1a37ef72f580753ee9057c2452b0310580aba0843f76ff5a98
6
+ metadata.gz: fe7ad8e2c972e6070f2a2bea717e30f7d66d021b0ad3bf618466acb29b3d8bbc51efec151424f2ce3d8b08f0c0edbf5a2b78b667b7ffc8f2f81ff380f08ecf96
7
+ data.tar.gz: 83fa337d4259faa6063c4322fac789c9b98e57bd7f2c8c3dc7e7142515179d2db69bb8700755c11c29078904af9bc7c6a99d01c1ffec2e6610dff5b6b4c2460a
data/CHANGELOG.md CHANGED
@@ -6,6 +6,97 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.4.1] - 2026-08-10
10
+
11
+ ### Fixed
12
+
13
+ - The packaged gem no longer includes agent- and development-tooling files.
14
+ 0.3.0 and 0.4.0 shipped `.claude/settings.json`, `AGENTS.md`, and
15
+ `CLAUDE.md`: the gemspec built its file list from `git ls-files` with a
16
+ reject-list of paths to exclude, so every newly tracked dotfile or tool
17
+ directory was packaged by default. The gemspec now allow-lists what ships
18
+ (`lib/`, `ext/`, `docs/` other than `docs/benchmarks/`, and a named set of
19
+ root files), so anything new stays out of the gem unless it is added
20
+ deliberately. Regression coverage checks the gemspec's file list in the
21
+ main suite and the built artifacts in `test/gem/packaged_gem_test.rb`.
22
+ None of the previously shipped files contained secrets — they are
23
+ development configuration and documentation, all public in the repository
24
+ — so the already-published 0.3.0/0.4.0 gems are unaffected in behavior and
25
+ have been left in place.
26
+
27
+ ## [0.4.0] - 2026-08-10
28
+
29
+ ### Changed
30
+
31
+ - **On a stock Debian/Ubuntu host, roughly a hundred IANA zone identifiers
32
+ stop resolving.** `reference_zone: "US/Eastern"` — and every other
33
+ backward-compatibility name, such as `"US/Pacific"`, `"Europe/Kiev"`, or
34
+ `"Japan"` — now raises `ArgumentError` there. Those names live in the
35
+ `tzdata-legacy` system package, which is not installed by default. Two ways
36
+ to get them back, either of which restores the previous behavior exactly:
37
+
38
+ ```ruby
39
+ gem "tzinfo-data" # in your Gemfile
40
+ ```
41
+ ```bash
42
+ apt install tzdata-legacy # on the host
43
+ ```
44
+
45
+ Canonical identifiers (`"America/New_York"`, `"Europe/Kyiv"`) are
46
+ unaffected. A host with no zoneinfo files at all — a scratch or distroless
47
+ container — needs the gem for `reference_zone:` to work at all.
48
+
49
+ The error message names the tz database that answered, how many identifiers
50
+ it has, and both remedies, so this is distinguishable from a typo. The
51
+ datasource and the count describe whichever database answered on your host,
52
+ so both differ from the example below:
53
+
54
+ ```
55
+ invalid reference_zone: "US/Eastern" (resolved against system zoneinfo at
56
+ /usr/share/zoneinfo, which provides 497 identifiers; this database has no
57
+ backward-compat names (US/Eastern and ~100 others), so if that is what this
58
+ is, it needs either the tzinfo-data gem or the tzdata-legacy system package)
59
+ ```
60
+
61
+ The remedy is worded as a condition rather than a claim about the name you
62
+ passed: whether a given identifier is one of the ~100 in IANA's `backward`
63
+ file isn't knowable without shipping that list, and asserting it would tell
64
+ every typo on such a host that `tzdata-legacy` will supply it.
65
+
66
+ A second, quieter difference comes with the same change: some distributions
67
+ compile tzdata in *rearguard* format, which strips negative DST, and on such
68
+ a host `Europe/Dublin` is modelled as an ordinary positive-DST zone rather
69
+ than a negative-DST one. Which distributions is not guessable — Ubuntu 24.04
70
+ is rearguard, Debian trixie is vanguard — so if you depend on tzinfo's
71
+ `dst?` flag, read it from the host rather than assuming. Resolved offsets
72
+ are the same either way, so no `Duckling.parse` result changes because of
73
+ it.
74
+
75
+ - `tzinfo-data` is no longer a runtime dependency. `tzinfo` already prefers
76
+ that gem when it is installed and falls back to the host's zoneinfo files
77
+ otherwise, so depending on it forced bundled tz data on every consumer to
78
+ serve the ones who want it. This is the change that produces the identifier
79
+ behavior above. Consumers who add `gem "tzinfo-data"` themselves get exactly
80
+ the previous behavior with no code change, and can still pick up a
81
+ tz-database revision by bumping that one gem. Dropping it means the bundled
82
+ tz data is no longer loaded at boot, so the first zone lookup does less
83
+ work; steady-state parsing is unaffected either way, since `reference_zone:`
84
+ resolution goes through the same tzinfo call once a database is loaded.
85
+
86
+ ### Added
87
+
88
+ - `Duckling::TZDataUnavailable`, raised when `reference_zone:` is given on a
89
+ host with no tz database at all — no zoneinfo files and no `tzinfo-data`
90
+ gem, as in a scratch or distroless container. Newly reachable because of the
91
+ dependency change above; previously a database always existed. It names both
92
+ fixes, where the underlying tzinfo error mentioned neither this gem nor
93
+ `reference_zone:`. Deliberately not an `ArgumentError`: it reports the
94
+ deployment's state, not a bad argument, so code rescuing `ArgumentError`
95
+ around caller-supplied zone names does not swallow it. Every other keyword
96
+ works without a tz database.
97
+
98
+ ## [0.3.0] - 2026-08-04
99
+
9
100
  ### Changed
10
101
 
11
102
  - **Breaking:** `reference_time:` now requires a Ruby `Time` object (or
data/README.md CHANGED
@@ -33,7 +33,7 @@ Duckling.parse("tomorrow", locale: "en")
33
33
  `Array` of entity `Hash`es (empty if nothing matched):
34
34
 
35
35
  ```ruby
36
- Duckling.parse(text, locale: "en", dims: ["time"], reference_time: nil, with_latent: false)
36
+ Duckling.parse(text, locale: "en", dims: ["time"], reference_time: nil, with_latent: false, reference_zone: nil)
37
37
  ```
38
38
 
39
39
  ### Keyword arguments
@@ -55,9 +55,49 @@ Duckling.parse(text, locale: "en", dims: ["time"], reference_time: nil, with_lat
55
55
  raises `TypeError`; wrap it in `Time.at(seconds)` first.
56
56
  - `with_latent:` (Boolean, default `false`) — include ambiguous/latent
57
57
  matches (e.g. a bare "morning") in the results.
58
+ - `reference_zone:` (String, default `nil`) — an IANA zone name, e.g.
59
+ `"America/New_York"`. Resolves each wall-clock result's UTC offset against
60
+ that zone on the result's *own* date, so a result before a DST transition
61
+ and one after it get different offsets instead of sharing
62
+ `reference_time:`'s single fixed one. An unknown identifier raises
63
+ `ArgumentError`; so does a `reference_time:` whose `utc_offset` disagrees
64
+ with the zone at that instant. It does not anchor the parse — see "Time
65
+ zone data" below.
58
66
 
59
67
  There is no `Duckling::Error` class — invalid `locale:`/`dims:` values raise
60
- plain `ArgumentError`.
68
+ plain `ArgumentError`, as do an unknown `reference_zone:` and a
69
+ `reference_time:` whose offset disagrees with it. The one named error a caller
70
+ is likely to meet is `Duckling::TZDataUnavailable`, for a host with no tz
71
+ database at all — see "Time zone data" below.
72
+
73
+ ### Time zone data
74
+
75
+ `reference_zone:` resolves against [tzinfo][], which uses the `tzinfo-data`
76
+ gem when it is installed and the host's own zoneinfo files otherwise. This gem
77
+ does not depend on `tzinfo-data`, so by default you get the host's database.
78
+
79
+ That is usually what you want, and it is the faster of the two to start up.
80
+ Two cases where it is not:
81
+
82
+ - **Backward-compatibility names.** Debian and Ubuntu ship names like
83
+ `"US/Eastern"` in a separate `tzdata-legacy` package that is not installed
84
+ by default, so roughly a hundred valid IANA identifiers raise
85
+ `ArgumentError` on a stock host. Either `gem "tzinfo-data"` or
86
+ `apt install tzdata-legacy` restores them. The error message names whichever
87
+ database answered and how many identifiers it has, so you can tell this
88
+ apart from a typo.
89
+ - **No zoneinfo files at all**, as in a scratch or distroless container.
90
+ `reference_zone:` raises `Duckling::TZDataUnavailable` there, naming both
91
+ fixes. Add `gem "tzinfo-data"` to bundle the data with your app, where you
92
+ can also patch its vintage by bumping one gem, or install the system
93
+ `tzdata` package. Every other keyword works without a tz database.
94
+
95
+ `reference_zone:` reinterprets result offsets; it does not anchor the parse.
96
+ Given without `reference_time:`, a relative expression like `"tomorrow"` still
97
+ anchors on the machine-local clock rather than on "now" in that zone — pass a
98
+ `reference_time:` in the zone to anchor as well.
99
+
100
+ [tzinfo]: https://github.com/tzinfo/tzinfo
61
101
 
62
102
  ### Return value
63
103
 
data/Rakefile CHANGED
@@ -60,7 +60,7 @@ end
60
60
  # extconf.rb runs in its own subprocess. ENV changes made there do not
61
61
  # reach the parent process, and the parent process runs `make`.
62
62
  #
63
- # So this fix changes ENV here, in the Rakefile, not in extconf.rb.
63
+ # So this fix changes ENV here, in the Rakefile.
64
64
  # The fix adds a prerequisite task to the local build's Makefile task.
65
65
  # This prerequisite task sets the correct host target. It runs before
66
66
  # the Makefile task, and before `make` runs later.
@@ -71,7 +71,7 @@ if (ruby_target = ENV["RUBY_TARGET"]) && ruby_target != RUBY_PLATFORM
71
71
  # away. Clearing RUST_TARGET alone lets rb_sys fall back to the target
72
72
  # baked into the container's $CARGO_HOME/config.toml, which is the
73
73
  # cross-compile target again. So a host triple that cannot be read is a
74
- # hard error, not something to skip past.
74
+ # hard error.
75
75
  #
76
76
  # Both variables belong to the whole rake process, and the cross build
77
77
  # reads them too. This is safe only because rake generates the cross
@@ -121,8 +121,8 @@ end
121
121
  #
122
122
  # So build from a throwaway plain clone instead, whose .git is a real
123
123
  # directory under the mount. Two consequences: the gem carries *committed*
124
- # state, not the working tree, and the clone gets its own Cargo target
125
- # directory rather than reusing this checkout's.
124
+ # state (uncommitted changes are excluded), and the clone gets its own Cargo
125
+ # target directory separate from this checkout's.
126
126
  CLONE_DIR = "tmp/native_gem_clone"
127
127
 
128
128
  def build_native_gem(platform)
@@ -163,10 +163,10 @@ end
163
163
 
164
164
  task :benchmark_env do
165
165
  # Force a realistic release-profile build regardless of .env.local's
166
- # RB_SYS_CARGO_PROFILE=dev (local dev checkouts only, never present in
167
- # CI). Must reenable :compile in case it already ran earlier in this same
168
- # rake process, so it's guaranteed to recompile under the forced profile
169
- # rather than reusing a stale dev-profile build.
166
+ # RB_SYS_CARGO_PROFILE=dev (local dev checkouts only; CI never has it).
167
+ # Must reenable :compile in case it already ran earlier in this same
168
+ # rake process, so it recompiles under the forced profile. A stale
169
+ # dev-profile build would be reused otherwise.
170
170
  ENV.delete("RB_SYS_CARGO_PROFILE")
171
171
  Rake::Task[:compile].reenable
172
172
  end
@@ -184,8 +184,8 @@ namespace :benchmark do
184
184
 
185
185
  desc "Run :record on a fresh branch off origin/main, then commit/push and open+auto-merge a PR via gh"
186
186
  task record_pr: ["release:guard_clean"] do
187
- # Explicit bash, not Rake's default `sh -c` (dash on Debian/Ubuntu
188
- # runners): dash's `set` doesn't support the `-o pipefail` flag below.
187
+ # Explicit bash: Rake's default `sh -c` is dash on Debian/Ubuntu
188
+ # runners, and dash's `set` doesn't support the `-o pipefail` flag below.
189
189
  sh("bash", "-c", <<~SH)
190
190
  set -euo pipefail
191
191
  original_ref="$(git symbolic-ref -q --short HEAD || git rev-parse HEAD)"
@@ -214,12 +214,14 @@ namespace :benchmark do
214
214
  end
215
215
 
216
216
  # The default suite exercises the extension compiled in this checkout.
217
- # test/gem/ exercises a *built* or *installed* gem instead — it needs one
218
- # handed to it, and the installed suite must not see this checkout's lib/ at
219
- # all so both run on their own, as plain `ruby test/gem/<file>`. See each
220
- # file's header.
217
+ # Three test subtrees run outside it:
218
+ # - test/gem/ exercises a *built* or *installed* gem instead it needs one
219
+ # handed to it, and the installed suite must not see this checkout's lib/.
220
+ # - test/capabilities/ is loaded by test_helper itself, gated on the tz probes.
221
+ # - test/environments/ holds contracts invoked directly by their CI step.
222
+ # See docs/tz-database-axis.md.
221
223
  Minitest::TestTask.create do |t|
222
- t.test_globs = FileList["test/**/*_test.rb"].exclude("test/gem/**/*")
224
+ t.test_globs = FileList["test/**/*_test.rb"].exclude("test/gem/**/*", "test/capabilities/**/*", "test/environments/**/*")
223
225
  end
224
226
 
225
227
  # Minitest::TestTask has no built-in way to declare a task dependency, and
@@ -227,7 +229,7 @@ end
227
229
  # `bundle exec rake` itself — `bundle exec rake test` run directly has no
228
230
  # guarantee `compile` ran first, which would surface as a confusing
229
231
  # LoadError/stale-behavior failure unrelated to the code under test.
230
- task test: :compile
232
+ task test: %i[compile]
231
233
 
232
234
  require "standard/rake"
233
235