Repository navigation
Add API.eager_load! to compile an API and load its I18n translations - #3019
Conversation
1a79281 to
f851b9a
Compare
Danger ReportNo issues found. |
Danger ReportNo issues found. |
9240107 to
1e2f663
Compare
1e2f663 to
17ea848
Compare
|
I think we should we provide |
Rails is already doing calling it through i18n. This is only when it's not a Rails app. I like the |
17ea848 to
92e64b0
Compare
Rename? |
e3f5d35 to
291702f
Compare
|
Could we expose this through an explicit The first-error improvement makes sense, but An explicit Rails already eager-loads I18n when application eager loading is enabled. Automatically invoking Grape API eager loading from Rails would need explicit integration; naming the method — AI posting on behalf of @dblock |
291702f to
5ce1cc2
Compare
4bc4928 to
dd8e887
Compare
|
Done: the setting is gone, and |
dd8e887 to
5af5ffc
Compare
a022324 to
7d99559
Compare
I18n reads its locale files on the first lookup, so in an app that precompiles its API the first error a process answered still paid for the load: 3.65 ms against 0.87 ms once the translations were in memory. API.eager_load! compiles the API and loads the translations, so an app calling it at boot pays for both there. It is an explicit call rather than a setting read whenever an API is compiled, because compile! also runs lazily on the first request: tied to it, the load would land on that request, even a successful one. compile! and request handling are unchanged. Rails already loads I18n at boot when config.eager_load is on; calling an API's eager_load! from Rails is left to a separate change. A Simple backend is loaded with translations(do_init: true), the load its first lookup would run, rather than eager_load!: that one also flags the backend so every later I18n.reload! loads at once, and raises on a LazyLoadable backend set to lazy_load. Loading only Grape's own locale file would not help, as the first lookup still reads the whole load path, Grape's file included. Any other backend is eager-loaded when it answers eager_load!, and left to its first lookup otherwise. Like compile!, eager_load! is kept off the setup steps an API records, so mounting the API does not run it again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7d99559 to
2eb5160
Compare
What
API.eager_load!compiles an API and loads I18n's translations, so an app can do both at boot:compile!and request handling are unchanged. There is no global setting.Why
I18n loads lazily, so the first error a process answers pays for the load. Measured in a fresh process after
API.compile!: first 400 3.65 ms lazy vs 0.87 ms loaded. Steady state is unchanged (0.095 vs 0.084 ms).Why a method, not a setting
An earlier version of this PR added
Grape.config.eager_load_i18nand loaded the translations inInstance#initialize. As @dblock pointed out,compile!also runs lazily on the first request, so with the setting on, the load moved onto that request, even a successful one, rather than to boot. An expliciteager_load!only runs when the app calls it.Rails already loads I18n at boot when
config.eager_loadis on (ActiveSupport's I18n railtie registersconfig.eager_load_namespaces << I18n). Calling an API'seager_load!from Rails would need explicit integration, left to a separate change.How the backend is loaded
LazyLoadable):translations(do_init: true), the same load its first lookup would run.eager_load!is avoided on purpose:@eager_loaded, so every laterI18n.reload!loads at once instead of on the next lookup;LazyLoadablebackend set tolazy_load: trueraisesUnsupportedMethodon it.Chain,KeyValue, …):eager_load!if it answers it. Theirtranslationstakes nodo_init:. A backend that does not inheritI18n::Backend::Basemay have noeager_load!, and is left to its first lookup.en.yml:I18n.backend.load_translations(path)reads just that file, but it does not mark the backend as loaded. The first lookup still reads the wholeload_path, Grape's file included, and overwrites any key an app stored over Grape's.Notes
compile!,eager_load!is inNON_OVERRIDABLEand delegated to the base instance, so mounting the API does not replay it.I18n.load_pathwith<<aftereager_load!is read only afterI18n.reload!. Assigning the path (I18n.load_path += [path]) drops the loaded translations, and the next lookup reads them again. The README's I18n section says so, and thecompile!section points to it.Acme::API.eager_load!instead ofcompile!.Tests
eager_load!compiles the API that serves requests, and loads the translations.eager_load!does not run it again.I18n.reload!leaves the translations to the next lookup.LazyLoadablebackend withlazy_load: trueloads its current locale; aChainbackend is loaded througheager_load!; a backend with noeager_load!still answers.eager_load!recorded as a setup step, the load moved intoInstance#initialize,eager_load!for every backend,do_init:for every backend, and norespond_to?guard each fail a spec.🤖 Generated with Claude Code