0x
Applaus

Orne - za 29 dec 2007, 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

Pencil

Orne - vr 04 jan 2008

Je hebt helemaal gelijk :) Mijn fout.

Pencil

Dovadi - vr 04 jan 2008

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.

Pencil

Orne - vr 04 jan 2008

“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!

Pencil

Orne - vr 04 jan 2008

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.

Lamp_off

Dovadi - do 03 jan 2008

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 .. :-)

Pencil

Orne - do 03 jan 2008

@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 :)

Pencil

Matthijs Langenberg - do 03 jan 2008

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.

Pencil

Marcel - wo 02 jan 2008

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











Welkom op Holland On Rails

Het startpunt voor Ruby On Rails in Nederland. Vind de laatste technieken, meningen en nieuwtjes.

Recente Jobs

Die-Hard programmeur!

Houd jij van bowlen en The A-team? Kom dan bij ons werken! Wij ontwikkelen alleen maar in Rails, dus feest! Daarnaast hebben we Herman Miller stoelen!
(Zomaar een leuk project welke we gemaakt hebben: mijnopenid.nl)

@ Holder, Obdam

Stagiaires zijn welkom

voor opdrachten met leading edge open source technologiën. We bieden zorgvuldige en inhoudelijk goede begeleiding.

@ Agile Dovadi, Amsterdam

Bekijk alle jobs »»

Gereedschapskist

Onmisbare tools voor
iedere 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 ;-) Recommend Me

Stephan Kaag

Het eerste Rails coreteam- member uit Nederland? Rails evangelist van het eerste uur.

Paul Engel

Én Rails programmeren én interfaces designen? Je zou hem superman kunnen noemen..

Robbert Dol

Droomt Ruby code, wat anderen een nachtmerrie noemen is voor hem een prettige droom.

Freek Monteban

Het nieuwste telg uit het Holland on Rails nest! Hij doet niets anders meer!