1 Kas yra „klasikinis“ ir „Zeitwerk“ režimai?
Nuo pat pradžių ir iki „Rails“ 5, „Rails“ naudojo „Active Support“ įgyvendintą automatinį įkėlimą. Šis automatinis įkėlimas žinomas kaip „klasikinis“ ir vis dar yra prieinamas „Rails“ 6.x. „Rails“ 7 šis automatinis įkėlimas daugiau neįtrauktas.
Pradedant nuo „Rails“ 6, „Rails“ pristato naują ir geresnį būdą automatiškai įkelti, kuris deleguoja į Zeitwerk grotelę. Tai yra „Zeitwerk“ režimas. Pagal numatytuosius nustatymus, programos, įkeliančios 6.0 ir 6.1 versijos pagrindinius rėmus, veikia „Zeitwerk“ režime, ir tai yra vienintelis režimas, prieinamas „Rails“ 7.
2 Kodėl persijungti nuo „klasikinio“ prie „Zeitwerk“?
„Klasikinis“ automatinis įkėlimas buvo labai naudingas, bet turėjo keletą probleminių dalykų, dėl kurių automatinis įkėlimas kartais buvo šiek tiek sudėtingas ir painus. „Zeitwerk“ buvo sukurtas tam, kad spręstų šią problemą, tarp kitų motyvacijų.
Kai atnaujinama į „Rails“ 6.x, labai rekomenduojama persijungti į „Zeitwerk“ režimą, nes tai yra geresnis automatinis įkėlimas, „klasikinio“ režimo naudojimas yra pasenus.
„Rails“ 7 baigia pereinamąjį laikotarpį ir neįtraukia „klasikinio“ režimo.
3 Aš bijau
Nebijok :).
„Zeitwerk“ buvo sukurtas taip, kad būtų kuo suderinamesnis su klasikiniu automatinio įkėlimo įrankiu. Jei jūsų veikianti programa šiandien teisingai įkelia, tikimybės, kad persijungimas bus lengvas. Daugelis projektų, didelių ir mažų, pranešė apie labai sklandžius persijungimus.
Šis vadovas padės jums pasitikėti automatinio įkėlimo įrankiu.
Jei dėl kokios nors priežasties susiduriate su situacija, kurios nežinote, kaip išspręsti, nebijokite atidaryti problemos „rails/rails“ ir pažymėti @fxn
.
4 Kaip aktyvuoti „Zeitwerk“ režimą
4.1 Programos, veikiančios su „Rails“ 5.x arba ankstesne versija
Programose, veikiančiose su „Rails“ versija ankstesne nei 6.0, „Zeitwerk“ režimas nėra prieinamas. Jums reikia būti bent „Rails“ 6.0.
4.2 Programos, veikiančios su „Rails“ 6.x
Programose, veikiančiose su „Rails“ 6.x, yra du scenarijai.
Jei programa įkelia „Rails“ 6.0 arba 6.1 pagrindinius rėmus ir veikia „klasikiniame“ režime, ją reikia išjungti rankiniu būdu. Jums turi būti kažkas panašaus į tai:
# config/application.rb
config.load_defaults 6.0
config.autoloader = :classic # IŠTRINKITE ŠĮ EILUTĘ
Kaip pastebėta, tiesiog ištrinkite perrašymą, „Zeitwerk“ režimas yra numatytasis.
Kita vertus, jei programa įkelia senus pagrindinius rėmus, jums reikia išjungti „Zeitwerk“ režimą:
# config/application.rb
config.load_defaults 5.2
config.autoloader = :zeitwerk
4.3 Programos, veikiančios su „Rails“ 7
„Rails“ 7 yra tik „Zeitwerk“ režimas, jums nereikia nieko daryti, kad jį įjungtumėte.
Iš tikrųjų, „config.autoloader =“ nustatymojo netgi neegzistuoja „config/application.rb“. Jei jis naudojamas, prašome ištrinti eilutę.
5 Kaip patikrinti, ar programa veikia „Zeitwerk“ režime?
Norėdami patikrinti, ar programa veikia „Zeitwerk“ režime, įvykdykite
bin/rails runner 'p Rails.autoloaders.zeitwerk_enabled?'
Jei tai spausdina „true“, „Zeitwerk“ režimas yra įjungtas.
6 Ar mano programa atitinka „Zeitwerk“ konvencijas?
6.1 config.eager_load_paths
Atitikties testas vykdomas tik užkraunant failus. Todėl norint patikrinti „Zeitwerk“ atitiktį, rekomenduojama visus automatinio įkėlimo kelius įtraukti į užkraunimo kelius.
Pagal numatytuosius nustatymus tai jau yra taip, bet jei projekte yra konfigūruoti papildomi automatinio įkėlimo keliai, panašūs į šiuos:
config.autoload_paths << "#{Rails.root}/extras"
jie nėra užkraunami ir nebus patikrinti. Juos pridėti prie užkraunimo kelių yra lengva:
config.autoload_paths << "#{Rails.root}/extras"
config.eager_load_paths << "#{Rails.root}/extras"
6.2 zeitwerk:check
Kai įjungtas „Zeitwerk“ režimas ir patikrintas užkraunimo kelių konfigūravimas, paleiskite:
bin/rails zeitwerk:check
Sėkmingas patikrinimas atrodo taip:
% bin/rails zeitwerk:check
Palaukite, aš užkraunu programą.
Viskas gerai!
Gali būti papildomų rezultatų, priklausomai nuo programos konfigūracijos, bet paskutinė „Viskas gerai!“ yra tai, ko ieškote. Jei dvigubas patikrinimas, paaiškintas ankstesniame skyriuje, nustatė, kad iš tikrųjų turi būti keli papildomi automatinio įkėlimo takai už automatinio įkėlimo takų ribų, užduotis juos aptiks ir įspės apie tai. Tačiau jei testų rinkinys sėkmingai įkelia tuos failus, tai gerai.
Dabar, jei yra bet koks failas, kuris nenustato tikėtinos konstantos, užduotis tai jums pasakys. Ji tai daro vienu failu vienu metu, nes jei ji judėtų toliau, vieno failo įkėlimo nesėkmė galėtų išplisti į kitas nesusijusias klaidas, susijusias su vykdomu patikrinimu, ir klaidų pranešimas būtų painus.
Jei pranešama apie vieną konstantą, ištaisykite tik tą ir paleiskite užduotį dar kartą. Kartokite, kol gausite "Viskas gerai!".
Pavyzdžiui:
% bin/rails zeitwerk:check
Palaukite, aš įkeliantis programą.
tikėtasi, kad failas app/models/vat.rb nustatys konstantą Vat
PVM yra Europos mokesčiai. Failas app/models/vat.rb
nustato VAT
, bet automatinis įkėlėjas tikisi Vat
, kodėl?
6.3 Akronimai
Tai yra dažniausia rūšis nesutapimų, su kuriomis galite susidurti, tai susiję su akronimais. Paaiškinkime, kodėl gauname tą klaidos pranešimą.
Klasikinis automatinis įkėlėjas gali automatiškai įkelti VAT
, nes jo įvestis yra trūkstamos konstantos pavadinimas, VAT
, jis iškviečia underscore
funkciją, kuri grąžina vat
, ir ieško failo, kuris vadinasi vat.rb
. Tai veikia.
Naujojo automatinio įkėlėjo įvestis yra failų sistema. Duodamas failas vat.rb
, Zeitwerk iškviečia camelize
funkciją su vat
, kuri grąžina Vat
, ir tikisi, kad failas nustatys konstantą Vat
. Tai sako klaidos pranešimas.
Tai lengva ištaisyti, jums tiesiog reikia pranešti inflektorui apie šį akronimą:
# config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym "VAT"
end
Tai paveikia, kaip Active Support inflektuoja globaliai. Tai gali būti gerai, bet jei norite, galite taip pat perduoti perrašymus į automatinio įkėlėjo naudojamus inflektorius:
# config/initializers/zeitwerk.rb
Rails.autoloaders.main.inflector.inflect("vat" => "VAT")
Turėdami šią parinktį, turite daugiau kontrolės, nes tik failai, kurie tiksliai vadina vat.rb
arba tiksliai vadina vat
direktorijas, bus inflektuojami kaip VAT
. Failas, vadintas vat_rules.rb
, tuo netrikdomas ir gali nustatyti VatRules
visiškai gerai. Tai gali būti naudinga, jei projekte yra šios rūšies pavadinimo nesuderinamumų.
Turint tai vietoje, patikrinimas pavyksta!
% bin/rails zeitwerk:check
Palaukite, aš įkeliantis programą.
Viskas gerai!
Kai viskas gerai, rekomenduojama toliau patikrinti projektą testų rinkinyje. Skyriuje Patikrinkite Zeitwerk atitikimą testų rinkinyje paaiškinama, kaip tai padaryti.
6.4 Susirūpinimai
Galite automatiškai įkelti ir įkelti iš standartinės struktūros su susirūpinimais
subdirektorijomis, pavyzdžiui
app/models
app/models/susirūpinimai
Pagal numatytuosius nustatymus app/models/susirūpinimai
priklauso automatinio įkėlimo takams ir todėl laikoma šakniniu katalogu. Taigi, pagal numatytuosius nustatymus, app/models/susirūpinimai/foo.rb
turėtų nustatyti Foo
, o ne Susirūpinimai::Foo
.
Jei jūsų programa naudoja Susirūpinimai
kaip vardų erdvę, turite dvi galimybes:
- Pašalinkite
Susirūpinimai
vardų erdvę iš tų klasių ir modulių ir atnaujinkite kliento kodą. - Palikite viską kaip yra, pašalindami
app/models/susirūpinimai
iš automatinio įkėlimo takų:
# config/initializers/zeitwerk.rb
ActiveSupport::Dependencies.
autoload_paths.
delete("#{Rails.root}/app/models/susirūpinimai")
6.5 app
turėjimas automatinio įkėlimo takuose
Kai kurie projektai nori, kad kažkas panašaus į app/api/base.rb
nustatytų API::Base
ir prideda app
į automatinio įkėlimo takus, kad tai pasiektų.
Kadangi „Rails“ automatiškai prideda visus app
subdirektorijas į automatinio įkėlimo takus (su keliais išimtimis), turime dar vieną situaciją, kai yra įdėti įdėti šakniniai katalogai, panašūs į tai, kas vyksta su app/models/susirūpinimai
. Taip nustatymas nebeveikia.
Tačiau galite išlaikyti tą struktūrą, tiesiog ištrindami app/api
iš automatinio įkėlimo takų inicializavimo faile:
# config/initializers/zeitwerk.rb
ActiveSupport::Dependencies.
autoload_paths.
delete("#{Rails.root}/app/api")
Būkite atsargūs dėl subdirektorijų, kuriuose nėra failų, kurie turi būti automatiškai įkeliami/įkraunami. Pavyzdžiui, jei programa turi app/admin
su ištekliais skirtais ActiveAdmin, jums reikia jų ignoruoti. Taip pat ir assets
ir panašiai:
# config/initializers/zeitwerk.rb
Rails.autoloaders.main.ignore(
"app/admin",
"app/assets",
"app/javascripts",
"app/views"
)
Be to, programa įkraus tuos medžius. Klaidos pranešimas dėl app/admin
būtų dėl to, kad jo failai nenustato konstantų, ir būtų apibrėžtas Views
modulis, pavyzdžiui, kaip nepageidaujamas šalutinis poveikis.
Kaip matote, turėti app
automatinio įkėlimo takuose yra techniškai įmanoma, bet šiek tiek sudėtinga.
6.6 Automatiškai įkeliamos konstantos ir aiškios vardų erdvės
Jei vardų erdvė yra apibrėžta faile, kaip čia yra Hotel
:
app/models/hotel.rb # Apibrėžia viešbutį.
app/models/hotel/pricing.rb # Apibrėžia viešbučio kainodarą.
Hotel
konstanta turi būti nustatyta naudojant class
arba module
raktažodžius. Pavyzdžiui:
class Hotel
end
yra gerai.
Alternatyvos, tokios kaip
Hotel = Class.new
arba
Hotel = Struct.new
neveiks, vaikinės objektai, tokie kaip Hotel::Pricing
, nebus rasti.
Šis apribojimas taikomas tik aiškioms vardų erdvėms. Klasės ir moduliai, kurie nenustato vardų erdvės, gali būti apibrėžti naudojant tuos idiomus.
6.7 Vienas failas, viena konstanta (tame pačiame viršutiniame lygyje)
Klasikinėje
veiksenos režime techniškai galėjote apibrėžti kelias konstantas tame pačiame viršutiniame lygyje ir visus jas perkrauti. Pavyzdžiui, turint
# app/models/foo.rb
class Foo
end
class Bar
end
nors Bar
negalėtų būti automatiškai įkeltas, įkeliant Foo
pažymėtų Bar
kaip įkeltą taip pat.
Tai netaikoma zeitwerk
režime, jums reikia perkelti Bar
į savo atskirą failą bar.rb
. Vienas failas, viena viršutinio lygio konstanta.
Tai paveikia tik konstantas, esančias tame pačiame viršutiniame lygyje, kaip pavyzdyje aukščiau. Vidinės klasės ir moduliai yra gerai. Pavyzdžiui, apsvarstykite
# app/models/foo.rb
class Foo
class InnerClass
end
end
Jei programa perkrauna Foo
, ji taip pat perkraus Foo::InnerClass
.
6.8 Šablonai config.autoload_paths
Būkite atsargūs, konfigūracijose, kuriose naudojami šablonai, tokie kaip
config.autoload_paths += Dir["#{config.root}/extras/**/"]
Kiekvienas config.autoload_paths
elementas turėtų atstovauti viršutinei vardų erdvei (Object
). Tai neveiks.
Norėdami tai ištaisyti, tiesiog pašalinkite šablonus:
config.autoload_paths << "#{config.root}/extras"
6.9 Klasės ir moduliai iš varikliukų dekoravimas
Jei jūsų programa dekoruoja klasės ar modulio iš variklio objektus, tikimybė, kad ji tai daro kažkur:
config.to_prepare do
Dir.glob("#{Rails.root}/app/overrides/**/*_override.rb").sort.each do |override|
require_dependency override
end
end
Tai turi būti atnaujinta: turite pranešti pagrindiniam
įkėlėjui ignoruoti katalogą su perrašymais, ir juos turite įkelti naudojant load
. Kažkas panašaus:
overrides = "#{Rails.root}/app/overrides"
Rails.autoloaders.main.ignore(overrides)
config.to_prepare do
Dir.glob("#{overrides}/**/*_override.rb").sort.each do |override|
load override
end
end
6.10 before_remove_const
Rails 3.1 pridėjo palaikymą before_remove_const
atgaliniam iškvietimui, kuris buvo iškviečiamas, jei klasė ar modulis atsakė į šį metodą ir buvo perkrautas. Šis atgalinis iškvietimas liko kitaip nesudokumentuotas ir mažai tikėtina, kad jūsų kodas jį naudoja.
Tačiau, jei taip yra, galite pertvarkyti kažką panašaus į
class Country < ActiveRecord::Base
def self.before_remove_const
expire_redis_cache
end
end
kaip
# config/initializers/country.rb
if Rails.application.config.reloading_enabled?
Rails.autoloaders.main.on_unload("Country") do |klass, _abspath|
klass.expire_redis_cache
end
end
6.11 Spring ir test
aplinka
Spring perkrauna programos kodą, jei kažkas pasikeičia. Test
aplinkoje jums reikia įjungti perkrovimą, kad tai veiktų:
# config/environments/test.rb
config.cache_classes = false
arba, nuo Rails 7.1:
# config/environments/test.rb
config.enable_reloading = true
Kitu atveju gausite:
perkrovimas išjungtas, nes config.cache_classes yra true
arba
perkrovimas išjungtas, nes config.enable_reloading yra false
Tai neturi jokio našumo nuostolio.
6.12 Bootsnap
Įsitikinkite, kad priklausote bent jau nuo Bootsnap 1.4.4.
7 Patikrinkite Zeitwerk
atitikimą testų rinkinyje
Užduotis zeitwerk:check
yra patogi migracijai. Kai projektas atitinka reikalavimus, rekomenduojama automatizuoti šią patikrą. Tam pakanka įkelti programą, tai ir daro zeitwerk:check
.
7.1 Nuolatinis integravimas
Jei jūsų projektas turi nuolatinį integravimą, gerai būtų įkelti programą, kai ten vyksta testų rinkinys. Jei dėl kokios nors priežasties programa negali būti nuolat įkelta, geriau žinoti tai nuolatinėje integracijoje nei gamyboje, ar ne?
Įprastai nuolatinės integracijos nustato tam tikrą aplinkos kintamąjį, nurodantį, kad testų rinkinys vyksta ten. Pavyzdžiui, tai gali būti CI
:
# config/environments/test.rb
config.eager_load = ENV["CI"].present?
Nuo Rails 7 pradžios, naujai sukuriamos programos yra tokiu būdu konfigūruojamos pagal numatytuosius nustatymus.
7.2 Paprasti testų rinkiniai
Jei jūsų projektas neturi nuolatinės integracijos, vis tiek galite įkelti programą testų rinkinyje, iškviesdami Rails.application.eager_load!
:
7.2.1 Minitest
require "test_helper"
class ZeitwerkComplianceTest < ActiveSupport::TestCase
test "įkelia visus failus be klaidų" do
assert_nothing_raised { Rails.application.eager_load! }
end
end
7.2.2 RSpec
require "rails_helper"
RSpec.describe "Zeitwerk atitikimas" do
it "įkelia visus failus be klaidų" do
expect { Rails.application.eager_load! }.not_to raise_error
end
end
8 Ištrinkite visus require
iškvietimus
Iš mano patirties, projektai paprastai to nedaro. Bet aš mačiau keletą, ir girdėjau apie keletą kitų.
Rails aplikacijoje naudojamas require
tik tam, kad įkeltų kodą iš lib
arba iš trečiųjų šalių, tokias kaip priklausomybės nuo juvelyrinių akmenų ar standartinė biblioteka. Niekada neįkelkite automatiškai įkeliamo aplikacijos kodo su require
. Žr., kodėl tai buvo bloga idėja, jau čia classic
čia.
require "nokogiri" # GERAI
require "net/http" # GERAI
require "user" # BLOGAI, IŠTRINKITE TAI (tikimasi, kad tai app/models/user.rb)
Prašome ištrinti visas tokių tipo require
iškvietimus.
9 Naujos funkcijos, kurias galite pasinaudoti
9.1 Ištrinkite require_dependency
iškvietimus
Su Zeitwerk visi žinomi require_dependency
naudojimo atvejai buvo pašalinti. Turėtumėte peržiūrėti projektą ir juos ištrinti.
Jei jūsų aplikacija naudoja vienos lentelės paveldėjimą, prašome peržiūrėti Vienos lentelės paveldėjimo skyrių Automašinio įkelimo ir konstantų perkrovimo (Zeitwerk režimas) vadove.
9.2 Kvalifikuoti vardai klasės ir modulio apibrėžimuose dabar yra galimi
Dabar galite tvirtai naudoti konstantų kelius klasės ir modulio apibrėžimuose:
# Automatiškas įkėlimas šioje klasės kūne dabar atitinka Ruby semantiką.
class Admin::UsersController < ApplicationController
# ...
end
Reikia atkreipti dėmesį, kad, priklausomai nuo vykdymo tvarkos, klasikinis automatiškas įkėlimas kartais galėjo įkelti Foo::Wadus
šioje vietoje:
class Foo::Bar
Wadus
end
Tai neatitinka Ruby semantikos, nes Foo
nėra įdėjime, ir visai neveiks zeitwerk
režime. Jei rastumėte tokį kampinį atvejį, galite naudoti kvalifikuotą vardą Foo::Wadus
:
class Foo::Bar
Foo::Wadus
end
arba pridėti Foo
į įdėjimą:
module Foo
class Bar
Wadus
end
end
9.3 Gijų saugumas visur
classic
režime konstantos automatiškai neįkeliamos saugios gijose, nors „Rails“ turi užraktus, pavyzdžiui, kad web užklausos būtų saugios gijose.
Konstantų automatiškas įkėlimas yra saugus gijose zeitwerk
režime. Pavyzdžiui, dabar galite automatiškai įkelti daugiausiai gijų skriptus, vykdomus naudojant runner
komandą.
9.4 Ankstyvasis įkėlimas ir automatinis įkėlimas yra nuoseklūs
classic
režime, jei app/models/foo.rb
apibrėžia Bar
, jūs negalėsite automatiškai įkelti to failo, bet ankstyvasis įkėlimas veiks, nes jis rekursyviai įkelia failus be jokio apmąstymo. Tai gali būti klaidų šaltinis, jei pirmiausia testuojate dalykus, ankstyvasis įkėlimas gali nesėkmingai vykdyti vėlesnį automatinį įkėlimą.
zeitwerk
režime abu įkėlimo režimai yra nuoseklūs, jie klaidos ir klaidos tais pačiais failais.
Atsiliepimai
Jūs esate skatinami padėti pagerinti šio vadovo kokybę.
Prašome prisidėti, jei pastebite rašybos klaidų ar faktinių klaidų. Norėdami pradėti, galite perskaityti mūsų dokumentacijos prisidėjimo skyrių.
Taip pat gali būti nepilnos informacijos arba informacijos, kuri nėra atnaujinta. Prašome pridėti bet kokią trūkstamą dokumentaciją pagrindiniam. Patikrinkite Edge vadovus pirmiausia, ar problemas jau išspręsta arba ne pagrindinėje šakoje. Patikrinkite Ruby on Rails vadovų gaires dėl stiliaus ir konvencijų.
Jei dėl kokios nors priežasties pastebite kažką, ką reikia ištaisyti, bet negalite patys tai pataisyti, prašome pranešti apie problemą.
Ir galiausiai, bet ne mažiau svarbu, bet koks diskusijos dėl Ruby on Rails dokumentacijos yra labai laukiamos oficialiame Ruby on Rails forume.