Applaus
Orne - za 29 dec 07 22:03
Bij het maken van een plugin loop ik tegen het volgende probleem.
Ik heb een class (model) SystemWidget. De plugin ziet er als volgt uit
HelloWidget < SystemWidget
…
end
Het leuke is dat ik alle subclasses van SystemWidget middels SystemWidget.subclasses kan bekijken, dit werkt via script/console!
Probleem is echter, onder development worden alle bestanden bij elke request herladen, met uitzondering de plugins. De eerste keer toont SystemWidget.subclasses keurig het object HelloWidget, de tweede request is subclasses leeg. Dit probleem doet zich niet voor via script/console.
Ik ga er vanuit dat dit komt omdat alle classes opnieuw geinitialiseerd worden, behalve de plugins. Hierdoor weet SystemWidget niet meer welke subclasses er zijn. Onder production mode blijft het wel werken.
Het zou fijn zijn als onder development mode ook alle plugins per request opnieuw geinitialiseerd worden.
Er bestaat een config variabele ‘load_once_paths’, deze heeft helaas geen effect bij mij en bij meerdere mensen.
Wie o wie weet het antwoord?
Enkele relevante URLS:
http://weblog.techno-weenie.net/2007/1/26/understanding-the-rails-plugin-initialization-process
http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/f0cfb07dfd5902c1
Welkom op Holland On Rails
Het startpunt voor Ruby On Rails in Nederland. Vind de laatste technieken, meningen en nieuwtjes.Recente Jobs
Gezocht: Ruby On Rails ontwikkelaar (junior of senior)
Eet, drink en droom jij over Ruby On Rails? Wil jij het liefste dag en nacht bezig zijn met jehobby; super coole webapplicaties ontwikkelen in Ruby On Rails?
Dan willen wij jou graag een podium bieden om je Ruby skills te vertonen aan onze nationale en internationale klanten!
@ Internetbureau Holder, Obdam
Bekijk alle jobs »»
Gereedschapskist
Onmisbare tools vooriedere developer!
- Ruby On Rails
Framework voor de web 2.0 developer. Eindelijk vooruitgang! - TextMate
Editor for true pro's
Typ, tab, top :-)
Nee, niet voor Win. - Made On A Mac
En nou is het over met die saaie grijze Windows bak van je!
Auteurs op deze site
Chris Obdam
'Less is more' evangelist, past dit ook dagelijks toe op zijn tandenborstel.Chiel Wester
Snelheidswonder op Ruby wielen. Leuk om mee te pair-programmen ;-)
Orne - vr 04 jan 08 16:56
Je hebt helemaal gelijk :) Mijn fout.
Dovadi - vr 04 jan 08 16:42
Heeft de foutmelding niets te maken met de quotes (als je de regel direct van deze webpagina hebt gekopieerd)? De starting quote “ en closing quote ” zijn op deze pagina verschillend en niet standaard recht " (?) …
Require ‘system_widget’ is eigenlijk een simpeler oplossing. Op deze manier wordt SystemWidget ook niet gewist na het afhandelen van een request.
Orne - vr 04 jan 08 12:26
“Dependencies.autoloaded_constants.delete(“SystemWidget”)” leverde de foutmelding “undefined local variable or method `“SystemWidget”’ for main:Object (NameError)” op, wat wel werkte was bovenaan de init.rb van HelloWidget “require ‘system_widget’”.
Nog even over dat de widgets nu afhankelijk zijn van mijn applicatie. Dit zou omzeild kunnen worden door SystemWidget om te toveren tot een plugin. Maar wellicht is dat iets om me een volgende vakantie mee zoet te houden ;)
Bedankt in ieder geval voor al jullie hulp!
Orne - vr 04 jan 08 12:00
Beste Dovadi:
Heel erg bedankt voor je uitgebreide antwoord. Je uitleg over hoe Rails classes laadt is erg informatief.
De reden dat ik op deze manier te werk ben gegaan is dat ik lange tijd op zoek ben geweest naar een alternatief voor components. Tijdje terug ben ik gaan stoeien met Typo (waar jullie site ook gebruik van maakt zag ik) en zag dat zij een Sidebar systeem hebben gemaakt om dingen als menu, recente blog posts, etc. te tonen. Wanneer je een sidebar in de plugin folder zet, is deze direct in de admin interface te beheren (ook dmv. subclasses).
Doordat veel logica in SystemWidget is opgenomen (bv configuratie, locatie van de widget), leek en lijkt het me beter om het toch op deze manier af te handelen. Daarnaast zullen veel widgets toch afhankelijk zijn van mijn cms, omdat ze bijvoorbeeld zorgen voor het renderen van de menu’s, laatste forum berichten etc.
Dovadi - do 03 jan 08 23:23
Orne,
Waarom maak je een plugin waarin je een subclass creëert op basis van een specifieke class (model) uit je applicatie? Met andere woorden je plugin is NIET zomaar her te gebruiken in een willekeurige andere app, omdat er een superclass met een specifieke naamgeving in je applicatie noodzakelijk is. Alleen om die reden denk ik (net als Matthijs) dat je op een andere manier jouw probleem moet oplossen.
Dit gezegd hebbende, was ik door jouw probleem nieuwsgierig geworden hoe het mechanisme van het herladen van de classes in development mode eigenlijk werkt. En dit is wat ik geleerd heb.
De classes worden geladen aan de hand van de zogenaamde missing_constants method. Rails komt als het ware SystemWidget tegen als een onbekende constant en gaat dan op basis van naamconventie zoeken naar het bijbehorende bestand in de paden zoals vastgelegd in de Dependencies.load_paths array. Afhankelijk van of het in development of production mode is, wordt het load of require mechanisme gebruikt om het gevonden bestand te ‘laden’. In development mode wordt het load mechanisme gebruikt, zodat een bestand als het ware door de ruby interpreter geforceerd opnieuw wordt geladen waardoor de server niet herstart hoeft te worden na een wijziging in de code van een class.
Bij het opstarten van de applicatie worden alle load_paths (in een bepaalde volgorde) vastgesteld, zoals bijvoorbeeld de paden naar de models, controllers etc. (overigens deze worden nog niet geladen!). Daarnaast worden in dit initialisatie proces wel de plugins geladen. In development mode wordt na een request (after_dispatch) alle afhankelijkheden weer gewist, dat wil zeggen alle classes die automatisch mbv het missing_constant method ‘gevonden’ zijn (deze worden bijgehouden in de Dependencies.autoloaded_constants Array). Maar dit geldt niet voor de plugins, deze zijn immers ‘bewust’ (en dus niet automatisch) bij het starten van de applicatie geladen. Dit is ook logisch, immers het betreft boilerplate code die tijdens de ontwikkeling van een applicatie niet gewijzigd wordt.
Jouw applicatie wordt gestart en je plugin wordt expliciet (load_plugins) geladen. Bij de regel class HelloWidget < SystemWidget komt Rails voor het eerst SystemWidget als onbekende constant tegen. Als gevolg daarvan wordt deze geladen en de naam wordt aan autoloaded_constants toegevoegd. Na het eerste request in development mode wordt SystemWidget “verwijderd” (maar HelloWidget niet!). Wanneer nu bij de volgende request in de applicatie aan SystemWidget om de subclasses gevraagd wordt, wordt HelloWidget niet teruggegeven omdat deze al eerder “bestond” en om die reden niet meer bekend is als subclass van SystemWidget.
Als je, ondanks alles, toch wilt vasthouden aan de constructie van jouw plugin, kun je voorkomen dat SystemWidget niet wordt gewist. Dit kun je doen door in het bestand van HelloWidget onderaan de regel “Dependencies.autoloaded_constants.delete(”SystemWidget")" op te nemen. Hierdoor wordt SystemWidget niet gewist na het afhandelen van een request.
Let op: als je de code van SystemWidget in development mode dan wijzigt, dan moet je de server wel herstarten om het effect hiervan te kunnen zien. Maar ach, met TDD of BDD is dat overkomelijk .. :-)
Orne - do 03 jan 08 21:02
@Marcel:
Bij mijn weten laadt Rails de classen automatisch. Het probleem doet zich al voor zonder een nieuw object te initieren (bv @widget = SystemWidget.new).
Wel ben ik op het idee gekomen om er misschien niet voor te zorgen dat de plugins constant herladen worden, maar dat juist de SystemWidget model NIET elke keer herladen wordt per request (onder development mode). Ik ga het dit weekend uitproberen en wellicht is het de oplossing (al zal ik de server in development mode wel moeten herstarten wanneer ik SystemWidget aanpas).
@Matthijs:
In mijn situatie is het juist wel de meest “agile” manier om van subclasses gebruik te maken. Wat ik wil is een widget systeem ala de Typo sidebar, waarbij je de widgets in je plugin folder kunt zetten waarna het CMS systeem ze automatisch kan herkennen middels de SystemWidgets.subclasses.
In ieder geval bedankt voor jullie reacties :)
Matthijs Langenberg - do 03 jan 08 12:31
Ik denk dat je het in een andere hoek moet zoeken: in plaats van het aanpassen van Rails zou je kunnen kijken of er misschien een ander constructie is die je toe zou kunnen passen zodat je niet afhankelijk bent van de subclasses method.
Marcel - wo 02 jan 08 16:20
Ik weet niet of dit relevant is en misschien is het volgende al lang bij je bekend, maar het zou te maken kunnen hebben met de manier waarop Rails de te herladen klassen bepaald. Even een voorbeeld:
Ik maak onder lib de klasse Foo in foo.rb aan. Deze gebruik ik in een controller door Foo.new aan te roepen. Hierbij zal Rails automatisch foo.rb laden en dit ook elke keer opnieuw bij een request doen.
Stel ik voeg in environment.rb deze regel toe:
require File.join(RAILS_ROOT, ‘lib’, ‘foo.rb’)
Hierna zal Rails bij het opstarten foo.rb laden, maar daarna niet meer.
Nu weet ik dus niet of dit met het probleem te maken heeft. Het heeft in ieder geval waarschijnlijk niets te maken met het herladen van de plugins, maar zou wel iets te maken kunnen hebben met het verdwijnen van SystemWidget.subclasses, afhankelijk van hoe die klassen worden geladen.
Plaats je reactie