Vytvořit dynamický podtřídy v Kolejích, který pracuje s STI

0

Otázka

Chci spojit dvě následující věci:

Moje STI strukturu:

Předpokládejme, že Kolejnice model User. Existují pouze dva typy uživatelů, a to Admin a Customer. STI sloupec je typetabulka se nazývá users ale v praxi, jsme nikdy nebude mít a User objekty kolem, jen Admins a Customers.

  • User (používá se pro ukládání všech dat a provádění společné funkce, ale nikdy instanciated)
    • Admin
    • Customer < User

Dynamicky rozšířit modely:

V některých případech, to je užitečné k dynamicky rozšířit model, třída, dočasně mít objekt s více schopnosti provádět konkrétní akce. Převzít můj create správce akce kontroly pro další pole "Odeslat uvítací e-mail" (která je poskytována new formě). Atribut je virtuální, můžeme tedy použít v podobě, jako kdyby to byl normální sloupci. To může být realizováno s:

extended_user_class = Class.new(User)
extended_user_class.send(:include, MyAwesomeMixins)
extended_user_class.class_eval do
  my_virtual_attribute :send_welcome_mail, default: true
end

model = extended_user_class.new
# send model to the view and it "just works"

Kombinace obou

Bohužel kombinace obou těchto technik nefunguje tak, jak je popsáno výše, protože Kolejnice STI se zdá být zlomené anonymní třídy:

[1] pry(main)> User.where(id: 1).to_sql
=> "SELECT `users`.* FROM `users` WHERE `users`.`id` = 1"
[2] pry(main)> Class.new(User).where(id: 1).to_sql
=> "SELECT `users`.* FROM `users` WHERE `users`.`type` IS NULL AND `users`.`id` = 1"

UPRAVIT: Chcete reprodukovat výše uvedené, musí být v produkci env.

Druhé tvrzení je nesprávné, protože filtry podle typu. První prohlášení je zamýšlené chování.

Takže, apearently anonymní třídy jsou špatné pro STI. Předefinování konstantní User jak je popsáno v https://dev.to/factorial/a-trick-with-ruby-anonymous-classes-11pp je pravděpodobné, že zlomit žádosti, - chci, rozšířené třídy, aby být použity pouze při volání explicitně (extended_user_class) - User musí být nedotčena.

Toho lze dosáhnout v Ruby 3 / Kolejnice 6?

anonymous-class ruby ruby-on-rails sti
2021-11-23 17:04:22
1

Nejlepší odpověď

0

Dvě možná řešení:

Singleton třídy

Experimenty ukázaly, že singleton třídy (také známý jako Eigenclasses) by pravděpodobně poskytnout řešení, nicméně tam může být ztráta výkonu až 25%.

Takže součástí dynamicky se rozšiřující model by se stal:

  1. Působí na normální třída Uživatele
  2. Vytvořit instanci nového Uživatele
  3. Přidat virtuální atributy se, že jedna instance pomocí jeho singleton třídy

Patch anonymní třídy předstírat, že to je nadřazené třídy

V tomto řešení, anonymní třídy je rozšířen s mixin, že delegáti následující tři metody třídy k superclass:

  • finder_needs_type_condition?
  • descendants
  • name

Například:

def name
  superclass.name
end

Tímto způsobem, anonymní třídy se stává transparentní a chová se jako třída dědí od. Toto řešení je spíše ošklivý hack, ale zdá se, že práce v praxi. To je ošklivější, ale rychleji než singleton třídy řešení.

Je tam jeden háček, nicméně:

User.all == [admin1, admin2, customer1]
admin1.class == Admin
extended_admin1 = extend_using_patch(admin1)
extended_admin1.class.name == "User"

Jak lze vidět, anonymní třídy extended_admin1 rozšiřuje Userne Admin.

2021-11-26 08:52:57

V jiných jazycích

Tato stránka je v jiných jazycích

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................