Agile Development met Ruby on Rails Casestudie: DieetS(t)imulatie

Maat: px
Weergave met pagina beginnen:

Download "Agile Development met Ruby on Rails Casestudie: DieetS(t)imulatie"

Transcriptie

1 Faculteit Ingenieurswetenschappen Vakgroep Informatietechnologie Voorzitter: Prof. Dr. Ir. P. Lagasse Agile Development met Ruby on Rails Casestudie: DieetS(t)imulatie door Joram Barrez Promotor: Prof. Dr. Ir. H. Tromp Scriptiebegeleider: Dr. K. De Schutter Scriptie ingediend tot het behalen van de academische graad van Licenciaat in de Informatica optie Toepassingsgerichte Informatica Academiejaar

2

3 Faculteit Ingenieurswetenschappen Vakgroep Informatietechnologie Voorzitter: Prof. Dr. Ir. P. Lagasse Agile Development met Ruby on Rails Casestudie: DieetS(t)imulatie door Joram Barrez Promotor: Prof. Dr. Ir. H. Tromp Scriptiebegeleider: Dr. K. De Schutter Scriptie ingediend tot het behalen van de academische graad van Licenciaat in de Informatica optie Toepassingsgerichte Informatica Academiejaar

4 Voorwoord "We will create a civilization of the Mind in Cyberspace. May it be more humane and fair than the world your governments have made before." - John Perry Barlow, 1996 De dag van vandaag is het internet een onmisbaar medium geworden in onze maatschappij. Steeds meer en meer ontwikkelt het internet zich tot hét communicatiemiddel tussen mensen, bedrijven en machines. Het is frappant hoe snel de wereld evolueert rondom ons. Onze nakomelingen zullen terecht komen in een wereld die wij op hun leeftijd ons nooit hadden kunnen inbeelden. Althans, ik toch niet. Toen ik op achtjarige leeftijd mijn eerste computer kreeg van m'n grootvader was een computer een veredelde schrijfmachine die gelukkigerwijs ook toeliet om spelletjes te spelen, destijds nog in zwart-wit. Geen haar op mijn hoofd kon er toen aan denken dat ik dertien jaar later zou afstuderen in een richting die me zal toelaten om brood op tafel te brengen met diezelfde schrijfmachine. Kleine kinderen worden groot, letterlijk en guurlijk. Na die eerste Olivetti IBM kloon volgden door de jaren een reeks van computers, steeds krachtiger en krachtiger. En toen was er het internet. Onder het mom van ik heb het nodig voor school, ging een wondere wereld voor me open. In het begin kwam er van dat schoolwerk uiteraard niet veel in huis. Na de fase van het en, chatten en online gamen ontdekte ik dat het internet rijkdommen aan informatie bezat. Ik begon me in te werken in HTML, deed mijn eerste stapjes BASIC en danste wat rondjes met Java. Ik was op slag verkocht, een coup de foudre zoals de Fransen zeggen. Een studie van deze sprookjeswereld was dan ook de meest logische stap na het bereiken van de wettelijke volwassenheid. Wie wil er nu niet betaald worden voor zijn hobby? Eenmaal op universiteit werd me al gauw duidelijk dat mijn smaak uitging naar het ontwerp van applicaties, naar het scheppen van iets uit het niets met een toverstaf die enkel nullen en enen praat. In de zomer van 2005 viel m'n oog op een nieuwkomer in het landschap van ontwikkelingstools: Ruby on Rails. Het legendarische lmpje waarin met Rails in vijftien minuten een heus blogsysteem wordt gecreëerd, deed mijn mond openvallen van verbazing. Maar zoals dat wel vaker gaat, begint men pas met iets wanneer men er reden toe heeft. Toen de vakgroep GH-SEL in de lente van 2006 dan ook een thesis over Ruby on Rails uitschreef, was de keuze snel gemaakt. De andere twee keuzemogelijkheden die we verplicht dienden op te geven, zijn nooit echt een optie geweest. Groot was mijn blijdschap dan ook toen de thesis daadwerkelijk aan mij werd toegewezen. In eerste instantie wil ik dan ook professor Herman Tromp bedanken om zich bereid te stellen als promotor van mijn thesis. Evenveel en zoniet meer dank ben ik verschuldigd aan mijn ouders en broer, zonder wie ik niet zou zijn wie ik nu ben. Ik besef dat het een voorrecht is om zonder zorgen op te kunnen groeien, vrij toekomstkeuzes te kunnen maken en altijd te kunnen beseen dat er iemand thuis is die me steunt, wat er ook gebeurt. Ik hoop dat ik mijn kinderen ooit hetzelfde zal kunnen schenken als wat ik heb mogen ontvangen van mijn ouders.

5 Uiteraard mag Kris, begeleider van deze thesis, niet vergeten worden. Meermaals heb ik beroep gedaan op zijn kennis, zijn mailbox volgespamd of hem nodeloos bezig gehouden met futiliteiten. Maar altijd nam hij de tijd voor me en beantwoordde grondig mijn vele vragen. Ik had me dan ook geen betere begeleider kunnen wensen. Bedankt Kris. Je hebt me veel bijgebracht, zowel op het vlak van computers als daarbuiten. Wanneer ik Kris vernoem, mag ik natuurlijk Bram en David niet vergeten. Ook zij hebben duidelijk hun stempel gedrukt op zowel dit werk als mezelf. Ik ben werkelijk gepriviligeerd geweest om mijn thesis te kunnen maken in een vakgroep met zulke aangename en capabele mensen. Bedankt Bram, voor de vaak technische hulp en je supersnelle reacties. Ik wens je ongelofelijk veel succes in het laatste jaar van je doctoraat. Bedankt David, voor de ondersteuning en hulp. Maar vooral bedankt voor de gesprekken die we voerden buiten de context van de thesis, het leven is meer dan alleen computers. Ik hoop dan ook dat je dromen van brandweer of lesgeven ooit waarheid worden. Bedankt Stéphanie, Joke, Filip, Jan en Benny voor de voorbije vier jaar. Het is mijn vurigste wens dat het hier niet eindigt. Ten slotte nog even het erepodium uitstallen voor het meisje (of tegenwoordig moet ik al zeggen vrouw) die mijn hart vijf jaar geleden gestolen heeft en het sindsdien nog steeds bij zich draagt. Bedankt Elke, om Elke te zijn. Ik hou ongelofelijk van je. Joram Barrez, Mei 2007

6 Toelating tot bruikleen De auteur geeft de toelating deze scriptie voor consultatie beschikbaar te stellen en delen van de scriptie te kopiëren voor persoonlijk gebruik. Elk ander gebruik valt onder de beperkingen van het auteursrecht, in het bijzonder met betrekking tot de verplichting de bron uitdrukkelijk te vermelden bij het aanhalen van resultaten uit deze scriptie. Joram Barrez, mei 2007

7 Agile Development met Ruby on Rails Casestudie: DieetS(t)imulatie door Joram Barrez Scriptie ingediend tot het behalen van de academische graad van Licenciaat in de Informatica optie Toepassingsgerichte Informatica Academiejaar Promotor: Prof. Dr. Ir. H. TROMP Scriptiebegeleider: Dr. K. DE SCHUTTER Faculteit Ingenieurswetenschappen Universiteit Gent Vakgroep Informatietechnologie Voorzitter: Prof. Dr. Ir. P. LAGASSE Samenvatting In dit werk bespreken we zowel de conceptuele als praktische kant van het Ruby on Rails ("Rails") framework in combinatie met de Agile Development methodologie. Dit werk bestaat grofweg uit twee grote delen. Het eerste deel beschouwt de conceptuele kant van zowel het framework als de methodologie. In hoofdstuk 1 worden de fundamentele concepten rondom Rails en Agile Development geïntroduceerd en uitgewerkt. Hoofdstuk 2 geeft een kort overzicht omtrent de belangrijkste principes van Ruby. In het derde hoofdstuk wordt tenslotte de meer technische kant van het framework behandelt, om zo de bespreking van de praktische implementatie te kunnen aanvatten met een gekend jargon. Het tweede deel bestaat uit de uitwerking van een praktische casestudie om zo de concepten te toetsen aan de praktijk. Hoofdstuk vier schetst kort de context van de te implementeren applicatie. In hoofdstuk 5 wordt de werkelijke implementatie toegelicht, volgens het iteratie-gebaseerd principe van Agile Development. Hoofdstuk 6 bevat tenslotte de conclusies die getrokken kunnen worden door de conceptuele kant uit deel 1 aan te vullen met de ervaringen uit deel 2. Trefwoorden: Ruby, Rails, Agile Development, Web applicaties

8 Agile Development with Ruby on Rails Case study: diets(t)imulation Joram Barrez Ghent University (UGent), Gent, Belgium. Supervisor(s): Prof. Dr. ir. Herman Tromp, Dr. Kris De Schutter Abstract Today, web applications are becoming more popular than ever before. In this quickly changing web based world, it is of great importance to be able to deliver high quality web applications within a short time frame. The Ruby on Rails web framework is a promising framework to cope with these demands. The combination of Ruby on Rails with the Agile Software Development strategy proves to be an even more beneficial solution. The mapping of Rails onto Agile Software Development is presented here and evaluated by means of a case study in a realistic context. Keywords Ruby, Rails, Agile Development, web, framework I. RUBY ON RAILS THE Ruby on Rails framework ( Rails ) was released in the summer of 2004 by David Heinemeier Hansson and has quickly attracted popularity among many web developers. Rails can be used for projects ranging from applications made by home enthousiastics to professional software for large companies. Rails can be best described as an open-source, full-stack framework written in Ruby to create web applications. In the context of web development, full-stack signifies that no other tool, library or framework is required in the development process. The main goal of Rails is gaining as much productivity profit as possible. In the next sections, the Agile Software Development strategy and the principles of Rails are described. Furthermore, the context of the case study is defined as are the results and conclusions of that case study. II. AGILE SOFTWARE DEVELOPMENT Leading literature suggests that the Agile Software Development methodology ( ASD ) is the best option to get optimal results with the Rails framework. ASD is most commonly used as a name for a collection of software development methods which are more lightweight than more traditional approaches. ASD is a logical evolution of developing software in the current economic environment. There is no well-defined approach for using ASD, since ASD is nothing more than a collection of principles. In practice, some of these principles are followed, and some are not. This makes ASD more flexible and easier to introduce in more traditional environments. In most cases however, the following principles are applied when employing the ASD methodology: Customer satisfaction is the number one priority, mostly by rapid software delivery and customer involvement. Developers work in iterations. An iteration is a short timeframe in which one specific functionality is implemented. Much (verbal) communication, both internal and with customers. After each iteration, an evaluation of the result of that iteration is required. The main purpose is to discover misinterpretations and errors early on, and so to decrease the costs associated with them. ASD suggests that each iteration produces a working, yet not fully functional application. Working in iterations in a communicative environment inevitably brings changes to previous decisions. ASD recognizes this fact and proposes the embracement of changes. After all, the final product will only correspond more with the wishes of the customer. It should be noted that every single iteration involves all steps of classical software development: requirement and functional analysis, implementation and testing. The main difference is the scale on which these phases function. III. PHILOSOPHY & CONCEPTS In this section the philosophy and key principles of the Ruby on Rails framework are discussed. Where applicable, these concepts are mapped to the goals of the ASD strategy. A. An opinionated and specific framework Rails is an opinionated but practical web framework. This means that it advocates a certain way of developing web apps and makes sensible default decisions that satisfy most people. It does mean that not all strategies can be supported, and the benefits of Rails can, in certain cases, disappear totally and even become a liability. However, for applications that hold the opinion of Rails (web application with a database back-end), there is a huge productivity boost by that specificity. That boost can ascribed to the fact that Rails can make certain assumptions about the application that is being developed. In most of the principles described next it is clear that this focus on one specific segment of the application market is the key to the productivity gains. B. Fixed Model-View-Controller ( MVC ) architecture Rails trades flexibility on the architectural level for productivity, by losing no time for defining a global architecture. More precisely, in Rails it is not possible to use anything other than the well-known MVC architecture. This strategy fits the ASD philosophy perfectly. In ASD, an architecture is not really designed, since working in iterations does not cope with this idea. This means that with ASD, an architecture will be automatically

9 be created during the iteration by following best practices and intuition. Rails elevates the use of MVC to an even higher level. Every piece of code has a fixed place and all the Rails MVC components are designed to work with each other automatically (without explicitly invoking a view from a controller, for example). Much of the glue code or boiler plate code can be avoided with this automated way of working. C. Convention-over-Configuration Rails uses the principle of Convention-over-Configuration ( CoC ) to allow rapid development. Basically this principle implies that no configuration is needed when the proposed conventions of Rails are followed. These conventions appear throughout every aspect of the framework and can go from minor to high-level automatic shortcuts for what would otherwise be many lines of configuration code. For example, names of models can follow the convention to have the singular form of the name of a database table, with an automatic mapping as result. This seemingly simple convention does raise productivity, since otherwise it would take some configuration time and code to have the same result. Notice that the conventions increase simplicity and maintainability, resulting in less but more predictable code. However, conventions also lower the flexibility of the framework. Rails is opinionated but not forcing, so the conventions are not compulsory. It is worth noting that not using the conventions leads to the loss of many benefits when trying to minimize amount and complexity of code By using the CoC principle, it is clear that software can be delivered more quickly. Naturally, this supports ASD since using less code overall means less code that could be potentially changed. Furthermore, less and simpler code also means that the iterations can be shorter, something that is encouraged in ASD. D. DRY Rails minimizes the amount and complexity of code even further by applying the Don t Repeat Yourself ( DRY ) principle where possible. For example, Rails uses reflection to determine the type and name of the attributes of a model. Since these attributes were already declared in the database schema, it would violate the DRY principle when they are later declared again in the model. E. Short feedback loop The most important asset when trying to cope with change is having a short feedback loop or short turnaround time. This is the elapsed time between making a change, and seeing the impact of that change. In many frameworks, the feedback loop is quite long because they require a compilation or deployment step. Frameworks that minimize the turnaround time, are obviously more favourable from the viewpoint of ASD. Changes in Rails are instantaneous. Rails accomplishes this by using separate environments for development, testing and production. Each environment contains settings to optimize the purposes of the current stage of development. In development mode, all classes are reloaded, caching is turned off and extensive logging is used. Since the feedback loop is so short, Rails copes very well with sudden customers changes. IV. A REALISTIC CASE STUDY A realistic case study was implemented to verify that the combination of Rails and ASD favor a productive development. This case study is based on an application written by the GH-SEL research group for the UZ Ghent. The application is targeted towards patients with kidney malfunctions and allows control of their diets by composing meals based on the patient s past consumptions. Doctors are able to monitor eating habits of their patients and receive a detailed nutrient analysis of their food consumption. V. RESULTS The development of the case study proved that the principles of ASD in collaboration with the Rails framework do lead to a fast implementation without neglecting the quality of the final product. It is also important to note that the use of ASD does not lead to lesser and/or poorly tested architectures, as is sometimes suggested. The architecture is accounted for by Rails itself. This is extremely inflexible from a developer s viewpoint, but it does raise productivity by not spending time on designing and configuration. ASD proved to be a valuable asset to the use of Rails. Testing cannot be enforced, but is encouraged by Rails by auto-generating the skeleton of test classes. The testing framework is completely integrated within the framework and proves to has a very low learning curve. Current trends such as Web 2.0, the use of Asynchronous JavaScript and XML (Ajax) and web services have to be supported by frameworks if they want to make a chance on the market. Rails incorporates many facilities to allow the development of such modern web applications using the same principles as described in earlier sections. A common critique on the Rails framework is the performance aspect. Ruby, hence Rails, is well known for its low performance. This is partly true, but in the case study it proved to be not as dramatic as some pretend it to be. Without optimizations, Rails applications are indeed slow compared to other frameworks or tools. However, with minimal effort the performance speed of the application could be increased nearly 32 times, easily matching the performance to an average level. Furthermore, it was also shown that Rails applications could easily be scaled in a distributed manner. VI. CONCLUSION Rails is all about trading flexibility for productivity. Rails focuses on the particular market of web applications with a database back-end. In this niche it is clear that Rails yields extremely good results. The case study is no exceptional example, as do suggest other sources (for example 37signals.com). The Rails philosophy is clearly influenced by the ideas of ASD. The use of both ASD and Rails proves to be a powerful combination to develop web based applications. The ability to create modern user interfaces can only endorse this fact. As the case study proved, Rails certainly is a rational choice to consider when one needs to develop a web based application.

10 Inhoudsopgave 1 Introductie tot Ruby on Rails Webapplicaties en het World Wide Web Wat is een framework? Geschiedenis Concepten en eigenschappen Opinionated software RAD : Agile (web)development De architectuur : Model-View-Controller Convention over conguration DRY Databank neutraliteit Environments Korte feedback loop Share-nothing architectuur Focus op een niche Nadelen van Rails Ruby Support van hosting providers Internationalisatie (i18n) Performantie Ruby, de taal achter het framework Geschiedenis Filosoe Natuurlijke taal i

11 2.2.2 Principle of Least Surprise (POLS) Technische aspecten Scripting- of programmeertaal? Object-Oriëntatie (OO) Overerving Modules Dynamische typering Reectie Dynamisch uitbreiden van klassen en objecten Waarom Ruby als basis voor Rails? De toekomst : Ruby JRuby Het Ruby on Rails framework Componenten Directorystructuur ActiveRecord Modelklassen Conventies Dynamische nders CRUD Validatie Callbacks & Observers Relaties Uitbreidbaarheid en onderhoudbaarheid Business Logica Kritiek ActionController Principe Views en conventies Filters ActionView Principe View-helpers: reductie van code in templates Concreet voorbeeld: wisselwerking tussen controller en view Conclusie ii

12 4 Rails praktisch toegepast Doelstelling Bespreking van de originele applicatie Situering Functionaliteit Architectuur Agile development in de praktijk Implementatie van de Dieet-S(t)imulatie applicatie Architectuur Iteratie 1: databankontwerp en modellen voor voeding Doelstelling Databankontwerp Migraties Creatie van modelklassen Data migraties: inladen van de Nubel tabel Business logica in modellaag Verandering: Services vs Rails Eenvoudige visualisatie: scaolding Testing Fixtures Het probleem van xtures Evaluatie van de iteratie Iteratie 2: gebruikersbeheer, authenticatie en authorizatie Doelstelling Gebruikersbeheer: model Gebruikersbeheer: controller en views Agile Development: Rails, architectuur en consistentie Persistentie van het wachtwoord Authenticatie Authorizatie Functional Testing Integration Testing iii

13 Scenario Testing Evaluatie Iteratie 3: maaltijd functionaliteit Doelstelling Standaard Rails: Modellen, Controllers en Views Een nieuwe generatie van het web: Web Ajax Visuele eecten RJS Templates Testen & Code Coverage Evaluatie Intermezzo: Rails & webservices Inleiding Service-georiënteerde architecturen & Webservices Aanbieden van webservices via Rails Webservices gebruiken in Rails REST Iteratie 4: Dokter functionaliteit Doelstelling Implementatie Evaluatie Iteratie 5: optimalisatie & deployment Doelstelling Testmethode Testopstelling Invloed van de development environment Overgang naar de standaard Rails productie omgeving Invloed van het platform Eager Loading Transacties Trage View-helpers Invloed van de hardware Caching iv

14 Mongrel Invloed van de hardware (2) Rails & performantie: beginsituatie vs. huidige conguratie Concurrente gebruikers Fysieke distributie en concurrente gebruikers Samenvatting: de applicatie in vogelvlucht Mogelijke uitbreidingen Evaluatie van toekomstbestendigheid Eindbeschouwingen & conclusies Conclusies Eindbeschouwing Bibliograe i v

15 Lijst van afkortingen API: Application Programming Interface AWS: Action Web Service CGI: Common Gateway Interface CSS: Cascading Style Sheets CSV: Comma-Separated Values CVS: Concurrent Versions System CRUD: Create, Read, Update & Delete DAO: Data Access Object DDL: Data Denition Language DOM: Document Object Model DSL: Domain Specic Language DRY: Don't Repeat Yourself ERb: Embedded Ruby FCGI: Fast Common Gateway Interface GH-SEL: Ghislain Homan Software Engineering Lab GUI: Graphical User Interface HTML: HyperText Markup Language HTTP: HyperText Transfer Protocol MVC: Model-View-Controller NUBEL: Nutriënten België OO: Object Orientation ORM: Object-Relational Mapping POLS: Principle Of Least Surprise vi

16 RAD: Rapid Application Development RDBMS: Relational DataBase Management System REST: Representational State Transfer RHTML: Ruby HyperText Markup Language RJS: Ruby JavaScript RXML: Ruby extensible Markup Language SOA: Service Oriented Architecture SOAP: Simple Object Access Protocol SQL: Structured Query Language SVN: Subversion (versiecontrole beheersysteem) TDD: Test Driven Development UDDI: Universal Description Discovery & Integration URL: Uniform Resource Locator W3C: World Wide Web Consortium WSDL: Web Services Description Language WWW: World Wide Web XHR: Xml Http Request XML: extensible Markup Language YAML: Yet Another Markup Language YARV: Yet Another Ruby VM vii

17 Hoofdstuk 1 Introductie tot Ruby on Rails Give a person a sh and you feed them for a day; teach that person to use the Internet and they won't bother you for weeks - Auteur onbekend Ruby on Rails is een full-stack open-source framework voor webapplicaties met een database back-end, geschreven in de programmeertaal Ruby. Full-stack betekent hier dat elke laag die nodig is om een werkende webapplicatie te verkrijgen volledig geïntegreerd is in het framework. Centraal in het framework staat een korte ontwikkelingscyclus en een minimum aan conguratie. Vele van de gebruikte concepten en technieken in het framework zijn niet nieuw, maar worden nu voor het eerst op een unieke manier met elkaar gecombineerd. Rails wist dan ook op zeer korte tijd een grote populariteit op te bouwen. In de komende delen gaan we na of deze populariteit al dan niet gerechtvaardigd is. 1.1 Webapplicaties en het World Wide Web Het Ruby on Rails framework is ontworpen met als doel het eenvoudig ontwikkelen van webapplicaties. Dit mag niet verwonderlijk zijn, gezien de meer dan exponentiële groei van webgebaseerde applicaties de laatste jaren. Het grootste voordeel van webapplicaties is de enorme alomtegenwoordigheid van het platform om deze applicaties te gebruiken. Zowat elk elektronisch toestel met wat rekenkracht heeft vandaag de dag mogelijkheden aan boord om het World Wide Web (WWW) te betreden. Webapplicaties vallen eenvoudigweg niet meer weg te denken uit de huidige maatschappij. Het WWW en webapplicaties onstonden begin jaren '90 als één van de vele toepassingen van een groot netwerk van onderzoekscentra - het internet. Het idee erachter is werkelijk eenvoudig en krachtig tegelijk en kent tot heden geen grote wijzigingen. Centraal staat het client-server paradigma. Men heeft enkele computers, de webservers, welke bepaalde documenten aanbieden naar de buitenwereld. Gebruikers van andere computers in het netwerk, de clients, kunnen deze documenten dan bekijken via speciale software (browser). Eén van de redenen van de enorme doorbraak van het WWW is dat dit alles gestandaardiseerd is. De aangeboden documenten kunnen bereikt worden via een URL (Uniform Resource Locator), worden opgemaakt in het karaktergebaseerde HTML-formaat (HyperText Markup Language) en de communicatie 1

18 tussen client en server wordt geregeld volgens het HTTP (HyperText Transfer Protocol) protocol. De grote kracht van het Web ligt net in de eenheid en eenvoud van deze drie basiscomponenten. Een vervelend probleem van het prille Web was het statische karakter van de documenten. Het toenmalig web was dan in feite niet meer dan een grote verzameling van gedeelde schijven. De oplossing kwam echter al gauw onder de vorm van een vierde standaard, met name CGI (Common Gateway Interface). CGI is het protocol waarmee webservers kunnen communiceren met externe processen en diens output inbrengen in een HTML document. Op deze manier kan men dynamische websites genereren, wat een onbeperkt scala aan mogelijkheden opent. Het is in dit plaatje dat Rails past. Hoewel CGI heden niet zo vaak meer wordt gebruikt omwille van performantieredenen, is het idee nog steeds toepasbaar op Rails en het merendeel van de andere webframeworks. Zoals te zien op guur 1.1, ontvangt een webserver requests van een client, roept eventueel een extern proces (bijvoorbeeld het Rails proces) aan om dynamische content te genereren (bijvoorbeeld data uit een databank halen) en stuurt dit terug naar de client. In de praktijk zal alles via het Rails proces gaan - zelfs statische content - maar er zal wel nog steeds een webserver front-end zijn. Figuur 1.1: Schematische voorstelling van CGI 1.2 Wat is een framework? Een framework bestaat uit een bibliotheek van herbruikbare softwarecomponenten, eventueel programma's of scripts en conventies over hoe de componenten interageren met elkaar. Het doel van een framework is de programmeur te ontheen van zogenaamde general programming, glue code of boilerplate code. Dit is code die in feite niet van belang is voor het eigenlijke doel van de applicatie, maar wel vereist is om het geheel te laten functioneren. Frameworks laten toe dat men zich kan concentreren op de werkelijke applicatielogica. In die zin is het Pareto principe uit de economie ook van toepassing op applicaties. Het Pareto principe stelt dat 20% van de oorzaken verantwoordelijk zijn voor 80% van de gevolgen (Vilfredo Pareto, 1906). 2

19 Toegepast op de ontwikkeling van applicaties, betekent dit dat slechts 20% van de code verantwoordelijk is voor 80% in de variatie van applicaties. Omgekeerd betekent dit dat 80% van de code zorgt voor de resterende 20%. Het is ook zo dat deze 80% vaak code is die terug te vinden is in het merendeel van de applicaties. Deze 80% zal men dan ook terug vinden in frameworks. Frameworks zorgen er dus voor dat men zich kan concentreren op de 20% die werkelijk van belang is voor de applicatie. David Black beschrijft in [1] dat de term framework eigenlijk afkomstig is uit de bouwindustrie. Daar verwijst de term framework naar de eerste fase in de constructie van een bouwwerk. Zoals de naam zegt is het bouwwerk in deze fase een samenhang van frames waar men later de werkelijke constructie op zal bouwen. Het is dus belangrijk dat het framework doordacht en degelijk is. Wanneer de framework-fase van een bouwwerk achter de rug is, ziet elk bouwwerk er hetzelfde uit. Het is pas na deze fase dat het eigenlijke werk begint waarmee de constructie zich onderscheidt van andere bouwwerken. De analogie is duidelijk. Net zoals een framework een inherent deel is van een bouwwerk, is het Rails framework een inherent deel van een Rails applicatie. Alle code van het Rails framework is ook code van de applicatie, en elke applicatie bestaat in de eerste instantie uit enkel code van het framework. Concreet betekent dit dat men zich bezig houdt met zaken die te maken hebben met het doel van de applicatie zelf. Men houdt zich echter niet meer bezig met low-level programming zoals databank connecties, caching, mapping van URL's op methoden, etc. De voordelen en de productiviteitswinst die een framework dus levert vallen zeker niet te onderschatten. 1.3 Geschiedenis Ruby on Rails is niet ontstaan als framework, maar is geëxtraheerd uit een realistisch software project. David Heinemeier Hansson schreef eind 2003 wat hulpklassen in Ruby die hij gebruikte om eenvoudig webapplicaties te ontwikkelen voor zijn werkgever, 37signals 1. De bekendste van deze applicaties zijn Basecamp 2, Backpack 3 en Ta-da List 4, die op het moment van schrijven gebruikt worden door meer dan mensen en bedrijven. Zonder het zich in eerste plaats te realiseren, had hij dus een framework voor webapplicaties gecreëerd. David Heinemeier Hansson besefte uiteindelijk de kracht van zijn framework en besloot het open-source te maken onder de MIT-licentie in juli Sindsdien is hij project maintainer en hoofdprogrammeur van het Rails team. In december 2005 kwam de lang verwachte versie 1.0 uit, welke als een symbolische volwassenwording beschouwd werd. De laatste grote update was versie 1.2.2, daterend van januari Na de lancering in juli 2004 won Rails zeer snel aan populariteit. Eén van de redenen is dat rond die tijd ook de term Web 2.0 ineens populair werd. Web 2.0 is een term die naar voor geschoven werd door O'Reilly Media in 2004 ([9]). Web 2.0 slaat op de evolutie in de ontwikkeling en design van webapplicaties waarbij het web een platform vormt voor applicaties. Populaire zijsprongen zijn bijvoorbeeld het gebruik van Ajax, wiki's, etc. Het feit dat Rails de slogan Web 2.0 ready meekreeg en dit ook daadwerkelijk is (zoals we later zullen zien), liet en laat Rails dus ook meedrijven op de Web 2.0 hype die nu nog steeds aan de gang is

20 1.4 Concepten en eigenschappen In deze sectie gaan we kort in op concepten en eigenschappen die terug te vinden zijn in Rails. Het is allerminst de bedoeling om hier reeds een volledige uitwerking van deze onderwerpen aan te gaan, maar eerder om een bepaald vocabularium op te bouwen om te gebruiken in de komende hoofdstukken Opinionated software David Hansson heeft van bij het openbaar maken van Rails steeds benadrukt dat Rails opinionated software is. Eenvoudig vertaald wil dit zeggen dat het framework een eigen idee heeft over de omgeving waarin het zal gebruikt worden. In de eerste plaats betekent dit dat één van oudste idealen in softwareontwerp, exibiliteit, hierdoor op de helling komt te staan. Rails ruilt exibiliteit op het infrastructuur niveau in voor exibiliteit op het applicatie niveau. Rails probeert niet alle vormen van ontwikkeling of architecturen te ondersteunen zoals dat bij frameworks vaak wel het geval is, maar laat slechts één architectuur toe en beperkt vaak bewust de keuzevrijheid van de ontwikkelaar. De winst die hierdoor echter bereikt wordt is een verhoogde productiviteit. Men hoeft geen keuzes meer te maken die reeds gemaakt zijn door het framework RAD : Agile (web)development RAD staat voor Rapid Application Development en is de alomvattende term voor de strategieën die ontwikkeld zijn als reactie tegen het waterfall model. Het waterfall model is een strategie uit de software engineering voor het tot een goed einde brengen van softwareprojecten en wordt sinds het onstaan in de jaren '70 nog steeds gebruikt. Het Waterfall model wordt echter vaak als inexibel beschouwd. Op guur 1.2 is het waterfall model schematisch weergegeven. De ontwikkeling van een applicatie verloopt volgens duidelijk afgelijnde stappen. Vooral het niet-iteratieve (eerder sequentiële karakter) wordt door velen als een groot nadeel gezien. Men heeft geen gezonde kruisbestuiving tussen de fasen. Fouten die in de eerste fasen naar boven komen, kan men vaak goedkoop oplossen. Fouten in latere fasen hebben vaak een veel grotere kost. Het oplossen van fouten die bijvoorbeeld ontdekt worden in de maintenance stap hebben een kost die 386 keer groter is dan het oplossen in de requirement stap ([3]). Figuur 1.2: Het waterfall model 4

21 Agile development behoort tot de RAD-strategieën en probeert dus ook de problemen van het waterfall model op te lossen. Agile development is nauw verwant aan Extreme Programming 5, maar de richtlijnen zijn realistischer en pragmatischer dan die van Extreme Programming. Het streefdoel is onder meer het risico op het falen van het softwareproject te laten dalen. Hiervoor ontwikkelt men in kleine tijdsperioden, iteraties genoemd, die van enkele dagen tot enkele weken kunnen duren. Elke iteratie heeft één bepaald doel en bezit elke stap uit het waterfall model : requirement analyse, design, testing... Testen is zeer belangrijk en vaak heeft men zelfs tests vóór de code zelf geschreven is, wat men Test Driven Development (TDD) noemt. Het doel van elke iteratie is het aeveren van een werkend product waarbij tijdens de iteratie één bepaalde functionaliteit werd toegevoegd. Dit betekent ook dat het resultaat van elke iteratie moet voldoen aan de kwaliteitseisen alsof het om de nale versie van het product zou gaan. Pas na de iteratie gaat men bepalen wat het doel van de volgende iteratie zal zijn, alles wordt dus gedreven door factoren van het moment zelf. Communicatie onder ontwikkelaars enerzijds en communicatie met de klanten anderzijds spelen een centrale rol in Agile Development. Vele projecten falen immers omwille van onduidelijkheden of misinterpretaties door slechte communicatie. Door echter constant feedback, commentaar of opmerkingen te krijgen van zowel de klant als andere ontwikkelaars kan de applicatie op elk mogelijk tijdstip bijgestuurd worden indien nodig. Rails is ontworpen met de gedachtengang van Agile Development als basis. Rails heeft een korte feedback loop (zie sectie 1.4.8) waardoor iteraties klein kunnen zijn, terwijl men toch nog productief is. Zelfs voor men één regel code geschreven heeft, kan men basisoperaties uitvoeren door gebruik te maken van scaolds (zie sectie 5.2.8). Op elk moment heeft men een volledig werkend product, zij het met beperkte functionaliteit. Door telkens na een iteratie het resultaat te laten evalueren door de klant worden de kosten van veranderingen beperkt. De kost is namelijk meestal gelimiteerd tot de vorige iteratie en de kans dat aanpassingen een invloed hebben op code die reeds zwaar verweven is in de implementatie wordt kleiner. Uiteraard zijn zware aanpassingen niet perfect uitsluitbaar, maar door na iteratie aanpassingen toe te laten zullen de meeste wensen van de klant al ingebracht zijn. Een terechte kritiek is dat Agile development geen rekening houdt met de globale architectuur van een project, net door de korte tijdsperioden en incrementele functionaliteitstoevoeging. Rails vermijdt dit probleem door eenvoudigweg de architectuur van de applicatie vast te leggen, zoals we zullen zien in het volgende punt. Een tekort aan documentatie wordt ook vaak geopperd als bron van kritiek op Agile Development. Men vreest dat de kennis enkel in het hoofd van de ontwikkelaars zit, wat uiteraard een probleem vormt wanneer deze ontwikkelaar het project zou verlaten. Het is wel degelijk zo dat werken met Agile Development minder documentatie produceert dan de meer traditionele methodologieën, maar men mag het hoofddoel van Agile Development niet vergeten. Agile Development stelt werkende software boven bergen aan documentatie. In Agile Development wordt de tijd gespendeerd aan al te veel documentatie omgezet naar tijd waarin men de software verbetert. Immers, als de klant zou mogen kiezen tussen minder functionele software met veel documentatie of sterk functionele software met minder documentatie, is de keuze snel gemaakt. Anderzijds dient men bij Agile Development, net zoals bij elke andere aanpak, voldoende maturiteit te hebben om de software intern voldoende te documenteren. We zullen zien dat Rails, mits het volgen van conventies, veel documentatie over de werking van de software overbodig maakt

22 De term Agile Development onstond in 2001, toen 17 invloedrijke guren uit de softwarewereld het Agile Manifesto 6 opstelden. Merk op dat Agile Development reeds voordien bestond, maar men toen meer algemeen sprak over lichte methodologiën en de aijning niet zo duidelijk was. Merk tevens op dat er niet zoiets bestaat als de Agile Development methode. Agile Development is een verzamelnaam voor verschillende losoën en technieken, maar in de praktijk kiest men vaak voor die subset waarmee men het liefste werkt. Agile Development legt geen restricties op in hoeverre de losoe gevolgd dient te worden om succesvol te zijn De architectuur : Model-View-Controller Model-View-Controller (MVC) is een vaak gebruikt en krachtig design pattern. MVC werd ontwikkeld in 1979 door Prof. dr. em. Trygve Reenskaug bij Xerox PARC voor een project geschreven in Smalltalk. De originele paper dateert reeds van 1979 [11], maar MVC is tot op heden nog steeds één van de meest gebruikte design patterns voor applicaties met een gebruikersinterface (Graphical User Interface of GUI). Het grootste voordeel van MVC is dat een scheiding afgedwongen wordt tussen de opslag van data, verwerking van events en output naar de gebruiker toe. Deze drie componenten zijn respectievelijk het Model, de Controller en de View. Het model draagt de verantwoordelijkheid om de data van de applicatie bij te houden. Deze data zijn in feite de business objecten van de applicatie: producten, gebruikers, een winkelkarretje etc. In webapplicaties is deze data vaak persistent door gebruik te maken van een databank. Merk op dat een model niet noodzakelijk hoeft persistent te zijn. Een winkelwagentje van een online winkel is hier een schoolvoorbeeld van: het is niet zinvol deze gegevens reeds persistent te maken voordat de gebruiker de bestelling daadwerkelijk geplaatst heeft. In Rails is het model bovendien de plaats waar business rules dienen geïmplementeerd te worden die van toepassing zijn op deze business objecten. Dit wordt verder uitgewerkt in secties en De Controller(s) ontvangen events (input van de gebruikers bijvoorbeeld, veroorzaakt via manipulatie van de view), passen het model aan en roepen de juiste view aan. De Controller laag speelt met andere woorden dirigent van het orkest. De View is de gebruikersinterface die de data representeert naar de gebruiker toe. Zoals reeds gezegd, is de grote kracht van MVC de scheiding van deze drie lagen. Het model weet niets van de view(s). Meer zelfs, één model kan verschillende views ontwikkeld met verschillende technologieën ondersteunen. Het hoeft niet gezegd te worden dat de portabiliteit van het model hiermee als grootste voordeel kan gezien worden. De view op zijn beurt kent ook het model niet. De view representeert enkel de data die het ontvangt van de controller. Of dit van een model of rechtstreeks van een databank afkomstig is gaat de view niet aan. De controller moet dan natuurlijk zowel het model als de view kennen, waardoor de controller in feite de lijm is die de stukjes van de puzzel aan elkaar hecht. Voor webapplicaties is MVC dé architectuur om overzichtelijke en duidelijk gestructureerde code te krijgen. In Rails is elke applicatie opgebouwd volgens dit patroon. Meer zelfs, het is niet mogelijk om Rails applicaties te ontwikkelen zonder MVC te gebruiken. Figuur 1.3 toont de typische ow in een MVC webapplicatie

23 Figuur 1.3: MVC in Rails Convention over conguration Het concept van Convention over conguration is het belangrijkste concept in het Rails framework. Vele frameworks proberen zo exibel mogelijk te zijn om een zo groot mogelijk scala aan applicaties toe te laten. Deze frameworks gebruiken bijvoorbeeld conguratiebestanden om deze exibiliteit te kunnen verwezenlijken. Rails gaat hier juist tegen in. Het verwacht bijvoorbeeld eenzelfde directory- en bestandenstructuur in elk Rails project. Klassenamen, methodes, tabellen, tabelkolommen en veel meer dienen een bepaalde conventie qua naamgeving te volgen. Wanneer een view dezelfde naam krijgt als een controller wordt deze automatisch opgeroepen na het uitvoeren van de controllermethode. Deze conventies moeten niet gevolgd worden, maar ze maken de code wel eenvoudiger en het coderen sneller. Indien deze conventies niet gevolgd worden, moet er expliciet gecongureerd worden. Door zich niet enkel vast te pinnen op de conventies en toch conguratie nog mogelijk te laten, beperkt Rails zich niet tot het domein van nieuwe applicaties. Doorheen hoofdstuk 5 zullen de te volgen conventies van dichterbij bekeken worden DRY DRY staat voor Don't Repeat Yourself, een motto dat Rails hoog in het vaandel draagt. Rails is ontworpen zodanig dat elk stukje code slechts één logische plaats heeft. Duplicatie van code leidt namelijk mogelijks tot inconsistentie. Wanneer een verandering dient gedaan te worden aan de applicatie moet de verandering slechts op één plaats gebeuren. De logische plaats voor een bepaald stuk code wordt vaak gesuggereerd door conventies of de vaste MVC architectuur Databank neutraliteit Rails streeft naar databank neutraliteit. Dit wil zeggen dat in theorie Rails eender welke RDBMS (Relational DataBase Management System) kan gebruiken als back-end. Bovendien abstraheert Rails de databank voor de programmeur. De programmeur werkt enkel met object van de model-klassen (objecten van het type ActiveRecord) en Rails zorgt voor de ORM (Object-Relational Mapping), zie ook sectie Dit betekent ook dat een programmeur in principe geen SQL moet gebruiken om te werken met data uit de databank, maar het blijft wel een mogelijkheid indien dit gewenst is. Rails gaat nog een stap verder. Zelfs voor het aanmaken, aanpassen of verwijderen van tabellen dient men de tools van de databankproducent niet te gebruiken. Rails laat toe zogenaamde Migrations te schrijven. Dit zijn scripts, geschreven in Ruby, die de structuur van de databank beschrijven. Rails zal dan de SQL 7

24 genereren om de tabel aan te maken. Het wijzigen van bepaalde tabellen of inladen van data kan ook via migrations gedaan worden. Bovendien wordt automatisch een versienummering bijgehouden zodat men eenvoudig kan migreren (vandaar de naam) naar een vroegere versie indien de aanpassing onnodig blijkt. Migraties worden uitgelegd en geïllustreerd in Environments Rails werkt met verschillende environments of omgevingen. De omgeving is één van de weinige dingen dat dient gecongureerd te worden in Rails, maar zelfs dan voorziet Rails volgende standaard omgevingen: Development: deze omgeving maakt het mogelijk dat aanpassingen aan de code onmiddelijk zichtbaar zijn. De webserver zal hiertoe bij iedere request de klassen opnieuw inladen. Dit heeft natuurlijk een niet te verwaarlozen impact op de performantie maar maakt het ontwikkelen veel makkelijker. In deze omgeving is bovendien caching afgezet, want dit kan soms leiden tot vreemde resultaten. Bovendien heeft deze omgeving meer debug mogelijkheden dan de andere. Production: deze omgeving is geoptimaliseerd voor performantie. Caching staat aan en de code wordt niet herladen wanneer dit niet nodig is. Debugging en foutrapportering zijn ingesteld op een lager niveau. De applicatie moet draaien in deze omgeving wanneer men met de applicatie in productie gaat. Testing: deze omgeving dient enkel en alleen gebruikt te worden tijdens het testen. Deze omgeving is geoptimaliseerd voor het runnen van Test Suites (verzameling van tests). Tussen elke test wordt de databank leeggemaakt en opnieuw ingesteld. Debugging en foutrapportering staan op het hoogste niveau. Dit is op zich niet zo spectaculair. Het grote voordeel van de omgevingen is echter dat Rails voor elke omgeving een andere databank gebruikt en dat wanneer gewisseld wordt van omgeving, Rails automatisch de juiste databank uitkiest. Zo blijft de data van de verschillende omgevingen gescheiden. Het wisselen van omgeving gaat verrassend eenvoudig en is zelfs transparant voor de ontwikkelaar wanneer men zich beperkt tot de standaardomgevingen Korte feedback loop Een belangrijke factor voor de productiviteit van een ontwikkelaar is de zogenaamde feedback loop. De feedback loop is de tijd die verstrijkt tussen het maken van een verandering in de code en die verandering daadwerkelijk zien op het scherm. In Rails is deze feedback loop zeer kort. Dat Rails in feite een allin-one of full-stack pakket is, wordt nogmaals bewezen door de aanwezigheid van een webserver in elk nieuw Rails project die geen regel conguratie vraagt. Deze webserver, WEBrick 7, is een in Ruby geschreven HTTP server die automatisch de laatste versie van de code gebruikt in development omgeving. Anders gezegd: elke verandering aan de code is meteen zichtbaar. Er dient tevens geen compilatie of deployment van code te gebeuren, wat een aanzienlijke tijdswinst oplevert. Het telkens herladen van klassen mag dan wel een positieve invloed hebben op de feedback loop, maar dat komt de snelheid van de applicatie zeker niet ten goede. Het is hiervoor dat de environments uit sectie naar voor komen. Wanneer een applicatie in productie wordt gebracht, zet men gewoon de environment naar production en de webserver zal op volle snelheid requests afhandelen

25 1.4.9 Share-nothing architectuur Schaalbaarheid is een belangrijke factor voor hedendaagse webapplicaties. Schaalbaarheid wil zeggen dat men eenvoudig meer belasting van de server kan opvangen, vaak door extra hardware toe te voegen. Eén van de mogelijke oplossingen om schaalbaarheid te behalen, is gebruik maken van de share-nothing architectuur zoals schematisch weergegeven in guur 1.4. In deze architectuur wordt er niets gedeeld tussen de verschillende (Rails)servers. Elke server kan dan eender welke request afhandelen. Vaak wordt de request opgevangen door een load balancer die de request doorgeeft naar één server via een bepaald algoritme. Dit betekent echter dat de bottleneck verschoven wordt naar een gemeenschappelijk punt in de architectuur. Hoewel de servers niets met elkaar delen, hebben ze toch vaak een gemeenschappelijke datastructuur nodig, in Rails is dit de databank. Hedendaagse databanken zijn echter het resultaat van jaren onderzoek en kunnen een zeer groot aantal concurrente requests behandelen. Er bestaan vele technieken om databanken te clusteren, moest de belasting dan toch te hoog blijken. Feit is dat het niet meer het probleem van de applicatie-ontwikkelaar is, maar dat van de databankontwikkelaar. Figuur 1.4: Share nothing architectuur 1.5 Focus op een niche Een vaak gebruikte slogan voor het Rails framework is meer productiviteit, maar wat maakt Rails productiever dan een ander framework? Een korte analyse van vorige sectie levert al een blik op het antwoord. Rails oert exibiliteit op in ruil voor productiviteit en focust op een niche-markt. Door zich enkel en alleen toe te spitsen op webapplicaties wordt de nood om zelf componenten aan elkaar te lijmen vermeden, omdat er slechts één soort applicaties mee kan gemaakt worden. Een hogere productiviteit door gebruik te maken van conventies alleen zou niet kunnen behaald worden had men zich niet gericht op één type applicatie. Het is juist door zich toe te spitsen, dat men ook bepaalde verwachtingen over de applicatie kan hebben. Een vaak gehoorde kritiek is dan ook dat Rails té speciek is. Want als men bijvoorbeeld Rails zou willen gebruiken om een desktop applicatie mee te bouwen, dan is dit niet mogelijk. Dit is zeker correct, maar dit is niet de losoe die achter Rails zit. Rails probeert een zo goed mogelijk gereedschap te zijn om webapplicaties mee te ontwikkelen, maar meer niet. Rails richt zich dus zeker op één welbepaalde niche, maar wel één die steeds belangrijker wordt met de tijd. 9

26 Een mooie analogie wordt gemaakt door Bruce Tate in [13], waar hij Rails bekijkt uit het standpunt van een java ontwikkelaar: Als ik een goede hamer heb, waar ik perfect mee overweg kan en de kleinste details van ken, dan lijkt het onmogelijk een nog betere hamer te vinden die me dubbel zo productief maakt. Maar mensen die Ruby on Rails vergelijken met een mix van 'general purpose' Java frameworks missen het punt. Je kan tien of meer keer productiever zijn voor sommige problemen door het gereedschap zelf radicaal aan te passen. Professionele bouwvakkers gebruiken nagelpistolen die een dozijn nagels kunnen inschieten in de tijd dat je met een hamer één nagel inklopt. Dit betekent echter niet dat je met een nagelpistool alle taken van een hamer kan doen. Net zoals nagelpistolen gespecialiseerd zijn, is ook Rails gespecialiseerd. Maar alle problemen oplossen ermee lukt niet. 1.6 Nadelen van Rails Geen enkel framework is perfect, en Rails valt zeker niet buiten deze denitie. In deze sectie bespreken we enkele nadelen die zeker moeten overwogen worden indien Rails geëvalueerd wordt voor de ontwikkeling van een bepaald project. Meer specieke nadelen ontdekt tijdens het implementeren zelf, worden behandeld in hoofdstuk Ruby Het feit dat Rails geschreven is in Ruby is op zich geen probleem. Integendeel, Ruby leent zich perfect voor dit doel zoals uit volgende hoofdstukken zal blijken. Maar naar bedrijven toe is Ruby moeilijk te verkopen. Weinig programmeurs kennen de taal en Ruby kent (nog) geen bedrijven zoals Sun, IBM of Microsoft die achter de taal staan en support ervoor bieden. Programmeurs staan vaak weigerachtig tegenover nieuwe talen omdat ze liever werken met wat ze vertrouwd zijn. Bovendien is er ook vanuit de bedrijven zeer veel geïnvesteerd in kennis en infrastructuur voor bestaande talen (Java,.NET,...). Bedrijven staan zeker niet te springen om deze investeringen overboord te gooien Support van hosting providers Om Rails te laten doorbreken bij het grote publiek is het van belang dat hosting providers Rails applicaties ondersteunen. Eén van de redenen dat PHP zo groot geworden is, is net de goede ondersteuning door hosting providers. Iedereen die zin heeft om met PHP dynamische websites te ontwikkelen en deze tentoon te spreiden op het web heeft keuze zat om zijn of haar website te laten hosten. Ook kan men er zeker van zijn dat men een applicatie kan overdragen naar een andere provider zonder veel problemen. Voor Rails is de situatie op hosting vlak echter anders. Weinig hosting providers bieden heden ondersteuning voor Rails. Dit is een grote drempel om Rails te gebruiken, aangezien men dan een eigen serverfaciliteit ter beschikking dient hebben om Rails nog maar te kunnen proberen Internationalisatie (i18n) Internationalisatie slaat op het feit dat software of webpagina's in een andere taal gebruikt of getoond worden naargelang de locatie van de gebruiker. Vaak gebeurt dit zelf zonder dat de gebruiker expliciet 10

27 aangeeft om een andere taal te gebruiken. Vooral voor webpagina's is internationalisatie een interessant middel om een groter doelpubliek te bereiken. Het is dan ook verwonderlijk dat er voor internationalisatie geen directe voorzieningen zijn in Rails. Het probleem ligt in feite in het kamp van Ruby, die nog geen ondersteuning biedt voor Unicode. Dit is enigzins verwonderlijk voor een taal die afkomstig is uit Japan en een Engelse syntax heeft. Er bestaan wel plugins zoals Globalize 8 of Localize 9 die zeer eenvoudige internationalisatie voor Rails mogelijk maken. Helaas is de functionaliteit van deze plugins bedroevend laag, zodat ze op dit moment nog geen waardige optie zijn Performantie Een al te vaak voorkomende kritiek op Rails is het aspect van performantie. Vele weblogs op het internet hameren erop dat Rails zoveel trager is dan pakweg PHP of java frameworks. De kritiek gaat dan steeds naar Ruby zelf, die als één van de traagste talen wordt beschouwd volgens The Computer Language Shootout Benchmarks 10. Performantie is dus ook een mogelijk nadeel van Rails. Maar men moet echter de feiten niet opblazen. Webapplicaties zoals Basecamp en het feit dat enkele universiteiten Rails reeds succesvol gebruiken (bijvoorbeeld universiteit van Notre Dame, Glamorgan, West Virginia, Waterloo en vele andere) bewijzen dat performantie niet het struikelblok is dat het hoort te zijn volgens sommigen. In sectie en verder zullen we zelf nagaan of het om een mythe of waarheid gaat. Men moet ook onthouden dat hardware tegenwoordig goedkoper is dan het loon van een programmeur. Dankzij de share-nothing architectuur kan een Rails applicatie makkelijk bijna oneindig schalen. De kost van extra hardware weegt niet op tegen de productiviteitswinst van de programmeur(s). Tevens moet men ook in het achterhoofd houden dat het gebruiken van Rails, en ook Ruby, voor velen het leren van een nieuwe technologie betekent. Het is dan ook niet meer dan logisch dat men vandaag nog niet het onderste uit de kan haalt omdat eenvoudigweg de ervaring op dit moment ontbreekt. Daarenboven kent Ruby onder invloed van de populariteit van Rails een sterke impuls en is men druk bezig Ruby performanter te maken

28 Hoofdstuk 2 Ruby, de taal achter het framework "Because of the Turing completeness theory, everything one Turing-complete language can do, can theoretically be done by another Turing-complete language, but at a dierent cost. You can do everything in assembler, but no one wants to program in assembler anymore" - Yukihiro Matsumoto, geestesvader van Ruby Het ontwikkelen van een webapplicatie met behulp van Rails betekent dat we in feite tegelijkertijd Ruby code schrijven. Rails leren zonder Ruby te leren is eenvoudigweg onmogelijk. Ruby wordt algemeen gedenieerd als een open-source, object-georiënteerde, geïnterpreteerde scripting taal. In deze sectie staan we even stil bij de taal achter het framework. 2.1 Geschiedenis Men zou kunnen denken dat Ruby, gezien de jonge leeftijd van Rails, ook een vrij recente geschiedenis kent. Het tegendeel is echter waar. De ontwikkeling van Ruby werd gestart op 24 februari 1993 door Yukihiro Matsumoto. Matsumoto was onder de indruk van de kracht en mogelijkheden van scripting talen, maar was van oordeel dat de toenmalige grote scripting talen, Perl en Python, niet voldeden aan zijn eisen. Meer bepaald wou hij een taal ontwikkelen die krachtiger was dan Perl en meer objectgeörienteerd dan Python. De naam Ruby (robijn) werd als zinspeling op Perl (parel) bedacht door een collega van Matsumoto. De eerste publieke versie, versie 0.95, werd op 21 december 1995 uitgegeven. In de periode groeide de populariteit van Ruby in Japan met een ongelofelijke snelheid. Ruby verstootte zelfs Python van de troon als nummer één scripting taal in Japan. Meer dan twintig boeken over Ruby werden in deze periode uitgegeven. Het was echter pas in 2001 dat het eerste niet-japanse boek werd uitgegeven (Programming Ruby: The Pragmatic Programmer's Guide door Dave Thomas). Ruby kende echter niet de groei in het Westen die het kende in Japan. De komst van Rails midden 2004 veranderde echter de hele situatie. Ruby werd gecatapulteerd tot dé hype van 2005 op het vlak van de ontwikkeling van webapplicaties. Statistieken 1 van uitgever O'Reilly tonen aan dat het aandeel verkochte boeken over Ruby het aandeel van zowel Perl als Python reeds overstegen heeft

29 2.2 Filosoe Vermits Ruby begonnen is als de visie van één man, zijn de principes achter de taal van een meer loso- sche aard dan bij de meeste (commerciële) talen. Enkele van deze principes staan haaks op wat wordt beschouwd als algemeen aanvaard voor programmeertalen Natuurlijke taal Het grote probleem van conventionele programmeertalen is volgens Matsumoto het feit dat programmeurs zich te veel moeten concentreren op machinelogica in plaats van het eigenlijke probleem zelf. Men is steeds aan het proberen de code sneller te maken door in het achterhoofd te houden hoe de machine de code zal uitvoeren. Eén van de doelen bij de ontwikkeling van Ruby is een taal te ontwikkelen die dichter bij een menselijke taal ligt. Op die manier wordt een programmeur bevrijd van zaken die niet bijdragen tot de oplossing van het probleem, zoals de uitvoering op de onderliggende machine. De keerzijde van de medaille is uiteraard dat een natuurlijke taal geen taal is die te interpreteren is door machines. Wat natuurlijk klinkt voor mensen, kan niet noodzakelijk vertaald worden naar performante machinecode. In een interview met Bill Venners 2 vertelt Matsumoto dat hij performantie graag inruilt voor vriendelijkheid ten opzichte van de programmeur. Hij is van mening dat, door de Ruby dichter bij de denkwijze van mensen te brengen, men vele malen productiever kan zijn. Programmeurs die productiever zijn, zijn tevens ook gelukkiger, volgens Matsumoto Principle of Least Surprise (POLS) Een leidraad doorheen het ontwerp van Ruby, en nauw aansluitend bij het vorige punt, is het POLS principe. Dit principe zegt dat een taal zich moet gedragen zoals men verwacht. Er mogen dus geen exotische of uitzonderlijke gevallen zijn op de vooropgestelde regels. Ruby probeert zo consistent mogelijk te zijn. Hierdoor is het vaak makkelijker over te schakelen naar Ruby uit een andere programmeertaal, dan over te gaan naar een andere programmeertaal vanuit Ruby. 2.3 Technische aspecten Met lososche aspecten alleen wordt geen programmeertaal ontworpen. Een programmeertaal dient ook een stevige technische basis te hebben om als een waardig alternatief beschouwd te kunnen worden. In deze sectie staan we even stil welke van die aspecten belangrijk zijn voor het Rails framework en welke aspecten verrassend zijn, komende van een voor ons meer bekende taal zoals Java, C# of C Scripting- of programmeertaal? In de inleiding stelden we dat Ruby een scripting taal is. Dit is de denitie die ook terug te vinden is op de ociële website van Ruby. Er zijn volgens John Ousterhout een drietal eigenschappen om na te gaan of een taal een scripting taal is [10] :

30 De programma's geschreven met een scripting taal of scripts laten toe om bestaande componenten, vaak in een andere taal geschreven, op eenvoudige wijze te laten interageren met elkaar. Een scripting taal is in feite een soort van lijm taal die gebruikt worden om bepaalde taken te automatiseren. Scripts zijn dus over het algemeen korte programma's, met een vaak eenvoudige taak. Scripting talen zijn over het algemeen geïnterpreteerd. Dit wil zeggen dat de scripts niet gecompileerd worden naar machinecode, maar de code rechtstreeks at runtime uitgevoerd wordt door een interpreter. Dit betekent ook dat scripting talen per denitie trager zijn dan gecompileerde talen, vermits een compiler over verschillende mogelijkheden beschikt om de code te optimaliseren voor de machine. Door echter niet gebruik te maken van een compiler wordt de editeer-compileer cyclus overgeslagen, wat een tijdswinst voor de programmeur betekent. Scripting talen zijn meestal ongetypeerd. Dit betekent dat variabelen geen type hebben. Dit komt de snelheid van programmeren ten goede, maar heeft een nadelige invloed op de snelheid van uitvoeren. Wanneer we deze enge denitie beschouwen kunnen we niet anders dan Ruby te categoriseren als een scripting taal. De denitie script is echter een erfenis uit het verleden. Scripts werden uitgevonden in een tijd dat computertijd, en dus ook compileren, duur was. Scripts waren korte programma's die een eenvoudige taak uitvoerden, en die snel konden geprogrammeerd worden zonder de dure stap van compileren. Moderne scripting talen zijn vandaag de dag echter niet meer beperkt tot eenvoudige, korte programma's. Het is zelfs zo dat java of C# bytecode in feite wordt geïnterpreteerd, vermits de bytecode at runtime vertaald wordt door de Virtuele Machine naar de onderliggende machinecode. Ruby bevat ook alle eigenschappen die men in hedendaagse programmeertalen terugvindt. Bovendien kan men moeilijk een Rails applicatie nog een script noemen. Matsumoto zelf noemt Ruby geen scripting taal, maar een pure object-georiënteerde taal vermits hij ook vindt dat de term scripting eerder een negatieve bijklank heeft [8] Object-Oriëntatie (OO) Object-Oriëntatie is een paradigma dat toelaat de wereld in computercode te modelleren als een verzameling van interagerende objecten. Meestal is elk object op zijn beurt een instantie van een bepaalde klasse, hoewel dit strikt gezien niet noodzakelijk is. OO is het paradigma dat gebruikt wordt in de meeste moderne programmeertalen. Ruby is een pure object-georiënteerde taal. Met puur bedoelen we hier dat alles in Ruby een object is. Er is dus geen notie van primitieve types (integer, double,...) zoals in andere OO-talen wel het geval is (C++, Java,...). Primitieve types zijn in feite een weergave van de onderliggende machinetaal en dit is net wat Matsumoto wil vermijden. In Ruby zijn bijvoorbeeld gehele getallen dus gewone objecten en is bijvoorbeeld de optelling niet meer dan een methode-oproep. De keuze voor pure OO heeft ook alles te maken met hetgeen we vertelden in sectie 2.2.2, met name consistentie. Wanneer alles een object is, gedraagt ook alles zich als een object. In Ruby is het dus mogelijk om op intuïtieve wijze een lus te deniëren aangezien een getal ook een object is, zoals te zien in onderstaand codevoorbeeld ( getal.times). De uitvoer van dit programma zal zijn. 14

31 1 g e t a l = 10 2 g e t a l. times do i 3 p r i n t i 4 end Codevoorbeeld 1: Getallen zijn objecten in Ruby (lijn 2) Overerving Object-Oriëntatie en overerving gaan meestal hand in hand. Overerving komt erop neer dat klassen gedrag (methodes) en toestand (attributen) overerven van andere klassen, waardoor een klassenhiërarchie ontstaat. Ruby ondersteunt enkelvoudige overerving zoals Java, maar geen meervoudige overerving zoals C++. Dit betekent dat klassen slechts kunnen gedrag en toestand overerven van één andere klasse. Ruby ondersteunt met opzet geen meervoudige overerving, omdat dit vaak tot verwarring kan leiden. Beschouw hiertoe guur 2.1. Klasse B en C erven over van de klasse A. De klasse D erft over van zowel B en C. Wanneer D een methode oproept van de klasse A, van welke klasse is dan deze methode overgeërfd, B of C? Dit probleem staat bekend in de literatuur als het diamond problem, omwille van de vorm van de klassenhiërarchie. Figuur 2.1: Het diamond probleem Verschillende programmeertalen lossen het probleem op verschillende manieren op. Het idee is dat de klassenvolgorde in de vorm van een lijst (linearisatie) moet worden vastgelegd, vertrekkende van klasse D naar klasse A. Er is echter geen standaard oplossing voor dit probleem gedenieerd. Sommige programmeertalen, waaronder Ruby, vermijden om onder meer die reden het gebruik van meervoudige overerving. Meervoudige overerving kan in een zekere zin wel nagebootst worden in Ruby, door gebruik te maken van modules (zie sectie 2.3.4). Overerving speelt een cruciale rol in Rails, zoals meermaals opgetekend in [1]. Beschouw als eenvoudig voorbeeld een modelklasse in Rails, zoals getoond in onderstaande code. Hoewel deze klasse geen eigen logica bevat, is deze modelklasse volledig functioneel dankzij de overerving op regel één. 1 c l a s s Product < ActiveRecord 2 end Codevoorbeeld 2: Een volledig functionele modelklasse in Rails 15

32 Bovenstaande code betekent dat de klasse Product overerft van de klasse ActiveRecord. Op deze manier heeft een Product reeds alle eigenschappen en functionaliteit van ActiveRecord. Het is zelfs zo dat deze klasse eigenlijk al bruikbaar is voor basisfunctionaliteiten die in elke modelklasse teruggevonden kan worden (de zogenaamde CRUD operaties, zie secie 3.3.4). We gaan dieper in op modellen in Rails in sectie Modules Een module is een manier om in Ruby klassen, methoden en constanten te groeperen. Modules in Ruby vervullen twee taken [16] : Het vermijden van naamconicten. In die zin kunnen modules gezien worden als de equivalentie van packages in Java of namespaces in C++. Modules geven mogelijkheid tot de mixin functionaliteit. Het eerste punt is vrij duidelijk: klassen, methoden, constanten, etc. met dezelfde naam in verschillende modules zullen niet conicteren. Het tweede punt is minder duidelijk, daar de mixin functionaliteit in vrij weinig talen voorkomt (bijvoorbeeld wel in Python, Perl en D). Het inmixen van een module in een klasse betekent dat de klasse alle functionaliteit van de module verkrijgt. De functionaliteit kan worden toegevoegd op klasseniveau, vergelijkbaar met static methoden uit bijvoorbeeld Java, of op instantieniveau (objectniveau). Beschouw onderstaande module. Het betreft een module welke een bedrijfseigen encryptie methode implementeert. Merk op dat de implementatie enkel illustratief is. 1 module BedrijfsModule 2 def e n c r y p t e e r ( een_object ) 3 r eturn een_object. to_s. r e v e r s e 4 end 5 end Codevoorbeeld 3: Module die encryptiemethode denieert Veronderstel dat het bedrijf heeft beslist dat toekomstige veiligheidsmodules dezelfde functionaliteit moeten aanbieden. Dit kan door het toevoegen van het keyword extend, waarmee de functionaliteit van de module ingemixt wordt in de klasse. Onderstaand codevoorbeeld illustreert dit. 1 c l a s s NieuweKlasse extend BedrijfsModule # de implementatie doet er n i e t toe 3 end Codevoorbeeld 4: Uitbreiden van functionaliteit via extend Dankzij de extend declaratie bezit de nieuwe klasse de functionaliteit van de module op klasseniveau. Dit betekent dat de uitdrukking NieuweKlasse.encrypteer(een voorbeeld) geldig is. 16

33 Veronderstel dat de module licht gewijzigd wordt naar onderstaande vorm: 1 module BedrijfsModule 2 def e n c r y p t e e r 3 r eturn s e l f. to_s. r e v e r s e 4 end 5 end Codevoorbeeld 5: Nieuwe implementatie van de module Nu is het mogelijk om de module in te mixen op instantieniveau. Het enige wat hiervoor moet gedaan worden is in NieuweKlasse het keyword extend te vervangen door include: 1 c l a s s NieuweKlasse i n c l u d e BedrijfsModule # de implementatie doet er n i e t toe 3 end Codevoorbeeld 6: Uitbreiden van functionaliteit via include Gesteld dat de constructor een string nodig heeft, kunnen we nu de encryptiemethode toepassen op het object zelf: 1 x = NieuweKlasse ( " een_ voorbeeld " ) 2 x. e n c r y p t e e r Codevoorbeeld 7: Functionaliteit wordt ingemixt op instantieniveau Het is duidelijk dat via mixins hetzelfde resultaat kan bekomen worden als met meervoudige overerving. Er is immers ook geen limiet op de modules die kunnen ingemixt worden. Het resultaat zijn de voordelen van meervoudige overerving zonder naamsconicten dankzij modules en vooral zonder de vaak geforceerde hiërarchie die ontstaat bij meervoudige overerving. Indien twee modules toevallig dezelfde methode aanbieden, wordt de methode die in de laatstgedeclareerde module staat gekozen. Ten slotte dienen we nog op te merken dat bovenstaande voorbeelden statische mixins zijn. Het is perfect mogelijk om at runtime een module in te mixen en zodoende een object meer functionaliteit te geven dan andere objecten van dezelfde klasse. Rails maakt veelvuldig gebruik van modules. In feite is het framework op het hoogste niveau slechts een verzameling van modules (ActiveRecord & ActionPack). Van de mixin functionaliteit wordt in Rails eveneens gretig gebruik gemaakt om bepaalde objecten at runtime meer gedrag te geven dan anderen van zijn soort. Zo zijn bijvoorbeeld mixins een manier om plugins voor het Rails framework te schrijven. De complete uitleg is te lang om hier op te nemen, maar de werkwijze is wel interessant om kort te vermelden: 1. Na het laden van Rails wordt nagegaan of er plugins beschikbaar zijn (hebben bij conventie een vaste plaats). Elke plugin bevat steeds een bij conventie vastgelegd bestand waarin conguratieparameters staan. 2. De klassen of objecten waarop de plugin van toepassing is (opgenomen in de conguratie) worden at runtime ingemixt met de plugin, die dus in feite een module is. 17

34 3. De klasse of objecten bezitten nu de functionaliteit van de plugin Dynamische typering Dynamische typering slaat op het feit dat variabelen in Ruby ongetypeerd zijn. Dit betekent dat bij de declaratie van variabelen het niet nodig is om het type van deze variabelen op te geven. Talen die dat wel vereisen, noemt men statisch getypeerd. De dynamische typering in Ruby wordt ook Duck Typing genoemd. Het gedrag van variabelen wordt namelijk niet bepaald door het type, maar door de mogelijkheden van het object achter de variabele. Duck Typing sluit dichter aan bij het menselijk redeneren dan statische typering. De term Duck Typing zelf werd afgeleid van de Duck Test die als volgt luidt: Stel dat men een vogel ziet lopen op de boerderij. Deze vogel heeft geen etiket op zich dat zegt eend. Maar de vogel ziet er zeker en vast uit als een eend. Bovendien, wanneer deze vogel de vijver opzoekt bemerkt men dat deze zwemt als een eend. Als de vogel zijn snavel opent kwaakt hij tevens ook als een eend. De enige logische conclusie is dat de vogel een eend is, of deze nu een etiket draagt of niet. [Immerman, Richard H. (1982), The CIA in Guatemala: The Foreign Policy of Intervention] Het is dan ook niet te verwonderen dat Matsumoto dit heeft gekozen als model voor typering omwille van de menselijkheid. Eén van de doelen is namelijk Ruby zo dicht mogelijk bij het menselijk redeneren te brengen (zie sectie 2.2.1). Beschouw onderstaand voorbeeld van Martin Traverso's weblog 3 dat ons zal helpen om duck typing uit te leggen: 1 c l a s s Car 2 a t t r _ a c c e s s o r : p r i c e 3 end 4 c l a s s Apple 5 a t t r _ a c c e s s o r : p r i c e 6 end 7 8 def c a l c u l a t e T o t a l P r i c e ( item, quantity ) 9 item. p r i c e quantity 10 end car = Car. new 13 car. p r i c e = c a l c u l a t e T o t a l P r i c e ( car, 2) apple = Apple. new 17 apple. p r i c e = 2 18 c a l c u l a t e T o t a l P r i c e ( apple, 12) Codevoorbeeld 8: Duck Typing In de methode calculatetotalprice wordt Duck Typing goed zichtbaar. Zowel objecten van de klasse Car als Apple hebben hetzelfde gedrag (een getter voor price, automatisch aangemaakt door de

35 attr_accessor declaratie) en zodanig is het voor deze methode niet van belang wat het type van de objecten is. Enkel het feit dat er een (getter voor) prijs moet zijn is van belang, zoals te zien is in regels 14 en 18. In statisch getypeerde talen zou een soortgelijk voorbeeld minder makkelijk te realiseren zijn. In Java bijvoorbeeld zouden zowel Car als Apple dezelfde interface moeten implementeren of dezelfde klasse moeten overerven die de methode calculatetotalprice bevat. Dit is echter vanuit logisch standpunt gezien geen correct gebruik van overerving of interfacegebruik, want een auto en een appel zijn op geen logische manier gerelateerd met elkaar. In Java zou de methode calculatetotalprice dus twee maal moeten geschreven worden: éénmaal voor het type Car en éénmaal voor het type Apple als argument. Dynamische typering heeft het aantrekkelijke voordeel dat men verlost is van de typering van variabelen of objecten, wat uiteindelijk weinig bijdraagt tot de eigenlijke applicatielogica. Het nadeel van dynamische typering is echter dat fouten zoals bijvoorbeeld het oproepen van een methode die niet gedenieerd is voor een bepaald object, pas zichtbaar worden bij de uitvoering van het programma. Dit soort fouten zou bij statische typering reeds bij het compileren zichtbaar worden vermits de typering wordt gecontroleerd voor uitvoering. Beide soorten typering hebben dus hun voordelen, maar zeker ook hun nadelen Reectie Reectie is het vermogen van een programmeertaal om tijdens de uitvoering van een programma informatie op te vragen over de structuur en het gedrag van objecten, met andere woorden: over zichzelf. Reectie laat toe dat een programma zichzelf automatisch optimaliseert of aanpast naargelang informatie beschikbaar over zichzelf. Ruby bezit een krachtige maar eenvoudig te gebruiken API voor reectie. Rails maakt veelvuldig gebruik van reectie om bepaalde zaken te automatiseren zoals zal blijken in hoofdstuk 3. Ter illustratie, hieronder drie methoden die door elk object in Ruby kunnen worden uitgevoerd: respond_to(method)? : Deze methode beantwoordt of het object de gegeven methode geïmplementeerd heeft. Rails gebruikt deze methode om methodes on the y aan te maken indien deze niet zouden bestaan. instance_variables : Retourneert een lijst van instanties van variabelen van een bepaalde klasse. Rails gebruikt deze truc om de objecten in de controller zichtbaar te maken in de view. method_missing(method) : Deze methode laat toe om code uit te voeren wanneer een methode wordt opgeroepen die niet gedenieerd is voor een bepaald object. Deze methode wordt gebruikt in Rails om at runtime nieuwe methoden te genereren (zie sectie 3.3.3) Dynamisch uitbreiden van klassen en objecten Een andere techniek die veelvuldig gebruikt wordt in Rails is het uitbreiden van bestaande klassen en objecten [1]. Elke klasse en object kan dynamisch geopend worden tijdens de uitvoering. Dynamisch betekent hier dat de dentie van de uitbreiding niet hoeft te behoren tot de declaratie van de klasse of het object. De klasse of object weet als het ware zelf niet dat ze worden uitgebreid. Uitbreidingen zijn zeer eenvoudig te schrijven: 19

36 1 c l a s s S t r i n g 2 def getfirstthreechars 3 r eturn s e l f [ 0, 3 ] 4 end 5 end Codevoorbeeld 9: Denitie van een nieuwe methode op de klasse String In dit geval breiden we de klasse String uit met een methode die de eerste drie letters van een String teruggeeft. De uitbreiding vindt plaats omdat de klasse dezelfde naam heeft als een reeds bestaande klasse. De zelfgedenieerde methode kan gebruikt worden alsof deze methode in de declaratie van de klasse zelf staat. Het is op deze manier dat Rails bijvoorbeeld de klasse Fixnum heeft uitgebreid om constructies zoals 10.kilobytes, 2.minutes.ago of 7.weeks.from_now mogelijk te maken zoals aangegeven in [17]. Ook objecten kunnen dynamisch uitgebreid worden. Men kan tijdens de uitvoering de functionaliteit van een object uitbreiden door een module te mixen met een object, zoals besproken in sectie Bij conictsituaties, zoals bijvoorbeeld tweemaal de denitie van getfirstthreechars, zal de laatst gekende denitie gekozen worden. Op volledig analoge werkwijze als in het codevoorbeeld kan men ook at runtime methoden toevoegen aan objecten zelf, in plaats van aan klassen. Men noemt de bekomen methoden singleton methoden, daar ze slechts van toepassing zijn op één speciek object. Dynamische uitbreidingen zijn uiterst exibel en interessant om te gebruiken, maar komen met verantwoordelijkheid. Iedereen die dat wil kan deze klasse uitbreiden, zonder dat de oorspronkelijke klasse hier weet van heeft. Dit kan er toe leiden dat eenzelfde klasse toch verschillende functionaliteiten kan aanbieden. Dit is duidelijk het geval voor Rails, waarbij bepaalde methodes niet in de standaardbibliotheek van Ruby zitten. Wil men deze methodes in een gewoon Ruby script gebruiken, zal men resoluut een error krijgen. Men zegt ook vaak dat Rails in feite een Ruby-dialect gebruikt. 2.4 Waarom Ruby als basis voor Rails? Waarom heeft David Heinemeier Hansson gekozen voor Ruby en niet voor de toen meer populaire talen zoals Java, PHP of ASP? Eén van de redenen was dat hij deze conventionele talen grondig beu was. Hij had een grote hoeveelheid ervaring met PHP op professioneel vlak en met J2EE door zijn studies. Hierdoor was hij, zoals hij aangeeft in een interview 4, blootgesteld aan de eigenschap om snel resultaat te zien (PHP) en aan een hoge graad van onderhoudbaarheid en ontwerp (J2EE). Of, zoals hij het zelf zegt de quick-n-dirty en de slow-n-clean aanpak. De symbiose van deze twee vond hij in een zelf ontwikkeld framework gebaseerd op Ruby, welke hij bij toeval ontdekte en absoluut niet gebaseerd was op eventuele (v)oordelen van Ruby. Een ander belangrijk punt is dat het in Ruby zeer eenvoudig blijkt zogenaamde DSL's te ontwikkelen. DSL staat voor Domain Specic Language en wordt door Martin Fowler gedenieerd in [5] als Een computertaal die gericht is op een specieke soort van problemen, in tegenstelling tot een algemene programmeertaal die kan gebruikt worden voor om het even welk probleem. Een voorbeeld maakt deze denitie duidelijk. Stel dat we een een aantal machines hebben die conserven dienen in te pakken naargelang een bestelling. Deze bestellingen kunnen geprogrammeerd worden in pakweg java of C. Echter, voor de bediende van de machine, is het veel eenvoudiger de bestelling te programmeren als volgt :

37 1 naam_bestelling = Colruyt_Aalst_01 2 b e s t e l l i n g = cola, 20 3 fanta, 15 4 blikken_ per_ karton = 12 Codevoorbeeld 10: Mogelijke DSL voor de bediening van een inpakmachine voor conserven Deze code is op het eerste zicht niet geschreven in een programmeertaal. Praktisch heeft men 2 mogelijkheden om zo'n syntax te bereiken. Ofwel ontwikkelt men een eigen taal, met eigen parser en compiler. In het andere geval gebruikt men een bestaande taal en past men de syntax aan naar de syntax van het domein. Ruby ondersteunt het tweede geval en laat dus toe om een syntax te deniëren. In feite staat er Ruby code, maar de syntax is dusdanig aangepast naar het specieke domein. Het bovenstaand voorbeeld maakt enkele zaken omtrent DSL's duidelijk: Er wordt gebruik gemaakt van een vocabularium eigen aan het domein. Begrijpbaar door domeinexperts die geen programmeur zijn. De programma's geschreven in een DSL zijn vaak sneller te schrijven en makkelijker te onderhouden. Zoals we in sectie 1.5 hebben beschreven, is Rails ook een domein-speciek framework. In feite kan men ruimgenomen Rails beschouwen als een DSL waarmee men webapplicaties kan ontwerpen. Een modelklasse in Rails kan bijvoorbeeld zijn: 1 c l a s s Product < ActiveRecord 2 belongs_to : brand 3 has_many : s e l l e r s 4 end Codevoorbeeld 11: Modelklasse in Rails Dit is een zekere vorm van DSL. Er wordt gebruik gemaakt van domein-specieke termen om de relaties aan te geven (regels 2 en 3) en het ziet er op het eerst zicht eerder declaratief dan programmatief uit. Dit is niet alleen snel om te coderen, maar ook eenvoudig om te begrijpen. Rails onderscheidt zich echter van de pure DSL's. Rails kan er dus uitzien als een DSL, maar laat nog steeds de vrijheid om Ruby te gebruiken waar nodig. In feite kunnen we dus beter spreken van een hybride DSL-programmeertaal. Hoe het creeëren van een DSL in Ruby gebeurt zou ons hier te ver leiden, maar wordt extensief en zeer grondig besproken in een online artikel 5 van Jim Freeze. In sectie zullen we een eigen eenvoudige DSL implementeren, waar de bovengenoemde uitleg op toegepast kan worden. 2.5 De toekomst : Ruby 2.0 Een programmeertaal evolueert met de tijd, net zoals een natuurlijke taal. De huidige versie van Ruby (de 1.8.x tak) ziet men als de laatste stap alvorens een nieuwe weg in te slaan. De nieuwe Ruby zal een

38 complete herziening van de taal betekenen. De herziening van de taal zelf noemt men Ruby 2.0, terwijl de implementatie Rite genoemd wordt. Dit betekent dat Ruby 2.0 in feite een specicatie zal worden, en Rite de implementatie van die specicatie. De huidige development versie, 1.9.0, wordt gezien als een experiment alvorens over te gaan tot Ruby 2.0. Enkele eigenschappen van Ruby 2.0: Een bytecode virtuele machine in plaats van een interpreter : YARV 6 (Yet another Ruby VM), ontwikkeld door Sasada Koichi. Hoewel YARV zeker nog een lange weg af te leggen heeft, is het duidelijk in benchmarks (zoals bijvoorbeeld gedaan door Antonio Cangiano 7 ) dat YARV op dit moment reeds enkele factoren eciënter is dan de traditionele Ruby interpreter. Performantiewinst voor Ruby betekent automatisch performantiewinst voor Rails. Een aangepaste syntax. Versies 1.9.x van de huidge Ruby beta versies gebruiken al veel nieuwe syntaxelementen die waarschijnlijk in Ruby 2.0 ook zullen komen. Dit betekent echter ook dat Ruby 2.0 niet compatibel zal zijn met oudere versies. Onregelmatigheden die in Ruby geslopen zijn door de evolutie in de tijd worden er uitgehaald. Let wel dat Matsumoto al reeds enkele jaren praat over Ruby 2.0. Er is nog geen datum gepland wanneer Ruby 2.0 daadwerkelijk publiek zal gemaakt worden. Indien Ruby 2.0 de beloofde verbeteringen zal brengen, zal de Rails community zonder twijfel deze nieuwe versie omarmen. 2.6 JRuby Een interessant project, gestart door Charles Nutter en Thomas Enebo en nog zeker het vermelden waard, is het JRuby project 8. JRuby is een open-source implementatie van de Ruby interpreter op het Java platform. Dit betekent dat Ruby code in feite at runtime vertaald wordt naar bytecode voor de JVM, alvorens op de JVM uitgevoerd te worden. Op deze manier kan Ruby code uitgevoerd worden zonder de Ruby interpreter. Meer details zijn beschreven door Bruce Tate in [14]. Gezien het Java platform reeds een grote gebruiksbasis kent en de stabiliteit ervan zich reeds bewezen heeft, kan men argumenteren dat één platform (de JVM) voor verscheidene talen een interessante evolutie is (naar analogie van het.net platform). Maar JRuby is meer dan dat. JRuby laat namelijk ook toe om Java code te schrijven in een Ruby script. Dit betekent dat Ruby programmeurs dankzij JRuby toegang hebben tot de immense verzameling aan Java bibliotheken. De hoeveelheid bibliotheken die geschreven is in Java overtreft vele malen die van Ruby en het zou zonde zijn deze expertise te verliezen. JRuby is nog niet klaar om gebruikt te worden voor productie doeleinden. Het is weliswaar reeds gelukt om Rails te laten werken op JRuby, maar er zijn zeker nog tekortkomingen. Wanneer men er in slaagt Rails te laten lopen op een J2EE applicatie server, welke reeds aanwezig is in vele bedrijven, wordt de drempel voor Rails alweer een stapje verlaagd

39 Hoofdstuk 3 Het Ruby on Rails framework "Ruby on Rails is astounding. Using it is like watching a kung-fu movie, where a dozen bad-ass frameworks prepare to beat up the little newcomer only to be handed their asses in a variety of imaginative ways." - Nathan Torkington, O'Reilly Media Inc. 3.1 Componenten In sectie 1.2 hebben we besproken wat een framework is. Rails is in feite niet meer dan een set van componenten geschreven in Ruby die op goed omschreven wijze interageren met elkaar. Vooraleer we van start gaan met een praktische implementatie met behulp van Rails in hoofdstuk 4, bespreken we in dit hoofdstuk de meer technische kant van het framework. De drie belangrijkste componenten van Rails zijn ActiveRecord, ActionController en ActionView en vervullen de rol van respectievelijk het model, controller en de view zoals gedenieerd in sectie Figuur 3.1 geeft een schematische weergave van het Rails framework. Pijlen in dit schema duiden op een interactie. Op de guur zijn volgende componenten te zien: Relationele DBMS: Rails werkt met een relationele databank als back-end om persistentie te bekomen. Zoals beschreven in sectie ondersteunt Rails een breed scala aan databanken. Bovendien abstraheert Rails de werkelijke databank door gebruik te maken van de ActiveRecord component die alle databankgerelateerde zaken verbergt. De programmeur kan dus volledig werken op objectniveau, zonder ooit van de databank besef te hebben. Web server: heeft als taak de inkomende aanvragen voor een bepaalde webpagina af te handelen. Om de dynamische inhoud te genereren wordt de aanvraag doorgegeven aan de applicatie ontwikkeld met Rails. Rails verwerkt de aanvraag en geeft een statische webpagina terug aan de webserver. Deze stuurt die dan door naar de gebruiker. ActiveRecord, ActionController en ActionView: deze drie componenten vormen de belangrijkste onderdelen van het Rails framework. Deze componenten zijn perfect op elkaar afgesteld 23

40 Figuur 3.1: Componenten van het Rails framework (uit [1]) zodanig dat zo min mogelijk code nodig is om de componenten te laten samenwerken. We behandelen deze componenten extensief in de volgende secties. CGI (Common Gateway Interface) bibliotheek: zoals voor scripting talen gebruikelijk is, bevat ook Ruby een zeer goede bibliotheek voor CGI ondersteuning. Zoals besproken in sectie 1.1 is CGI is een standaard ontworpen om aanvragen door te sturen van de webserver naar een externe applicatie. Op deze manier kan een webserver dynamische inhoud creëren. Een webserver kan CGI gebruiken om Ruby programma's uit te voeren (en dus ook Rails). Het nadeel van CGI is dat voor elke request een apart proces op de server dient opgestart te worden. Het FCGI (Fast CGI) protocol lost dit probleem op door meerdere processen draaiende te houden en één proces de requests te laten verdelen over de andere. Rails biedt ondersteuning voor zowel CGI als FCGI, waarbij de laatstgenoemde duidelijk performanter is. Zowel CGI als FCGI worden compleet verborgen voor de programmeur. ERb: staat voor Embedded Ruby en is een programma geschreven in Ruby door Seiki Masatoshi. ERb laat toe om Ruby code te schrijven in een HTML le. De Ruby code staat tussen speciale tags ( <% %> of <%= %>) en ERb zal de code binnen de tags uitvoeren wanneer de HTML le gelezen wordt. Op deze manier kan dynamische inhoud gegenereerd worden binnenin een statische HTML le alvorens deze naar de webserver wordt doorgegeven. ERb is vergelijkbaar met PHP, JSP of ASP. ERb is dus geen zelfstandige programmeertaal, maar een programma dat een.rhtml le inleest en de Ruby code die in de tags staan uitvoert. ActiveSupport, ActionWebService, ActionMailer: Dit zijn standaard componenten van Rails welke in tegenstelling tot de drie hoofdcomponenten (ActiveRecord, ActionController en ActionView) zullen gebruikt worden afhankelijk van de vereisten van de applicatie. ActiveSupport is in 24

41 feite niets meer dan een set van bibliotheken welke gedeeld worden door alle Rails componenten. ActiveSupport breidt bestaande Ruby objecten uit met functionaliteit die handig zou kunnen zijn voor Rails applicaties: tijd en datum uitbreidingen (bijvoorbeeld Time.now.next_week.at_midnight), uitbreidingen voor getallen (bijvoorbeeld 20.kilobytes), ondersteuning voor Unicode (wat niet standaard in Ruby zit), etc. ActionWebservice en ActionMailer tenslotte zijn twee componenten welke ervoor zorgen dat een Rails applicatie ondersteuning kan hebben voor respectievelijk webservices en . ActionWebService wordt verder behandeld in Directorystructuur Tijdens de creatie van een nieuwe Rails applicatie wordt een vaste directorystructuur gegenereerd. Dit lijkt triviaal, maar deze structuur is cruciaal voor de werking van Rails. Meer bepaald is dit een voorbeeld van Convention over Conguration zoals besproken in Elk bestand nodig voor de applicatie heeft welgeteld één plaats waar deze mag komen. Door deze plaats vast te leggen bij conventie verdwijnt de noodzaak om de plaats van een bepaald bestand vast te leggen via een conguratie. Een controller klasse weet bijvoorbeeld perfect waar de corresponderende view, met bovendien nog een naam volgens conventie, zich bevindt. Men kan deze directorystructuur vergelijken met het packagesysteem in Java of C++, waarbij in dit geval de packages vast zijn. 3.3 ActiveRecord Rails gaat ervan uit dat persistente informatie wordt bijgehouden in een relationele databank. Deze databank vormt dus het datamodel in de MVC architectuur. In plaats van echter rechtstreeks SQL te gebruiken om de databank te bevragen, bevat Rails een ORM (Object-Relational Mapping) laag die alle SQL verbergt voor de programmeur. Grofweg kan men zeggen dat bij een ORM een tabel gemapt wordt op een klasse en records op objecten. Deze laag wordt in Rails ActiveRecord genoemd. ActiveRecord encapsuleert bovendien de toegang tot de databank en voegt domeinlogica toe aan het model. Belangrijk is op te merken dat ActiveRecord een alleenstaande component is en niet gekoppeld is met ActionPack. Men kan ActiveRecord gebruiken in elke applicatie die een ORM nodig heeft en niet volledig Rails wenst te gebruiken Modelklassen Modelklassen zijn klassen die de modellaag vormen. In Rails zijn dit de klassen die overerven van de ActiveRecord klasse. Men maakt bovendien gebruik van overerving en reectie om het merendeel van het sleutelwerk achter de schermen uit te voeren. Stel dat we een tabel products in de databank hebben met als attributen naam, prijs, etc. Een modelklasse, die tevens perfect functioneel is, ziet er bijvoorbeeld als volgt uit : 1 c l a s s Product < ActiveRecord : : Base 2 end Codevoorbeeld 12: Perfect functionele modelklasse in Rails 25

42 Deze klasse erft over van de Base klasse in de ActiveRecord module (eerste regel), die een onderdeel is van het Rails framework. Objecten van deze klasse stellen een record voor in de tabel products. Door gebruik te maken van overerving en vooral van reectie is deze klasse slechts twee regels groot, maar kan ondermeer al volgende basisfunctionaliteit aanbieden: Product.nd(): Deze methode laat toe om objecten van het type Product op te halen uit de databank. Zo zal bijvoorbeeld Product.nd(:all, :order => 'DESC') een lijst retourneren van alle producten in de databank in omgekeerde natuurlijke ordening. Deze methode is wellicht de meest gebruikte methode van een ActiveRecord object en vereist geen regel eigen code. Product.save(): Deze methode maakt veranderingen aan een bepaald ActiveRecord object persistent. Dit komt met andere woorden overeen met een SQL INSERT operatie. product.attribute: alle attributen kunnen gelezen en veranderd worden zonder dat de attributen ergens zijn aangegeven. Rails maakt gebruik van reectie en informatie in het databankschema om te bepalen wat de attributen zijn en wat de types van de attributen zijn. Dit is een uiting van het DRY principe (zie sectie 1.4.5): de denitie van de attributen is reeds gebeurd in de denitie van de tabel, dus zou het tegen het DRY principe zijn om deze nogmaals te herhalen. Merk op dat we ook nergens expliciet hebben aangegeven in welke tabel de producten zitten. Dit wordt geregeld door gebruik te maken van naamconventies en reectie Conventies Het Convention over Conguration principe komt sterkst naar voor in ActiveRecord. Volgende conventies worden de facto gebruikt: Tabelnamen zijn in het meervoud, namen van modelklassen in het enkelvoud. Op deze manier weet Rails perfect welke tabel wordt gemapt op de modelklasse, zonder dit expliciet te congureren. De modelklasse Product wordt dus automatisch gemapt op de tabel products. Dit ogenschijnlijk eenvoudig principe zou anders conguratie vragen die nu niet vereist is. De primaire sleutel van een tabel is steeds id genaamd en is van het type integer. Rails maakt extensief gebruikt van dit veld, maar als ontwikkelaar dient men er nooit rekening mee te houden. Een vreemde sleutel heeft steeds volgende vorm: naamreferentietabel_id. Relaties worden door middel van deze conventie bekomen. Wederom komt men als ontwikkelaar nooit in aanraking met dit attribuut, tenzij bij het databankontwerp. Deze drie conventies zorgen ervoor dat heel wat conguratiewerk vermeden wordt. Merk op dat deze conventies kunnen gevolgd worden, maar niet moeten gevolgd worden (bvb via set_table_name of set_primary_key). Het volgen van deze conventies bespaart echter wat tijd, wat tot een hogere productiviteit leidt. 26

43 3.3.3 Dynamische nders De nd methode (ook nder genoemd) is wellicht de meest gebruikte methode van een ActiveRecordklasse. Deze methode is zeer krachtig daar het een zoekoperatie in de databank in een meer menselijke vorm uitdrukt. De onderliggende code van het Rails framework zal dan deze uitdrukking omzetten in een SQL SELECT operatie, waarbij de resulterende tabelrijen worden omgezet naar een collectie van ActiveRecord objecten. Hoewel een nder reeds een grote verbetering is ten opzichte van SQL, kan het toch nog behoorlijk complex worden: 1 product = Product. f i n d ( : a l l, : order => name, : c o n d i t i o n s => p r i c e < 50 and p r i c e > 20, : l i m i t => 10, : o f f s e t => 5) Codevoorbeeld 13: Een complexe nder Een deel van de complexiteit wordt opgelost door dynamische nders. Stel dat we bijvoorbeeld een Product model hebben met naam en prijs als attribuut. Een dynamische nder kan dan bijvoorbeeld zijn: 1 Product. find_by_name ( naam) Codevoorbeeld 14: Dynamische nder Deze methode is niet terug te vinden in de klasse Product, maar zal toch het gewenste resultaat geven. Rails gebruikt hiervoor reectie om te bepalen of de methode bestaat voor Product ( een goede uitleg hieromtrent wordt gegeven door Josh Susser op zijn weblog 1 ). Er wordt gebruik gemaakt van de methode method_missing. Het gebruiken van een methode komt in feite neer op het sturen van een bericht met een bepaalde naam naar een object. Conceptueel, wanneer een object zulk bericht ontvangt zal gezocht worden via het method lookup path welke methode dient uitgevoerd te worden. Als er echter geen methode gevonden wordt die voldoet aan de vereisten, dan zal een NoMethodError exceptie opgegooid worden, tenzij er voor het object een method_missing is gedenieerd. In dat geval zal method_missing worden uitgevoerd. Beschouw bijvoorbeeld onderstaande klasse: 1 c l a s s C 2 d e f method_missing ( method ) 3 puts "Onbekende methode! " 4 end 5 end Codevoorbeeld 15: Opvangen van oproepen naar onbestaande methodes We kunnen dit testen door bijvoorbeeld C.new.onbestaandemethode() uit te voeren. gegenereerd worden, maar de code in method_missing wordt uitgevoerd. Er zal geen fout Dynamische nders werken op precies dezelfde manier. Wanneer een onbestaande methode (bijvoorbeeld nd_by_name) gebruikt wordt op een ActiveRecord object, zal method_missing opgeroepen worden. Op basis van de tekst na het keyword nd_by wordt dan een nder aangemaakt. In dit geval zal de

44 dynamische nder vertaald worden naar de gewone nder Product.nd(:all, :conditions => name =?, naam) volgens het principe van dynamische uitbreiding van objecten (zie sectie 2.3.7). Daar dit uiteraard gepaard gaat met ink wat overhead zal Rails het bekomen resultaat cachen wanneer men in de toekomst nog de dynamische nder zou gebruiken op een ander soortgelijk object. ActiveRecord laat bovendien ook dynamische nders toe op basis van meerdere attributen: 1 Product. find_by_name_and_price (naam, ) Codevoorbeeld 16: Dynamische nder voor twee attributen Dynamische nders zijn dus een krachtig hulpmiddel van ActiveRecord die de leesbaarheid en onderhoudbaarheid van de code verhogen CRUD CRUD staat voor Create, Read, Update & Delete en verwijst naar de basisoperaties op een databankrecord. Deze operaties zijn eenvoudig uit te voeren met ActiveRecord zonder ooit één letter SQL te moeten typen. Het zijn ook deze operaties die het meest worden gebruikt in de applicatielogica. CRUD operaties zijn daarom automatisch voorzien door Rails door gebruik te maken van reectie. Onderstaand stuk code geeft de CRUD operaties weer. Zoals duidelijk te zien is, is simpliciteit de grootste troef van deze uitdrukkingen. 1 product = Product. new 2 product. name = DVD s e i z o e n 1 3 product. p r i c e = product. save #Create 5 product2 = Product. find_by_name (DVD s e i z o e n 1 ) #Read 6 product2. update_attribute ( : p r i c e, ) #Update 7 product2. d estroy #Delete Codevoorbeeld 17: CRUD operaties Validatie In applicaties met een databank, zoals de meeste webapplicaties, is het nodig om gebruikersinvoer te controleren alvorens de data op te slaan in de databank. Vaak vindt deze controle of validatie plaats in een controller. In Rails behoort deze validatie echter tot het model. Op vele discussiegroepen op het internet wordt dit aangehaald als een negatief punt van ActiveRecord, waar men het dan heeft over een tight coupling tussen de validatieregels en het model. De reden om validatie op te nemen in de modellaag berust echter op consistentie. Stel dat men een bepaald formulier heeft waarmee gebruikers bepaalde invoer kunnen ingeven. Indien validatie buiten het model zou gebeuren, moet men een entiteit voorzien die gekoppeld wordt aan het formulier en deze regels beschrijft. In de software wereld is het hergebruik van componenten meer regel dan uitzondering. Indien we de integriteit van ons model willen garanderen moeten we dezelfde regels dus opnemen in het model, om niet voor problemen te zorgen als het model op zichzelf wordt gebruikt. Het DRY principe indachtig, 28

45 is het dus niet meer dan logisch dat de denitie van de validatieregels op één plaats gebeurt. In dat opzicht kunnen validatieregels beschouwd worden als gewone business logica behorende bij het model. Zoals we later zullen zien (sectie 5.2.6), vormt de modellaag in Rails eigenlijk ook een business logica laag. Elke ActiveRecord klasse kan voorzien worden van drie methoden die voorzien in de validatie: validate(), validate_on_create() en validate_on_update(). Deze methodes zullen respectievelijk steeds opgeroepen worden voor een save, new en update operatie en controleren of het ActiveRecordobject niet conicteert met de beschreven validatie regels. Vermits validatie quasi altijd voorkomt in elke webapplicatie en deze validatie vaak gelijkend is qua aard, voorziet Rails in een aantal standaard methoden voor validatie. Onderstaand voorbeeld toont dat door regel twee toe te voegen, een Product niet kan gepersisteerd worden zonder een prijs. Merk op dat we hiermee toelaten dat een prijs nul of negatief is. Om dit te vermijden dienen we een andere standaard validatiehelper toe te voegen op regel drie. 1 c l a s s Product < ActiveRecord : : Base 2 validates_presence_of : p r i j s 3 v a l i d a t e s _ n u m e r i c a l i t y _ o f : p r i j s, : only_integer => true 4 end Codevoorbeeld 18: Validatiehelpers voor een modelklasse Een volledige lijst van validatiehelpers kan teruggevonden worden in [17] Callbacks & Observers De levenscyclus van modelobjecten wordt beheerd en bijgehouden door Active Record. Callbacks zijn methodedenities die kunnen gedeclareerd worden om op te treden vóór of na een bepaalde levenscyclusgebeurtenis. Zo zal bijvoorbeeld de code in before_save steeds uitgevoerd worden voor het opslaan van een modelobject. ActiveRecord voorziet standaard in een twintigtal vaste callbacks voor elk model object. Het grote nadeel van callbacks is dat ze de modelcode vaak vervuilen met logica die in feite niet hoort bij het domein object. Het schoolvoorbeeld hiervan is het toevoegen van geavanceerde logging via de callbacks. Dit nadeel werd gelukkig in een latere versie van Rails opgelost met de komst van Observers. Deze klassen hechten zich transparant aan een bepaalde modelklasse en registreren zich voor gebeurtenissen van de levenscyclus zonder dat de modelklasse hier weet van heeft. Op die manier heeft men een duidelijke scheiding tussen de domeinlogica en de even noodzakelijke niet-domeinlogica zonder de geest van het MVC patroon te schaden. In feite kan de functionaliteit die Observers bieden het best vergeleken worden met de technieken uit aspect-oriented programming, waarbij het ook mogelijk is om transparant code te injecteren op bepaalde momenten in de uitvoering Relaties Vaak zijn de business objecten van een applicatie op één of andere manier geassocieerd met elkaar. Op databank niveau betekent dit dat bepaalde tabellen geassocieerd zijn door middel van foreign key references. Men kan onderscheid maken tussen 1-1, 1-veel en veel-veel relaties. Ook bij de relaties komt het Convention over Conguration principe weer naar voor. Indien de vreemde sleutel van het formaat 29

46 naamtabel_id is, waarbij naamtabel de naam is van de geassocieerde tabel, zal ActiveRecord automatisch weten op welke kolom bijvoorbeeld een onderliggende join zal moeten gebeuren. Op die manier zorgt ActiveRecord ervoor dat er geen notie meer is van tabellen, maar men puur op object-niveau kan werken. Onderstaand stuk code toont bijvoorbeeld een 1-1 relatie tussen de modelklasse Bestelling en Factuur. 1 c l a s s B e s t e l l i n g < ActiveRecord : : Base 2 has_one : f a c t u u r 3 end 4 c l a s s Factuur < ActiveRecord : : Base 5 belongs_to : b e s t e l l i n g 6 end Codevoorbeeld 19: De modelklassen voor een bestelling en factuur Door deze twee declaraties en de conventie van vreemde sleutels in de onderliggende databank (de tabel voor Bestelling bevat een attribuut factuur_id en bestellingsnr ), zijn uitdrukkingen zoals deze mogelijk: 1 b e s t e l l i n g = B e s t e l l i n g. f i n d _ b y _ b e s t e l l i n g s n r (1234) 2 b e s t e l l i n g. f a c t u u r. p r i j s = f a c t u u r = Factuur. f i n d _ b y _ b e s t e l l i n g s n r (5678) 4 f a c t u u r. b e s t e l l i n g. k l a n t = ' Joram Barrez ' Codevoorbeeld 20: Gerelateerde entiteiten worden benaderd als gewone attributen Merk op dat we hier in feite het DRY principe overtreden. Zowel bij het aanmaken van de tabellen als bij de modellen dienen we de relaties expliciet te declareren. Deze tekortkoming bespreken we verder in sectie Uitbreidbaarheid en onderhoudbaarheid De fase waarin een applicatie ontwikkeld wordt is slechts een klein deel van het totale kostenplaatje. Bij zowat elk groot project dient men rekening te houden met toekomstige uitbreidingen en onderhoud. Het is dan ook van allergrootst belang dat de gebruikte ORM hiervoor geen belemmering vormt. Onderhoudbaarheid kan men beschrijven in termen van aantal regels code en complexiteit van die code. Hoe minder regels code, hoe minder code men in een latere fase zal moeten doorspitten om een bepaald probleem te lokaliseren en aan te passen. Hoe complexer de code, des te moeilijker men zal wijs raken uit bepaalde constructies. Uiteraard heeft ook de combinatie van beide een invloed. Merk trouwens op dat naast het aantal regels code ook de leesbaarheid van de code een rol speelt. Zo kan men perfect minder lijnen code schrijven door alle leesbaarheid op te oeren, wat uiteraard niet de bedoeling is. Qua regels code scoort ActiveRecord dankzij het gebruik van reectie zeer goed. Men kan al een volwaardige modelklasse implementeren met slechts twee regels code, indien men enkel standaard CRUD operaties nodig heeft. Complexiteit kan slechts minimaal door ActiveRecord opgelost worden, maar dit geldt voor iedere ORM. Het beheersen van de complexiteit is in de eerste plaats de verantwoordelijkheid van de ontwikkelaar. Doordat echter alles in Rails, en bijgevolg dus ook in ActiveRecord voldoet aan een bepaalde conventie is de eerste stap naar reductie van de complexiteit al gezet door het framework. 30

47 Indien een ontwikkelaar zich namelijk aan de conventies houdt, zal men later makkelijk zijn weg vinden doorheen de code. Uitbreidbaarheid is een onderdeel dat perfect past in de losoe van ActiveRecord. Doordat men intensief gebruik maakt van reectie zijn veranderingen zo goed als ogenblikkelijk en zonder aanpassingen in de code. Wanneer het datamodel dus evolueert, evolueren de modelklassen eenvoudigweg mee door de reectie. Men dient uiteraard nog wijzigingen door te voeren in de logica die de modelklassen gebruikt. Maar dit kan men ook oplossen door gebruik te maken van Duck Typing in Ruby. In sectie gaan we hier praktisch dieper op in. Het dient dus gesteld te worden dat ActiveRecord een waardige bondgenoot is bij het implementeren van toekomstgerichte applicaties. Vooral het feit dat modelklassen evolueren met het datamodel is een perfect hulpmiddel, en past tevens perfect in de losoe omtrent verandering bij Agile Development Business Logica Vaak is een standaard modelklasse vergezeld van relatiedeclaraties en validatieregels toereikend voor het merendeel van de toepassingen. Toch kan het gebeuren dat bepaalde logica over het domein object dient geïmplementeerd te worden. Naar analogie met validatie, is de beste plaats om dit te doen in de modelklasse zelf, waardoor de integriteit van het model verzekerd blijft. Aangezien modelklassen in wezen gewone Ruby klassen zijn, kan men dus eender welke geldige Ruby constructie toevoegen aan een modelklasse. Veronderstel de modelklasse Person, waarvan men in de databank een naam en een geboortedatum opslaat. Als business logica, zou het bijvoorbeeld wenselijk zijn om de leeftijd van een persoon kunnen op te vragen. Onderstaande code toont aan hoe dit bekomen moet worden, zonder de leeftijd op te slaan in de databank (dit zou trouwens niet opportuun zijn, gezien dan jaarlijks een wijziging zou moeten gebeuren): 1 c l a s s Person < ActiveRecord : : Base 2 def age 3 r eturn ( Date. today s e l f. b i r t h d a t e ) / #. 2 5 voor s c h r i k k e l j a r e n 5 end 6 end Codevoorbeeld 21: Denitie van business logica Door deze declaratie kunnen we, hoewel age geen attribuut is, in de databank toch schrijven: 1 joram = Person. c r e a t e ( : name => ' Joram Barrez ', : b i r t h d a t e => Date. parse ( ' 10/10/1985 ' ) ) 2 joram. save # P e r s i s t e n t maken 3 puts joram. age # > 21 Codevoorbeeld 22: 'age' gedraagt zich als een standaard attribuut Business logica is dus zeer eenvoudig in te bedden in een ActiveRecord klasse, omwille van het feit dat het niet meer is dan een gewone Ruby klasse. Men kan zelfs de automatisch gegenereerde methoden overloaden om eigen logica toe te passen. 31

48 Kritiek Alhoewel ActiveRecord een zeer handige ORM is, zijn er toch enkele punten die beter zouden kunnen. Allereerst leent ActiveRecord zich het beste voor applicaties waarvoor tegelijkertijd een nieuwe databank kan worden opgezet. Door deze databank te ontwerpen volgens de conventies van ActiveRecord kan veel werk bespaard worden. In bedrijfscontext is er echter meestal een databank aanwezig, en dient men een applicatie te ontwerpen bovenop deze databank. Vele van de voordelen van ActiveRecord vervallen dan, gezien de structuur dan manueel moet gedeclareerd worden. Ten tweede zorgen de conventies er wel voor dat een databank Rails compatibel wordt, maar dit wil niet zeggen dat dit de optimale oplossing is voor een ander framework. Vaak wordt een databank gedeeld over meerdere applicaties. Door de databank te structureren op de manier vooropgesteld in Rails, koppelt men in feite de databank iets te dicht met het framework. Nogmaals, het gebruik van Rails verplicht het gebruik van de conventies absoluut niet, maar vele voordelen van ActiveRecord vervallen wel als men het niet doet. Ten derde kunnen we het opgeven van de relaties beschouwen. Wanneer we hier even nuchter bij stil staan, valt het op dat dit in feite niet meer is dan conguratie. Zelfs wanneer we de conventies volgen, dienen we dit nog op te geven. Zeggen dat Rails dus helemaal conguratie-vrij is, is dus niet waar. Als vierde punt van kritiek kunnen we de ondersteuning voor meerdere databanken aanhalen. Rails heeft standaard geen ondersteuning hiervoor. Hoewel het wel mogelijk is, zoals beschreven door Chad Fowler in [4], is het niet triviaal en voelt het zeer on-rails aan. Het feit dat Rails geen ondersteuning hiervoor biedt heeft alles te maken met het feit dat wanneer men wel ondersteuning hiervoor zou inbouwen, men gaat moeten congureren om duidelijk aan te geven welk model behoort tot welke databank, wat men net probeert te vermijden in Rails. De veronderstelling dat de data beperkt is tot één databank vermijdt deze conguratie. Voor bijvoorbeeld eenvoudige online blog applicaties is dit misschien zo, maar in professionele omgevingen is het eerder regel dan uitzondering om data verspreid te hebben over verschillende databanken. Als laatste punt kunnen we zeggen dat ActiveRecord niet helemaal het DRY principe volgt. Het opgeven van de relaties tussen de Active Record klassen en het merendeel van de validatieregels kunnen immers uit de databank gehaald worden. Door namelijk de schema's uit de databank te analyseren kan al veel informatie hieruit gehaald worden. Een declaratie NOT NULL voor een bepaald attribuut kan bijvoorbeeld vertaald worden naar validates_presence_of en een declaratie INTEGER kan bijvoorbeeld gemapt worden op validates_numericability_of. De meeste van de voorkomende validatiehelpers kunnen op die manier automatisch ingevuld worden. Het bewijs dat dit technisch wel mogelijk is, bewijst de ORM DrySQL 2. Dit is een ORM gebaseerd op ActiveRecord, maar die de tekortkomingen van ActiveRecord wegwerkt. DrySQL genereert automatisch de juiste validatieregels op basis van de metadata in de databank, alsook de relaties tussen de verschillende modelklassen op basis van de vreemde sleutels. Naar onze mening is het dus slechts een kwestie van tijd tot dit alles ook zijn weg zal vinden naar ActiveRecord

49 3.4 ActionController Principe Het domein model is een statische entiteit. Wil men dynamische content genereren dan dient men op basis van gebruikersinvoer dit model at runtime te bevragen, te manipuleren en de data klaar te zetten om weergegeven te worden. In het MVC patroon is dit de taak van de controller laag. In Rails wordt controller laag in de MVC architectuur van Rails verzorgd door de module ActionController, welke een submodule is van ActionPack. In Rails is de werking van de controller laag voornamelijk gebaseerd op het gebruik van URL's. Elke controllerklasse (dit is een klasse die overerft van de klasse ActionController::Base) wordt gemapt op de url /controller_naam waarbij controller_naam een klassenaam is. Elke methode van een controllerklasse die publieke zichtbaarheid geeft is toegankelijk via de URL /controller_naam/methode_naam. De methoden van een controller noemt men in Rails-termen een actie. Wanneer een request voor een bepaalde URL ontvangen wordt door de webserver, zal deze de request doorgeven aan de dispatcher, zoals aangegeven op guur 3.2. De dispatcher zoekt de juiste controllerinstantie op en roept de correcte actie aan door de gegeven URL te evalueren. In de actie zelf kan men eender welke code schrijven. In de praktijk zal dit echter meestal neerkomen op het bevragen van één of meerdere modellen, de resultaten in variabelen te steken en de correcte view gebaseerd op conventies op te roepen. Figuur 3.2: Afhandelen van een request In feite is de rol van de controllerlaag in Rails beperkt tot het klaar zetten van data voor de view-laag. Het overgrote deel van de logica is namelijk reeds geïmplementeerd in de modelklassen zelf, waardoor er voor de controllerlaag enkel nog inter-model logica overblijft, wat veel minder frequent voorkomt. 33

50 3.4.2 Views en conventies De belangrijkste taak van de controllers bestaat erin data op te halen, te verwerken en klaar te zetten voor de views. Views worden verder behandeld in 3.5. In tegenstelling tot de meer traditionele aanpak, waarbij de view werkelijk wordt gemanipuleerd door de controller, beperkt de rol van een controller in Rails zich tot het klaarzetten van data die wordt doorgegeven in de vorm van een set variabelen. De controllermethodes bevatten bovendien meestal geen enkele declaratie die aangeeft welke view juist moet worden opgeroepen. Men beslist at runtime welke view men zal aanroepen op basis van naamconventie. Dit principe zorgt bovendien voor een scheiding van de controller en view laag die in het originele MVC patroon niet aanwezig is. De view verkrijgt enkel een set van variabelen, maar van wie is onbelangrijk. De view beslist zelf wat er met deze variabelen gedaan wordt en hoe deze afgebeeld worden. De view doet juist wat de view moet doen: tonen van de klaargezette data. Analoog weet ook de controller dankzij de conventies niet wat zijn uiteindelijke view is, tenzij men er voor kiest om dit expliciet op te geven. In principe kunnen dus dankzij deze eenvoudige werkwijze de controller en view laag afzonderlijk van elkaar ontwikkeld worden, op voorwaarde dat men uiteraard duidelijke afspraken omtrent de door te geven variabelen afspreekt. In feite kan dit dus beschouwd worden als een transparante interface tussen de controller en view laag Filters Het komt vaak voor dat men ontdekt dat bepaalde acties een gemeenschappelijk stuk code bevatten. De logische vervolgstap is dan meestal deze code te isoleren in een methode die opgeroepen wordt in de verschillende acties. Het grote voordeel is dan uiteraard dat aanpassingen slechts op één plaats dienen gedaan te worden. Men kan dit eenvoudig in een controllerklasse bereiken door een nieuwe private methode te deniëren die men dan aanroept vanuit de andere acties. Het privaat zijn van de methode is cruciaal, want publieke acties kunnen immers via een URL gebruikt worden van buitenaf. Rails bevat bovendien ook nog het mechanisme van lters om een soortgelijk resultaat en meer krachtigere zaken op een intuïtieve manier te bereiken. Eenvoudig gezegd zijn lters methoden die voor, na of rond (before-, after- en aroundlters) een actie automatisch opgeroepen worden. Het voordeel bestaat er dan uit dat men niet meer expliciet de methoden dient op te roepen in de actiecode, maar dat het framework dit voor ons doet. Filters hebben tal van toepassingen zoals bijvoorbeeld logging (aroundlter), compressie (afterlter), authenticatie (beforelter), etc. In zeker zin lteren deze methodes dus de input en output van de acties. In sectie zullen we gebruik maken van een beforelter om authenticatie te implementeren. Beschouw als extra illustratie onderstaand voorbeeld van een aroundlter voor logging (gebaseerd op een voorbeeld uit [17]). Deze eenvoudige lter zal naar het log de uitvoeringstijd van de acties schrijven. Op regel twaalf wordt door middel van het keyword yield de actie opgeroepen om na uitvoering terug te keren naar de lter. Aangezien een lter tussenkomt bij alle acties (dus de publieke methodes), dienen we de lter privaat te maken om deze ontoegankelijk te maken van buitenaf en ervoor te zorgen dat de lter niet tussenkomt bij de eigen oproep. Zoals het voorbeeld aantoont, is een lter dus niet meer dan een private methode die een speciale behandeling van het framework ondervindt. 34

51 1 c l a s s V o o r b e e l d C o n t r o l l e r < A p p l i c a t i o n C o n t r o l l e r 2 3 a r o u n d _ f i l t e r : meet_tijd_actie 4 5 def t o o n _ l i j s t # Voorbeeld van een p u b l i e k e methode end 8 9 p r i v a t e 10 def meet_tijd_actie 11 s t a r t = Time. now 12 y i e l d 13 einde = Time. now 14 l o g g e r. i n f o ( "#{action_name } duurde #{einde s t a r t ) seconden ) 15 end end Codevoorbeeld 23: Het gebruik van een aroundlter voor eenvoudige logging Ten slotte dient nog vermeld te worden dat men niet beperkt is tot één lter, maar men zoveel lters kan declareren als men wenst. Er wordt dan een lter chain (lter ketting) opgebouwd die daarboven condities kan bevatten om bepaalde lters wel of niet uit te voeren. 3.5 ActionView Principe Het view gedeelte van de Rails MVC architectuur zit bevat in de module ActionView van het framework. Deze module is klein in vergelijking met de twee andere hoofdmodules en behoeft ook niet zo veel uitleg. De werking van een Rails view is vrij eenvoudig en steunt zoals te verwachten op het Convention over Conguration principe. Wanneer een bepaalde actie van een bepaalde controller uitgevoerd is, wordt er gezocht naar een speciek bestand of template. De conventie bestaat er uit dat deze template dezelfde naam heeft als de net uitgevoerde actie en terug te vinden is in de directory die de naam van de controller draagt. Deze eenvoudige conventie zorgt ervoor dat we niet meer expliciet een bepaalde view moeten oproepen in de controlleracties. Zoals gezien in sectie 3.4.2, bestaat de taak van de controller dan enkel uit het klaarzetten van data. Het framework zorgt er dan voor dat de data (onder de vorm van instantievariabelen) beschikbaar is voor de view, die volgens de net beschreven conventie gekozen wordt door het framework. Om de view te implementeren heeft men grofweg de keuze tussen drie mogelijkheden. De belangrijkste en meest gekozen optie is het gebruik van RHTML, dat reeds kort aangehaald werd in sectie 3.1. Een RHTML template is in feite niets meer dan gewone HTML, uitgebreid met Ruby code onder de vorm van ERb tags. Op die manier kan men statische HTML pagina's aanvullen met dynamische informatie klaargezet door een controller. Veronderstel dat een controller de heeft klaargezet die een collectie van producten uit de databank bevat. De RHTML template die dan de namen van alle producten afbeeldt, wordt getoond in onderstaand codevoorbeeld. De ERb tags zijn te herkennen aan <% 35

52 ... %>, de tags met een extra gelijkheidsteken worden bovendien geëvalueerd naar tekst en ingebracht in de pagina, zoals bijvoorbeeld in regel drie. 1 <h1>namen van producten </h1> 2 each do product %> 3 <%= product. name %><br/> 4 <% end %> Codevoorbeeld 24: RHTML template om de namen van de producten uit de te tonen RHTML templates zijn niet de enige mogelijkheid als output. De twee andere courant gekozen opties zijn RXML (Ruby XML) en RJS (Ruby JavaScript). RJS wordt gebruikt om JavaScript te generen en werken we verder uit in sectie RXML is een manier om XML te produceren en maakt gebruikt van de Builder 3 biliotheek. Via Builder kan XML gegenereerd worden op basis van Ruby declaraties. Een eenvoudig RXML voorbeeld dat de namen van de producten in xml-vorm genereert, wordt hieronder getoond. Net zoals vorig voorbeeld, heeft de controller nu ook weer een klaargezet. each do product 2 xml. product do 3 xml. name ( product. name ) 4 end 5 end Codevoorbeeld 25: een eenvoudige RXML template Het resultaat van deze template ziet er dan (bijvoorbeeld) als volgt uit: 1 <product > 2 <name>abc</name> 3 </product > 4 <product > 5 <name>xyz</name> 6 </product > 7... Codevoorbeeld 26: Resultaat van de RXML template Merk op dat er ook een conventie bestaat omtrent precedentie van templates indien meerdere templates voldoen aan de naamconventie voor views. Zo hebben RHTML templates voorrang op RXML templates, en hebben RXML templates op hun beurt voorrang op RJS templates View-helpers: reductie van code in templates De technieken beschreven in vorige sectie zijn handig, maar zijn niet speciek voor het Rails framework. Rails voegt echter een bonte verzameling van zogenaamde view-helpers toe aan de viewlaag en laat

53 bovendien de mogelijkheid open om eenvoudig zelf view-helpers te deniëren. Eenvoudig gezegd zijn view-helpers methodes die kunnen opgeroepen worden in een template om de viewcode tot een minimum te bepreken. Het schrijven van code in de view, leidt namelijk gemakkelijk tot het schrijven van té veel code in de views. In principe kan men zelfs de databankconnectie en alle andere logica in de view stoppen via een ERb tag. Dit is echter geen goeie aanpak, omwille van volgende redenen: Het opsplitsen van logica in modellen, controllers en views heeft als grootste voordeel het hergebruik van componenten. Dankzij de duidelijk aijning tussen de deelcomponenten kan men namelijk makkelijk componenten wisselen en de impact van veranderingen beperken tot de component zelf. Zo is het bijvoorbeeld mogelijk om eenzelfde controller te gebruiken voor RHTML, RXML of RJS templates, zonder dat de controller hier weet van heeft. Indien alle logica in de viewlaag zou bevat zitten, is dit uiteraard niet meer mogelijk. RHTML, de meest gebruikte optie, is eigenlijk veredelde HTML. In vele bedrijven wordt de view ontwikkeld door professionele designers, die meestal geen programmeurkennis hebben. Het beperken van de hoeveelheid code in de view is dus ook positief om zulke organisatie werkbaar te houden. Ten slotte is het testen van views niet zo eenvoudig als het testen van pure logica. Door de logica te bundelen in view-helpers, kan men deze view-helpers afzonderlijk testen zonder de belemmering van een view. De collectie van view-helpers die gedenieerd zijn door Rails, zijn te talrijk om hier op te sommen. We verwijzen graag naar de Rails API 4 voor meer informatie hieromtrent. Het codevoorbeeld hieronder illustreert de werking van een view-helper. De uitdrukking in dit voorbeeld, link_to, genereert een HTML-link naar een bepaalde actie van een bepaalde controller. De analoge HTML code is <a href="/voorbeeld/lijst producten>bekijk lijst van producten</a>. Het is duidelijk dat de eerste uitdrukking veel eenvoudiger te onthouden en onderhouden is dan de tweede. 1 <%= link_to " Bekijk l i j s t van producten ", 2 { : c o n t r o l l e r => ' voorbeeld ', 3 : a c t i o n => " l i j s t _ p r o d u c t e n "} 4 %> Codevoorbeeld 27: Een eenvoudige view-helper 3.6 Concreet voorbeeld: wisselwerking tussen controller en view Een kort voorbeeld maakt veel duidelijk omtrent de interactie tussen controller en views. Veronderstel dat we een model Student hebben met als attributen naam en telefoonnummer. Veronderstel dat we wensen een lijst van studenten te visualiseren en nieuwe studenten toe te voegen. De controllercode staat hieronder weergegeven

54 1 c l a s s S t u d e n t C o n t r o l l e r < A p p l i c a t i o n C o n t r o l l e r 2 3 def l i j s t _ s t u d e n t e n = Student. f i n d ( : a l l, : order => 'naam ' ) 5 end 6 7 def nieuw = Student. new 9 end d e f maak_student_aan = Student. new ( params [ : student ] ) 13 i save 14 r e d i r e c t _ t o : a c t i o n => l i j s t _ s t u d e n t e n 15 e l s e 16 render : a c t i o n => niew 17 end 18 end end Codevoorbeeld 28: StudentController Het tonen van een lijst van studenten is vrij eenvoudig: de controlleractie haalt alle studenten op en steekt deze in de De view heeft toegang tot deze variabele en toont alle studenten door te itereren over de collectie. Dit wordt hieronder geïllustreerd. 1 <table > 2 <% f o r student 3 <tr> <td> <%= student. naam %> </td> 4 <td> <%= student. telefoon_nummer %> </td> <tr> 5 <% end %> 6 </table > 7 <%= link_to ' Nieuwe student ', : a c t i o n => ' nieuw ' %> Codevoorbeeld 29: View voor de lijst van studenten Zoals te zien is in de laatste regel wordt de lijst afgesloten met een link naar de actie 'nieuw'. In deze actie zien we op regel acht van de controllercode dat een wordt aangemaakt, dat echter geen attribuutwaarden bevat. We doen dit omdat dit object gebruikt wordt door de view-helpers om de ingevulde data voorlopig in te steken. Na het uitvoeren van de actie 'nieuw' wordt bij conventie de view 'nieuw.rhtml' gerendered. Onderstaande code toont deze template. Merk op dat we hier uitvoerig gebruik maken van view-helpers om een HTML-formulier, tekstvelden en de knop aan te maken. Merk ook op dat de informatie verstuurd zal worden naar de actie 'maak_student_aan', zoals te zien is op de tweede regel. 38

55 1 <%= error_messages_for ' student ' %> 2 <% form_tag : a c t i o n => ' maak_student_aan ' do %> 3 Naam : <%= t e x t _ f i e l d ' student ', 'naam ' %> <br/> 4 Telefoon Nummer : <%= t e x t _ f i e l d ' student ', ' telefoon_nummer ' %> <br/> 5 <%= submit_tag "Maak aan" %> 6 <% end %> Codevoorbeeld 30: nieuw.rhtml De input van het formulier wordt door Rails automatisch in een hashobject ' params' gestoken dat toegankelijk is in de controller. Op regel twaalf van de controller zien we dat deze hash kan gebruikt worden om een nieuwe student aan te maken (de eerste parameter van de view-helper text_eld geeft de sleutel van de hash aan). De reden waarom dit kan werken, is omdat we de tekstvelden in de view dezelfde namen hebben gegeven als de werkelijke attributen van het model. Door deze conventie te volgen kunnen we alle atrributen van een model met één coderegel instellen. In regel dertien wordt dan nagegaan of het persisteren van de nieuwe student lukt. Indien bijvoorbeeld validatieregels geschonden worden, wordt het formulier voor een student aan te maken opnieuw getoond (regel zestien). Indien het wel lukt, wordt de lijst van studenten getoond, nu aangevuld met de nieuwe student (regel veertien). De reden waarom we gebruik maken van een render in het geval van falen, is omdat een render de toestand van het bijhoudt. Zodanig kunnen de foutboodschappen van de validatieregels opgeslagen worden in dit object, waarna ze uitgeschreven worden op regel één van de pagina met het aanmaakformulier. Een redirect op zijn beurt houdt geen toestand bij, wat net vereist is hier. 3.7 Conclusie Wanneer men voorgaande hoofdstukken analyseert, kan men concluderen dat het Rails framework uitblinkt in simpliciteit en compactheid. Men heeft slechts beschikking over enkele componenten, die echter perfect op elkaar afgesteld zijn. Door zich enkel te richten op webapplicaties kan men de vereiste functionaliteit van het framework minimaal houden. Het betekent ook dat het aanleren van Rails vrij eenvoudig is, de overgang naar Ruby buiten beschouwing gelaten. Conventies bepalen waar welk onderdeel van de applicatie moet terecht komen en beperken de conguratietijd tot een absoluut minimum. Wat uniek is aan Rails is dat slechts één manier de goede is om een webapplicatie te ontwikkelen, namelijk die vooropgesteld door Rails. Men heeft de keuzemogelijkheden bewust beperkt, zodat men zich kan concentreren op de werkelijke logica. Het feit dat Rails zich alleen op de webapplicatie-niche richt, is de grote kracht maar meteen ook de achillespees van Rails. Aangezien steeds meer bedrijven echter de evolutie naar webgebaseerde applicaties volgen, gaat Rails ongetwijfeld een positieve toekomst tegemoet. Een tweede groot voordeel is het feit dat Rails een full-stack of een all-in-one framework is. Rails voorziet mogelijkheden voor elke laag of fase. Zo bezit Rails een volwassen data access laag (ActiveRecord), een volwaardig testing framework, een compleet geïntegreerde webserver, etc. Indien men dus vertrouwd is met Rails dient men geen enkel ander pakket te leren of te gebruiken. Ook dit heeft uiteraard implicaties op de productiesnelheid. Doordat alles bovendien voorzien is door Rails zijn deze componenten dan ook volledig op elkaar afgestemd. In vele andere frameworks dient men vaak zelf de verschillende componenten van verschillende producenten te congureren zodat met elkaar kan gecommuniceerd worden. 39

56 De naam Ruby on Rails is in feite nog niet zo slecht gekozen. Rails vernoemen zonder Ruby zou te veel eer geven aan het framework zelf. Hoewel het framework vele eindjes aan elkaar knoopt en reeds vele zaken in eigen beheer neemt, dient de werkelijke logica nog steeds geschreven te worden in Ruby. Daar de productiviteitswinst met Rails zo hoog is, zet Rails Ruby werkelijk op rails, om aan een hogere snelheid vooruit te schrijden (op het vlak van webapplicatie-ontwikkeling). 40

57 Hoofdstuk 4 Rails praktisch toegepast "After you nish the rst 90% of a project, you have to nish the other 90%." - Michael Abrash De bespreking van een framework is nooit volledig zonder een toetsing aan de praktijk. Een framework kan conceptueel nog zo goed in elkaar zitten, maar wanneer het praktisch gebruik ervan tegenvalt is het zo goed als waardeloos. We zullen daarom een case study uitwerken om Rails praktisch te evalueren. De case study bestaat uit de ontwikkeling van een applicatie volgens de Agile Development methodiek. De bevindingen hieromtrent kunnen teruggevonden worden in het volgende hoofdstuk, de beschrijving van de applicatie in dit hoofdstuk. 4.1 Doelstelling Het doel van de implementatie bestaat er in een applicatie na te bouwen die oorspronkelijk ontwikkeld en onderhouden werd en wordt binnen de vakgroep Ghislain Homan Software Engineering Lab (GH-SEL). Meer bepaald bestaat het doel erin aan te tonen dat een soortgelijke functionaliteit bereikt kan worden met Ruby on Rails. Dezoorspronkelijke applicatie werd ontwikkeld met behulp van J2EE in opdracht van de dienst medische nierziekten (nefrologie) van het UZ Gent. Naar analogie en respect ten overstaan van de originele applicatie, werd de applicatie Dieet-S(t)imulatie gedoopt. 4.2 Bespreking van de originele applicatie Situering De ontwikkeling van de applicatie is gestart om nierpatiënten (en mogelijks ook personen met andere aandoeningen) de mogelijkheid te geven om hun eetgedrag vast te leggen en op te volgen. Nierpatiënten mogen namelijk bepaalde grenzen van nutriënten niet overschrijden in een bepaalde tijdspanne. Door ingenomen voedsel in te geven kan men nagaan wat en hoeveel men nog in de tijdspanne kan eten. De projectomschrijving van de originele applicatie vermeldt het volgende: 41

58 Binnen het kader van chronische ziektes worden patiënten met heel uiteenlopende pathologieën geconfronteerd met strenge dieetvoorschriften. Deze voorschriften zijn voor tal van patiënten, alsook voor hun naasten, een belangrijke stressfactor. Veel patiënten slagen er dan ook niet of onvoldoende in om zich aan een voorgesteld dieet te houden. (...) Nu blijkt dat een interactieve informatievoorziening aan patiënten een betere dieetopvolging met zich meebrengt. Een verhoogde opvolging op het gebied van hun dieet zal indirect ook aanleiding geven tot minder medische kosten in de vorm van medicatie, minder snel ziekteverloop, betere algemene gezondheid, minder ziekenhuisopnames... Ook een groot aantal mensen zonder medische klachten kan voordeel halen hebben uit een goed inzicht in hun dagelijkse voedingsgewoonten. Dit komt ten goede aan zowel de patiënt als de gemeenschap (terugbetalingen via ziektekostenverzekering), en brengt zeker een meerwaarde met zich mee, naast de wetenschap dat de levenskwaliteit sterk kan verbeterd worden voor een grote groep van mensen Functionaliteit In grote lijnen kan men in de originele applicatie volgende functionaliteiten onderscheiden: Authenticatie: het is van groot belang dat patiënten en begeleidend personeel pas toegang krijgen tot de private gegevens na authenticatie. Het zou onaanvaardbaar zijn dat derden de medische gegevens kunnen raadplegen. Verbonden met authenticatie is het beheer van gebruikers. Ingeven van voedselopname: patiënten kunnen ingeven wat ze hebben gegeten en dit opslaan. Het ingeven van een maaltijd verloopt door het kiezen van bepaalde voedingsmiddelen uit een lijst, die onderverdeeld is in categorieën en subcategorieën. Op dit moment wordt gebruik gemaakt van informatie verkegen van het NUBEL (Nutriënten België) agentschap. Grasche visualisatie van een maaltijd: de gekozen voedingsmiddelen worden door middel van grasche symbolen afgebeeld op een bord. Het voordeel van deze eenvoudige doch intuïtieve voorstelling is dat deze toegankelijk is voor alle lagen van de bevolking. Samenstelling van een maaltijd: men kan van de voedingsmiddelen apart, of van de maaltijd in het geheel bekijken wat de samenstelling onder de vorm van nutriënten is. Wanneer bepaalde grenzen overschreden worden, wordt dit ook grasch weergegeven. Historiek en statistieken: de reeds ingegeven maaltijden kunnen geraadpleegd worden via zowel tekstuele als grasche visualisatie. Op die manier kunnen patiënten en hun zorgverstrekkers het eetgedrag over een bepaalde periode nagaan. Merk op dat deze lijst slechts summier de belangrijkste functionaliteit aangeeft van de applicatie. Een volledige business analyse is reeds verricht door Dr. K. De Schutter, S. Van Wonterghem, B. Adams en D. Matthys, waardoor het nutteloos zou zijn hun werk hier te herhalen. Deze businessanalyse is wel het vertrekpunt geweest in de ontwikkeling van onze implementatie. Het is de functionaliteit in bovenstaande lijst die we ook zeker wensen terug te vinden in onze uiteindelijke implementatie. Onderstaande screenshot geeft een beeld vanuit het standpunt van een patiënt in de originele applicatie. 42

59 4.2.3 Architectuur De oorspronkelijke applicatie is opgebouwd als een client-server systeem. Dit is vandaag de dag de meest gekozen architectuur voor applicaties, waarbij het grootste voordeel de scheiding tussen de client en server is. De server voorziet in een bepaalde dienst waarvan een aantal clients gebruik maken om een functionaliteit aan een gebruiker aan te bieden. Aan clientzijde wordt gebruik gemaakt van lichte (thin) client. Dit betekent dat het meeste van de logica zich aan serverzijde bevindt, in plaats van aan clientzijde. Met Rails en met webframeworks in het algemeen, is men sowieso beperkt tot een ultra-thin client (de browser), dus veel verschil tussen de originele en onze applicatie zal er op dit vlak niet zijn. Aan serverzijde kunnen we twee lagen onderscheiden: een business laag en een data laag. Zoals de naam laat vermoeden is de functie van de data laag het aanbieden van data. Concreet betekent dit hier de data uit de Nubel-tabel, gebruikersinformatie en statistische informatie. In deze data laag gebruikt men het DAO (Data Access Object) design pattern om wijzigingen aan het datamodel in de toekomst eenvoudig mogelijk te maken. Het DAO pattern bestaat uit een interface welke de data-gedreven functionaliteit aanbiedt naar buiten toe. Deze interface encapsuleert de databron, zodanig dat men slechts de interface dient te kennen en niet de implementatie. De werkelijke code die ervoor zorgt dat de juiste data gelezen, gemanipuleerd of verwijderd wordt is een klasse die dan de DAO interface implementeert. De consequenties van deze eenvoudige design pattern zijn verregaand. Dankzij het DAO pattern kan men de databron wijzigen, door een nieuwe klasse te schrijven die de DAO interface implementeert, zonder dat aan de rest van de applicatie aanpassingen dienen gedaan te worden. De rest van de applicatie werkt namelijk met de DAO interface, en deze is ongewijzigd. Op die manier kan men de data opslaan in databanken, XML-les, objecten, etc. en blijft de applicatiecode ongewijzigd. De tweede laag aan serverzijde, is zoals gezegd de business laag. Deze laag wordt gevormd door een set van services. Deze services bestaan in de eerste plaats uit een interface welke een bepaalde functionaliteit aanbiedt naar de clients toe. Deze interfaces, en deze interfaces alleen, vormen het aanspreekpunt van de clients. Wederom komt dit de uitbreidbaarheid meer dan ten goede. Men kan namelijk de implementatie van de interfaces wijzigen, zonder dat de clientcode dient te wijzigen, aangezien deze enkel de interfaces kennen. In de architectuur kan men vier verschillende services onderscheiden. De implementatie van de 43

60 meeste van deze services maken op hun beurt gebruik van de DAO interfaces. Hierdoor krijgt men in de globale architectuur een duidelijke scheiding in drie lagen: client, business en data; waarbij zich op de scheiding de besproken interfaces bevinden om de implementatie van de lagen onafhankelijk van elkaar te maken. De services zijn: Foodservice: deze service biedt functionaliteit aan om informatie over voedingsmiddelen op te zoeken. Bijvoorbeeld het opvragen van alle voedselcategorieën gebeurd via deze service. Dietservice: functionaliteit betreende het dieet. Bijvoorbeeld het ingeven van een geconsumeerde maaltijd is een functionaliteit van de Dietservice. Nephrologyservice: geeft aan welke de grenzen voor bepaalde nutriënten zijn voor een patiënt. Patientservice: biedt functionaliteit omtrent de patiënt aan. gebruikersgegevens verloopt via deze service. Authenticatie en beveiliging van 4.3 Agile development in de praktijk In zagen we reeds dat Rails uitermate geschikt is voor Agile Development, onder meer door een zo goed als directe feedback loop en het feit dat men van in het begin en op ieder moment in de tijd een werkende applicatie heeft. Vermits Rails deze manier van ontwikkelen promoot, hebben we besloten de applicatie te ontwikkelen volgens de Agile Development methodiek. Dit betekent dat we naast Ruby on Rails ook Agile Development kunnen evalueren naar het einde toe. De klant waarvoor we de applicatie zullen ontwikkelen is de vakgroep GH-SEL. De ontwikkeling door middel van Agile Development houdt voor ons grosso modo volgende zaken in: Het werken in iteraties: dit wil zeggen dat we in een bepaalde (korte) tijdsperiode werken aan één welgedenieerde functionaliteit of doel. Bij het begin van iedere iteratie komen we samen met de klant en bespreken het volgende doel. Iedere iteratie bestaat uit een implementatie, testen en een evaluatie op het einde. Na iedere iteratie is een (beperkt) werkend product voor handen. Communicatie, met en naar de klant toe. In Agile Development stelt men de afnemer van het uiteindelijke product centraal. Er is zeer veel (mondelinge) communicatie, waardoor men precies weet wat de klant wil of niet wil. Een werkend product na iedere iteratie, hetzij met beperkte functionaliteit. Door na iedere iteratie een werkend product te hebben kan de klant ook ingrijpen wanneer iets niet is zoals gewenst. De klant centraal. Het werken in iteraties heeft als voordeel dat op elk ogenblik een werkend product kan getoond worden. Het tonen van het product aan de klant heeft als voordeel dat de klant de vooruitgang met eigen ogen kan aanschouwen. De klant kan bovendien ingrijpen, commentaar en kritiek leveren wanneer dit nodig zou blijken. In Agile Development poogt men de klant zo dicht mogelijk te betrekken bij de ontwikkeling van het project. Zowel de klant, die zich betrokken voelt, als de programmeurs, die beter op de wensen van de klant kunnen inspelen, hebben er baat bij. 44

61 Hoofdstuk 5 Implementatie van de Dieet-S(t)imulatie applicatie "Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, Massachusetts Institute of Technology In dit deel bekijken we de werkelijke implementatie van de applicatie beschreven in hoofdstuk 4. Het is niet de bedoeling om hier de code van de applicatie in detail toe te lichten. Dit zou niet veel bijbrengen en we richten onze pijlen liever op de software-ontwikkeling zelf zoals voorzieningen en gebruikte werkwijzen alsook de sterke of zwakke punten in Rails. De volledig broncode en applicatie kan worden teruggevonden op de CD-ROM. De bespreking is chronologisch opgebouwd en gaat van de eerste iteratie tot de laatste iteratie zoals deze in werkelijkheid werd afgehandeld. Dit lijkt ons e beste aanpak om het iteratie-gebaseerde Agile Development uit te werken. Per iteratie wordt ingegaan op hoe de zaken werden aangepakt om de gewenste functionaliteit te bereiken. Wanneer tijdens de iteratie een voordeel, nadeel of nieuwe techniek van Rails werd ontdekt, wordt deze besproken in die iteratie waar de ontdekking ook daadwerkelijk voorkwam. 5.1 Architectuur Een kritiek die vaak geopperd wordt bij het gebruik van Agile methoden is dat men het ontwerp van de architectuur negeert. Men heeft weliswaar een idee van waar men uiteindelijk naartoe wil, maar de architectuur wordt pas zichtbaar na enkele iteraties en wordt steeds verjnder. Dit heeft alles te maken met het hoofdoel van Agile Development: snelheid. Men wil de klant zo snel mogelijk een werkend product leveren in een zo kort mogelijke tijd. Het is correct dat Agile methoden minder de nadruk leggen op een architectuurontwerp vooraf, maar dit sluit een architectuur als dusdanig niet uit. Men dient de gulden middenweg te volgen, en het gezond verstand te gebruiken bij het ontwikkelen via Agile methoden. Als men bijvoorbeeld ziet dat men zeer 45

62 veel data heeft, dient men niet te beginnen met een gewoon bestand, om dan later te refactoren naar een databank. Bovendien stelt Fowler, dient een Agile programmeur zeker en vast rekening te houden met toekomstige uitbreidingen. Het is niet omdat men werkt in iteraties, dat de code uit deze iteraties niet toekomstbestendig kan zijn. Volgens hem zijn bewezen design patterns onmisbaar om dit te verwezenlijken. Wanneer we het bovenstaande toepassen op Rails, zien we dat Rails de Agile denkwijze goed ondersteunt. In Rails is een globale architectuur reeds vastgelegd zoals gezien in Het werken in iteraties is dus zeer eenvoudig met Rails, vermits elk codedeel één welgedenieerde plaats heeft in de MVC architectuur en we dus geen architectuurontwerp hoeven te doen vooraf. Bovendien komt het ontwerp van de architectuur van de originele applicatie vrij goed overeen met de MVC architectuur: De view laag komt overeen met de GUI aan clientzijde. We wensen een soortgelijke visualisatie te bereiken, echter met een andere technologie. De controller laag vormt de plaats waar de business logica zich bevindt. De business laag in de originele applicatie komt hiermee overeen. De model laag kan gezien worden als de data laag in het origineel ontwerp. Merk op dat Rails op het eerste zicht geen absolute scheiding van de lagen biedt zoals het geval is met interfaces. De controller laag gebruikt de model laag zonder tussenkomst van een interface. Wijzigt een model, dan wijzigen ook de controllers die dit model gebruiken. Dit is zeker en vast een probleem. We gaan dieper in op dit probleem in Iteratie 1: databankontwerp en modellen voor voeding Doelstelling De applicatie is een zeer data-gedreven applicatie. Centraal staat de informatie omtrent de voedingsmiddelen. Deze informatie wordt getoond aan de gebruiker, toegepast in de berekeningen van de nutriëntenwaarden, aangewend om de maaltijd mee samen te stellen en historieken op te slaan. Zonder deze data zou de applicatie niet bestaan of allerminst van betekenis zijn. Het is dan niet meer dan verwonderlijk dat het doel van de eerste iteratie het modelleren van deze voedingsmiddelen is. Het ontwerp van een rudimentair model is ook de meest gekozen werkwijze om een Rails project aan te vangen Databankontwerp De Nubel tabel werd ons geleverd als een csv bestand. Elke rij van deze tabel stelt een bepaald voedingsmiddel voor. Voor elk van de voedingsmiddelen zijn de waarden voor 27 nutriënten gegeven (energie, eiwitten, vet, suikers, etc.). Deze waarden gelden voor 100 gram van het betreende voedingsmiddel. Elk van de voedingsmiddelen behoort tevens tot een bepaalde categorie, waarbij subcategorieën mogelijk zijn. Deze informatie indachtig, bekomen we de databankstructuur zoals te zien op guur 5.1. Belangrijk is allereerst op te merken dat we de conventies van Rails volgen voor het ontwerp van de tabellen. Deze conventies zullen later werk besparen door bepaalde zaken automatisch te laten verlopen net omdat de conventies gevolgd worden: 46

63 Figuur 5.1: Databankontwerp voor iteratie 1 De namen van tabellen zijn in het meervoud. De primaire sleutel is 'id'. Vreemde sleutels hebben als naam de naam van de gerefereerde tabel in het enkelvoud, gevolgd door een underscore en 'id'. De zelf-refererende tabel categories heeft als sleutel naar zichzelf de naam parent_id. Dit is belangrijk om Rails automatisch voor ons een boomstructuur te laten opbouwen van categorieën en subcategorieën (zie 5.2.4). Elke categorie heeft een naam, een url naar een icoon en een vreemde sleutel naar een andere categorie. De tabel foods bevat alle voedingsmiddelen, voorzien van een naam, de url naar een icoon en een vreemde sleutel naar de categorie waartoe het voedingsmiddel behoort. Voor het opslaan van waarden van de nutriënten per voedingsmiddel wordt gebruik gemaakt van de jointabel foodcontents (die weliswaar ook een model zal vormen). Een rij in deze tabel linkt één bepaald nutriënt met één bepaald voedingsmiddel, en slaat hiervoor een hoeveelheid (quantity) op. Door deze vorm van normalisering te gebruiken kunnen nieuwe nutriënten onafhankelijk van de voedingsmiddelen toegevoegd worden en omgekeerd. Tenslotte is een nutriënt gelinkt met een bepaalde eenheid, afkomstig uit de tabel units met een vreemde sleutel vanuit nutrients. Voor elke eenheid slaan we de naam en de afkorting ervan op Migraties Er zijn twee valabele opties bij het aanmaken van de vereiste tabellen. De eerste mogelijkheid is gebruik te maken van de DDL (Data Denition Language) van de databank. Over het algemeen genomen is dit standaard SQL, hoewel er tussen verschillende databankproducten subtiele verschillen kunnen zitten. Zo is een geheel getal bij MySQL int(11), terwijl het bij postgresql integer is en bij een Oracle product number. Dit is slechts één voorbeeld uit de vele, subtiele verschillen. Het is dan ook duidelijkdat de portabiliteit van de denitie van de databankschema's niet gegarandeerd is. Een tweede probleem met databankspecieke DDL is versiebeheer. Het werken in iteraties brengt onvermijdelijk veranderingen met zich mee. Ook het databankschema ontsnapt hier niet aan: een tabel verwijderen, toevoegen, kolommen hernoemen, kolommen toevoegen, etc. zijn nu eenmaal eigen aan een iteratie-gebaseerde aanpak. Bij applicatiecode wordt dit probleem opgelost door gebruik te maken van een versiecontrolesysteem zoals CVS of SVN. Verschillende programmeurs kunnen aan dezelfde applicatie 47

64 werken en hun code toevoegen aan een centrale repository. Deze repository zal de verschillende stukken code samenvoegen en een versienummer geven. In het geval van problemen kan men dan eenvoudig teruggaan (rollback) naar een oudere, correcte versie van de applicatie. Voor databankschema's bestaat echter zo geen elegante oplossing. De meest benaderende manier is het DDL schema ook onder versiecontrole te houden en bij problemen het gehele databankschema verwijderen en de oudere DDL opnieuw toepassen. Het is duidelijk dat hierdoor ook alle data in de databank verloren gaat, wat meestal niet wenselijk is. Sinds recent (versie 1.1.2) zijn migraties toegevoegd aan het Rails framework. Migraties leveren onder meer een oplossing voor bovenstaande problemen. Migraties zijn het best uit te leggen aan de hand van een voorbeeld. De migratie voor de tabel nutrients ziet er bijvoorbeeld als volgt uit: 1 c l a s s CreateNutrients < ActiveRecord : : Migration 2 extend MigrationHelpers 3 4 def s e l f. up 5 c r e a t e _ t a b l e : n u t r i e n t s do t 6 t. column : name, : s t r i n g, : n u l l => f a l s e 7 t. column : unit_id, : i n t e g e r, : n u l l => f a l s e 8 end 9 10 foreign_key : n u t r i e n t s, : unit_id, : u n i t s add_index : n u t r i e n t s, : name 13 end def s e l f. down 16 drop_table : n u t r i e n t s 17 end 18 end Codevoorbeeld 31: Migrate voor de nutrients tabel Dit voorbeeld toont een aantal interessante zaken aan. We zien we dat een migratie bestaat uit twee methoden: de methode up en de methode down. De methode up wordt toegepast wanneer we migreren (vandaar de naam) naar een hogere versie dan de huidige versie van de databank. In dit voorbeeld zien we vooraleerst dat een tabel nutrients wordt gecreeërd met twee kolommen. Merk op dat we geen kolom id speciceren zoals vereist volgens de conventies van Rails. Rails zal automatisch een primaire sleutel genaamd id aanmaken indien we niet expliciet de primaire sleutel opgeven. Wederom een voorbeeld van Convention over Conguration. Zoals uit het voorbeeld blijkt, wordt een kolom aangemaakt door een naam en type op te geven. Voorts zijn een aantal optionele parameters zoals hier het feit dat null verboden is mogelijk. In wezen is een migratie niet meer dan een in Ruby geschreven subklasse van de klasse Migration van de module ActiveRecord. We drukken met andere woorden het databankschema uit in Ruby. In feite zijn migraties een perfect voorbeeld van een DSL, zoals beschreven in 2.4. Door deze DSL te gebruiken kunnen we op een databankonafhankelijke manier de verschillende tabellen speciceren, waarbij Rails dan deze DSL converteert naar databankspecieke DDL. Door migraties te gebruiken bereiken we dus in de eerste plaats databankonafhankelijkheid. Maar migraties lossen ook het probleem van versiecontrole op. De manier waarop dit gebeurt, is werkelijk 48

65 simpel. Iedere migratie krijgt een nummer. Maken we een nieuwe migratie aan, krijgt deze het volgende beschikbaar nummer. In de databank houdt Rails een tabel bij met één rij en één kolom met daarin de huidige versie van de databank. Wanneer we het migratiecommando ingeven, controleert Rails het verschil tussen de huidige versie en de beschikbare migraties die benoemd zijn volgens conventie (de nummer van de migratie vooraan). De migraties die hoger zijn dan de huidige versie worden toegepast en de huidige versie wordt verhoogd. Analoog kunnen we ook zeer eenvoudig teruggaan naar een vorige versie van de databank via het omgekeerde commando. Daartoe dient de methode down. In deze methode dienen we de omgekeerde operaties uit te voeren dan diegene die zijn toegepast in het migreren naar een hogere versie via de methode up. In het voorbeeld is de omgekeerde versie van het aanmaken van een tabel het verwijderen van die tabel. Zoals ook uit het voorbeeld blijkt is de DSL van migraties vrij uitgebreid. Merk op dat we in het voorbeeld niet helemaal eerlijk zijn geweest. De uitdrukking foreign_key behoort namelijk niet tot de lijst van uitdrukkingen van migraties. Migraties bieden geen ondersteuning voor het opgeven van vreemde sleutels. De reden die hiervoor wordt aangehaald, is dat men deze constraint op modelniveau dient aan te pakken. We delen echter de mening van Dave Thomas zoals beschreven in [17], waarin hij aangeeft de opgave van vreemde sleutels beter behoort tot de denitie van het datamodel. Naar zijn voorbeeld hebben we dus de DSL van migraties uitgebreid met volgende methode: 1 module MigrationHelpers 2 def foreign_key ( from_table, from_column, to_table ) 3 constraint_ name = " fk_#{from_ table }_#{from_column}" 4 execute %{ a l t e r t a b l e #{ from_table } add c o n s t r a i n t 5 #{ constraintname } f o r e i g n key (#{from_column }) 6 r e f e r e n c e s #{ to_table }( id ) } 7 end 8 end Codevoorbeeld 32: Uitbreiding van de migratie DSL voor vreemde sleutels Merk op dat dit MySQL specieke code is. Vreemde sleutels zijn dus een zwak punt van migraties, maar de verwachtingen zijn dat in volgende versies dit wel zal toegevoegd worden. Dit voorbeeld toont echter wel het dynamische karakter van goed Ruby aan. We hebben zonder weinig code de DSL kunnen uitbreiden met onze eigen code door middel van mix-ins. Als conclusie kunnen we dus stellen dat migraties een zeer welkome toevoeging zijn aan het Rails framework. De voordelen die migraties bieden negeren, zou niet verstandig zijn. We hebben dan ook migraties gecreeërd voor alle tabellen beschreven in Migraties zijn ook een waardevolle partner bij het toepassen van Agile Development. Niet alleen zijn ze zeer goed in het omgaan met verandering, maar ook het systeem van versienummering maakt het werken in iteraties makkelijker. De denitie van migratie is namelijk het gradueel aanpassen van de databankschema's, en dit gradueel aanpassen is net waar het bij Agile methoden om draait Creatie van modelklassen Door het volgen van de Rails conventies tijdens het databankontwerp (zie 5.2.2), wordt ons heel wat werk bespaard bij de creatie van de modelklassen. Zo is alle datafunctionaliteit reeds voorzien door intelligente 49

66 toepassing van de metadata in de databank, wat betekent dat een modelklasse in deze fase slechts bestaat uit twee lijnen code (zoals gezien in 3.3.1) plus de validatie- en relatiedeclaraties, aangezien we in deze iteratie enkel tot doel hebben de data voor te stellen en weer te geven. Dit betekent dan ook dat het meeste werk bestaat uit het databankontwerp en dat de modellaag in feite hier uit voortvloeit. Onderstaande code toont bijvoorbeeld het model voor voedsel. 1 c l a s s Food < ActiveRecord : : Base 2 belongs_to : category 3 has_many : f o o d c o n t e n t s 4 has_many : n u t r i e n t s, : through => : f o o d c o n t e n t s 5 6 validates_presence_of : name, : category_id 7 validates_uniqueness_of : name 8 end Codevoorbeeld 33: Modelklasse voor voedsel Deze code, hoewel slechts zeven lijnen lang, laat volgende zaken toe: Het opvragen van de categorie van een voedingsmiddel (food.category) Het opvragen van de collectie van nutriënten en de bijhorende hoeveelheden ( food.nutrients en food.foodcontents). Merk op dat de declaratie in lijn vier aangeeft dat een voedingsmiddel meerdere nutriënten bezit, met als join-tabel foodcontents. Wanneer foodcontents een pure join-tabel zou geweest zijn (enkel vreemde sleutels) zou een gewone has_many declaratie al genoeg zijn. Nu dienen we echter aan te geven dat het model foodcontent tevens dienst doet als join-tabel via de through optie omdat een voedingsinhoud een volwaardig domeinobject is. Validatie van aanwezigheid van een naam en categorie en uniekheid van naam bij het persisteren van een modelobject van deze klasse. Alle functionaliteit bekomen via metadata, zoals beschreven in sectie 3.3.1: (dynamische) nders, CRUD operaties en getters/setters voor attributen. De rest van de modelklassen is compleet analoog. Enkel bij de tabel categories hebben we een attribuut parent_id voorzien. De naam voor het attribuut is niet zomaar gekozen maar volgt wederom een conventie. Deze zorgt ervoor dat automatisch een boomstructuur kan worden opgebouwd met de verschillende categorieën, wat betekent dat we de ouder en de collectie van kinderen van een bepaalde categorie kunnen opvragen alsof het om gewone attributen gaat. Hadden we de naamconventie niet gevolgd had dit vrij veel zelf geschreven logica betekend Data migraties: inladen van de Nubel tabel Migraties zijn niet meer dan Ruby code. Maar tegelijk zijn ze verweven in het Rails framework en hebben dus toegang tot alle componenten ervan. Meer bepaald hebben ze toegang tot de modelklassen, wat toelaat data in te laden met behulp van migraties. 50

67 Hoewel migraties in principe niet ontworpen zijn voor deze taak, is er geen enkele reden waarom we ze niet zouden gebruiken. In de eerste plaats biedt het gebruik van de eenvoudige modelsyntax een véél hogere productiesnelheid dan zelf een script in eender welke taal te schrijven welke hoogstwaarschijnlijk gebruik maakt van SQL. Ten tweede zorgen de modelobjecten er voor dat de integriteit van de data reeds verzekerd is bij het inladen, aangezien de validatieregels gedeclareerd zijn in de modellaag. Als laatste voordeel is uiteraard het inherente versiebeheer van migraties een pluspunt: indien er fouten in de data zitten is dit slechts een kwestie van terug neerwaarts migreren. Onderstaande code toont het gedeelte omtrent categorieën van de datamigratie die gebruikt werd om de data uit de Nubel tabel in te laden in de databank. Het is duidelijk uit regels acht tot veertien dat ActiveRecord het schrijven van dit script enorm vereenvoudigd. 1 c l a s s LoadNubelData < ActiveRecord : : Migration 2 def s e l f. up while ( l i n e = i n p u t F i l e. r e a d l i n e ) 5 s p l i t t e d L i n e = l i n e. s p l i t ( " ; " ) 6 categoryname = s p l i t t e d L i n e [ 0 ]. gsub (/NBS3 (([0 9]+\.)+). chomp 7 c a t e g o r y I c o n = ' / i c o n s / c a t e g o r i e s / ' << categoryname << '. png ' 8 category = Category. find_by_name ( categoryname ) 9 i f! category # c a t e g o r i e b e s t a a t n i e t 10 category = Category. new 11 category. name = categoryname 12 category. icon = c a t e g o r y I c o n 13 category. save 14 end 15 end 16 end 17 def s e l f. down 18 Category. d e s t r o y _ a l l 19 end 20 end Codevoorbeeld 34: Inladen van categorieën via een datamigratie Hoewel datamigraties in de literatuur slechts summier vermeld worden ([17]), verdienen ze volgens ons toch meer aandacht. Zogoed als elke webapplicatie is datacentrisch en zal dus op een bepaald punt data dienen in te laden. Zoals in het codevoorbeeld te zien is, werken we op domein- of modelniveau, wat veel minder regels code tot gevolg heeft. We kunnen dus zonder aarzelen stellen dat migraties een zeer belangrijke component van Rails zijn of zullen worden Business logica in modellaag Een heikel punt is het feit dat in Rails de business logica ingebed is in de modellaag. afvragen of deze aanpak wel de goede is. Men kan zich In de praktijk zijn er tal van mogelijkheden om business logica te plaatsen. Wanneer we Rails bekijken, zijn er concreet twee manieren om business logica te implementeren: 51

68 Business logica in de databank, door bijvoorbeeld gebruik te maken van stored procedures. Een aparte business tier die de business logica onafhankelijk van de databank encapsuleert, naar analogie met de originele J2EE applicatie. Algemeen wordt de eerste manier afgeraden, omdat zo teveel logica verweven wordt met het datamodel. Indien deze data ooit nog herbruikt dient te worden, is de tweede manier de beste oplossing. In de originele J2EE applicate wordt ook gebruik gemaakt van een business tier bovenop de data access tier. In de tegenwoordige n-tier architecturen is dit de meest gekozen optie. In Rails lijkt het echter alsof er geen onderscheid is tussen de data access- en business tier. De data access tier is echter zeker en vast aanwezig in Rails, zij het transparant doordat dit alles geautomatiseerd wordt. De modelklassen kunnen dan praktisch volledig tot een business tier gerekend worden, want uiteindelijk bevat zulke klasse enkel validatieregels en eigen logica. We kunnen dus stellen dat Rails de best practice uit de softwareontwikkeling volgt waarbij businesslaag en data access laag van elkaar gescheiden zijn. Door de transparante automatisatie is het onderscheid niet echt duidelijk, maar het is er zeker en vast wel. Opgesomd kunnen we dus in een Rails applicatie onderscheiden: Het datamodel: bevat fundamentele constraints zoals datatypes, sleuteldeclaraties en de naleving van referenties. De (transparante) data access laag: zorgt voor de standaard DAO functionaliteit zoals nders, CRUD operaties en relaties. Merk op dat deze data access laag automatisch mee evolueert met het datamodel, wat uiteraard heel wat werk uitspaart en perfect past in de Agile losoe om veranderingen makkelijk op te vangen. De business laag: dit zijn de modelklassen waarin domein-specieke logica staat Verandering: Services vs Rails Een terechte vraag is hoe Rails zou omgaan met veranderingen in de databank. In de originele applicatie werd dit elegant opgelost door de scheiding tussen de DAO laag en de services (business laag). Indien van vandaag op morgen beslist wordt om een andere databron dan de Nubel tabel te gebruiken, moet dit kunnen zonder al te veel werk. Deze vraag is een perfecte toetsing van een extreem geval van verandering, en dus ook van hoe Agile Rails op dat vlak wel is. De oplossing voor dit probleem lag niet voor de hand, zo bleek uit de kleine discussie die ontstond in de vakgroep GH-SEL. Het probleem is dat controllers afhankelijk zijn van veranderingen in het model, en dus afhankelijk zijn van zowel de data access- als de businesslaag in het geval van Rails. Deze afhankelijkheid zou desastreus kunnen blijken voor mogelijke veranderingen. Gesteld dat bijvoorbeeld één attribuut in de databank zou veranderen, kunnen zowel het model als een controller hier hinder van ondervinden. Het probleem komt in feite neer op een gemis van expliciete interfaces in Ruby, wat Java wel heeft. Een mogelijke oplossing die voorgesteld werd, was de originele applicatie te emuleren en een facade boven de modellaag te plaatsen. De controllers zouden dan met de facade praten in plaats van met de modellen. Hoewel dit hoogstwaarschijnlijk een correcte implementatie zal opleveren, is het duidelijk dat hier toch iets wringt aangezien we dan manueel sleutelen aan de vaste MVC architectuur van Rails. 52

69 De oplossing werd uiteindelijk aangegeven door Richard Conroy op de Ruby on Rails mailinglist. Hij stelde dat het uiteindelijk doel eigenlijk een applicatie met verschillende datasources is die structureel verschillen (schema), maar semantisch gelijk zijn (voedingsmiddelen). Deze uitdrukking kan dienen als schooldenitie voor Duck Typing (zie sectie 2.3.5), wat de uiteindelijke oplossing voor het probleem is. Bemerk dat de gelijkaardige semantiek de sleutel is waarom Duck Typing werkt. Bemerk echter ook dat wanneer de semantiek zou wijzigen dan ook de betekenis van de applicatie verandert, wat uiteraard niet de bedoeling kan zijn (bijvoorbeeld onze applicatie gebruiken om boeken te bestellen, hoe absurd dit ook klinkt). Er is op dit moment geen enkele taal of framework die zulke verandering kan opvangen, als het überhaupt ooit mogelijk zou zijn of moeten zijn.. Beschouw bijvoorbeeld het model Category, die het attribuut name bezit. Veronderstel dat deze kolom moet veranderen naar category_number, waarbij een bedrijfsspecieke identicatiecode gebruikt wordt. Er zijn nu twee mogelijkheden om de veranderingen in de controllers te beperken: Er wordt gewerkt van bij het begin met het attribuut name. Bij de attribuutsverandering implementeren we aan modelzijde de getter name, die het category_number retourneert. Het nadeel is dat het eigenlijk niet meer een naam is en dus verwarrend kan werken in latere onderhoudswerkzaamheden. Van bij de creatie van het model wordt een getter identication voorzien, die in eerste instantie de naam teruggeeft. Wanneer het attribuut in de databank wijzigt, verandert enkel de implementatie van deze getter naar het nieuwe attribuut. We kunnen Duck Typing in feite beschouwen als transparante interfaces. Het gebruik van Duck Typing vraagt enkel van de ontwikkelaar een meer lososche omschakeling, maar het resultaat is uiteindelijk hetzelfde. Dankzij Ruby is Rails dus opgewassen tegen dit soort van problemen. Het J2EE framework biedt echter wel een functionaliteit aan die Rails momenteel niet kan evenaren. Wanneer verschillende services dezelfde interface implementeren maar verschillen qua implementatie laat J2EE toe om at runtime deze service op te zoeken en te gebruiken. Zo kan eenvoudig de producent van de voedingsmiddelentabel gewijzigd worden indien de interface gerespecteerd wordt door de producent. Rails bezit op dit moment nog niet zulke high-level enterprise functionaliteit. Het is ook twijfelachtig of Rails dit ooit zal ondersteunen, aangezien dit absoluut niet de markt is waarop Rails zich richt Eenvoudige visualisatie: scaolding Het doel dat we voor ogen hadden bij de aanvang van deze iteratie is op dit moment bereikt, maar we hebben niets om aan de klant te tonen. Klanten zijn doorgaans minder of niet geïnteresseerd in de achterliggende code, maar des te meer in de mogelijke functionaliteit en performantie. Vanuit het standpunt van de ontwikkelaar is dit eerder ongelukkig, maar dit is nu eenmaal de realiteit. Men vraagt zich ook niet af hoe een wasmachine werkt, enkel of de machine goed werkt en dat ook snel doet. Als ontwikkelaar dient men in de eerste plaats te kiezen voor een degelijke, onderhoudbare en toekomstbestendige implementatie, maar het Agile manifest in het achterhoofd, mag de klant zeker en vast niet vergeten worden. Het zou handig zijn moesten we op dit moment een eenvoudige GUI ontwikkelen via enkele eenvoudige controllers en views. Maar dat zou betekenen dat we buiten de scope van de huidige iteratie gaan, iets wat in Agile Development ten stelligste wordt afgeraden. Rails voorziet een standaard oplossing onder de vorm 53

70 van scaolding voor dit probleem. Een scaold, vrij vertaald een werk in steigers, is in Rails-context een automatisch gegenereerde verzameling van controllers en views en laat toe alle CRUD operaties op de modellen grasch uit te voeren. Scaolds kunnen statisch of dynamisch zijn. In het eerste geval bestaat het scaolding uit het uitvoeren van een script dat aan introspectie van de modellen doet en op basis daarvan controller- en viewcode genereert. Zo zal het datatype onderzocht worden om bijvoorbeeld een tekstveld voor een string-attribuut te genereren in de view. Het resultaat van een statische scaold is fysieke Rails code. Dynamische scaolds bestaan op hun beurt uit scaolddeclaraties voor een model in een controllerklasse. Deze controller zal dan de CRUD operaties voor dat model aanbieden door at runtome de logica te generern. Het voordeel ten opzichte van statische scaolds is dat de controllers en views mee evolueren met aanpassingen in databank en model. Statische scaold vormen op hun beurt dan een basis die men manueel kan aanpassen en uitbreiden indien de stap naar een volwaardige GUI wordt gezet. Figuur 5.2 toont het resultaat van statische scaolding toegepast op het model voor een Categorie. Voor de andere modellen werd een soortgelijke scaold gegenereerd, zodat de klant de vruchten van deze iteratie al kan aanschouwen. Figuur 5.2: Resultaat van een statische scaold: een listing van de categorieën in de databank Testing Iedere iteratie dient het testen van de nieuwe code te bevatten. Algemeen kan testen omschreven worden als de fase waarin de correctheid en volledigheid van software wordt nagegaan. Tests bestaan uit een collectie van vragen wiens antwoord wordt nagegaan om te bewijzen dat de applicatie zich gedraagt zoals we vereisen of verwachten. Men moet steeds onthouden dat testen inherent subjectief is aan de ontwikkelaar(s) die de tests schrijven en zodoende (onbewust) vaak niet alles getest wordt. Belangrijker in het opzicht van Agile Development is dat (goede) tests garanderen dat niet meer naar voltooide iteraties moet gekeken worden. Indien we een set van tests voor een voltooide iteratie hebben 54

71 geschreven, kunnen deze tests in een verder stadium herhaald worden om na te gaan of de verandering - die inherent is aan Agile Development - geen impact heeft op vroegere implementaties. Rails maakt onderscheid tussen drie soorten test: unit tests, functionele tests en integratie tests. Deze drie testen respectievelijk modellen, controllers en de ow in of over één of meerdere controllers. Testing is volledig geïntegreerd in het framework en het schrijven wordt tot op een zekere hoogte aangemoedigd. De creatie van een model of controllerklasse heeft onmiddelijke ook de creatie van een unit- of functionele test tot gevolg. Het feit dat Rails een testframework bezit, dat bovendien geheel geoptimaliseerd is om te werken met de componenten van het framework, kan enkel als een groot voordeel gezien worden. In vele frameworks dient men namelijk de tests te schrijven met een afzonderlijk testframework, zoals bijvoorbeeld Jakarta Cactus of JUnit (zoals in de originele applicatie). Het gebruik van een inherent testframework betekent het verdwijnen van conguratie om de componenten en het testframework met elkaar te laten samenwerken. Het schrijven van tests wordt meestal gedreven door requirements. In de eerste plaats zijn er de customer requirements, waaronder vereiste functionaliteit, performantie en workow. Ten tweede zijn er de implementatie requirements, waarbij de implementatie een bepaald gedrag moet vertonen. De vereisten van de klant beperken zich in deze iteratie tot de soort data die moet worden opgeslagen en opgehaald. Meestal zullen de vereisten van een klant uitgedrukt worden in termen van een user interface, wat in deze iteratie van ondergeschikt belang is. Belangrijker in deze iteratie zijn de implementatie requirements: we moeten nagaan of de modellen zich gedragen zoals we wensen, want de correcte werking van de controllers in de volgende iteraties zal in de eerste plaats steunen op een correcte werking van de modellen. Voor deze iteratie kunnen we ons dus beperken tot unit tests, gezien de huidige GUI een tijdelijke is. Voor unit tests maakt Rails gebruik van de standaard Test:Unit module van Ruby, weliswaar aangepast om samen te werken met ActiveRecord. Een unit test is niets meer dan een Ruby klasse die overerft van de klasse Test:Unit:TestCase en waarvan de methodes de conventie volgen dat ze allemaal beginnen met test_. Een Ruby testcase bestaat dus uit meerdere methodes of tests. De verzameling van een aantal testcases noemt men een testsuite. Net zoals de meeste unit testing frameworks zijn de unit tests in Ruby opgebouwd rondom het idee van een setup methode en asserties. In de setup methode dient code te komen die algemeen geldt voor alle tests. Een assertie is een methodeoproep in een test waarvan de waarheidswaarde van de argumenten wordt nagegaan. Daarnaast zijn en er een heleboel aangepaste asserties die bijvoorbeeld testen op nil, gelijkheid van argumenten, waarden binnen intervalgrenzen, geworpen excepties, etc. zoals beschreven in [17]. Voor alle modellen werden unit tests geschreven die alle CRUD operaties en extra logica testen. Het testen nam ongeveer de helft van de iteratie in, omdat tests schrijven nu eenmaal uitgebreider is dan de logica zelf schrijven. Men moet namelijk alle mogelijke (rand)gevallen testen om te verzekeren dat de applicatie correct werkt. Ter illustratie staat hieronder de code uit de testcase voor categorieën die de Create operatie controleert. Meer bepaald wordt nagegaan of de validatieregels die de aanwezigheid en uniekheid van een naam vereisen worden gerespecteerd. Merk op dat de een variabele is die geïnitialiseerd wordt tijdens de setup duidt aan dat het hier om een instantievariabele gaat). 55

72 1 def test_save 2 valid_category = Category. c r e a t e ( : name=> ' The_Valid_Category ' ) 3 a s s e r t valid_category. save, ' Saving o f v a l i d category f a i l e d. ' 4 non_ valid_ category = Category. new 5 a s s e r t! non_valid_category. save 6 a s s e r save 7 assert_equal ActiveRecord : : Errors. default_error_messages [ : taken ], e r r o r s. on ( : name ) 9 end Codevoorbeeld 35: Unit Test voor het opslaan van een categorie We kunnen ook eigen logica testen. Beschouw bijvoorbeeld onderstaand voorbeeld. Het gaat hier om een callback (zie 3.3.6) die er voor zorgt dat subcategorieën worden verwijderd bij de verwijdering van een categorie. 1 def before_destroy 2 s e l f. c h i l d r e n. each do c h i l d 3 c h i l d. d estroy 4 end 5 end Codevoorbeeld 36: Callback voor het verwijderen van een categorie Onderstaande code toont de test voor deze callback. Eerst wordt een random boomstructuur van categorieën opgebouwd in regel vijf, waarna de wortel van deze boom wordt verwijderd in regel acht. Als de callback correct is, moet de gehele boom verwijderd zijn wat getest wordt op regel negen. 1 def test_cascading_delete 2 n r _ o f _ i n i t i a l _ c a t e g o r i e s = Category. count 3 nr_of_children = 10 4 nr_of_grandchildren = 5 5 parent = generate_tree ( nr_of_children, nr_of_grandchildren ) 6 assert_equal n r _ o f _ i n i t i a l _ c a t e g o r i e s + nr_of_children 7 + nr_of_grandchildren + 1, Category. count 8 a s s e r t parent. d estroy 9 assert_equal n r _ o f _ i n i t i a l _ c a t e g o r i e s, Category. count 10 end Codevoorbeeld 37: Unit test voor het controleren van de callback in vorig voorbeeld Testen is hét perfecte middel om ontbrekende of dubieuze logica te ontdekken. Tijdens het testen zijn sommige delen van de modellen herschreven, omdat de tests aantoonden dat het gebruik ervan te complex is. Tijdens het testen is ook gebleken dat bepaalde logica ontbrak in de modellen. Zo is bijvoorbeeld de callback zoals hierboven beschreven in de code geraakt in de test fase, omdat het testen ons deed nadenken over wat het verwijderen van een categorie nu juist betekent. 56

73 Fixtures In algemene test-termen bedoelt men met een xture een omgeving waarin tests uitgevoerd worden. In Rails echter, is een test xture een eenvoudige specicatie van de inhoud van een model. Test xtures worden geschreven volgens het CSV (Comma-Separated Value) of YAML (YAML Ain't Markup Language) formaat. Een xture voor een categorie ziet er in YAML formaat bijvoorbeeld als volgt uit: 1: vers_vlees_categorie id: "1" name: Vers Vlees icon: /icons/categories/vers_vlees.png parent_id: In testcases kan een xturesbestand gebruikt worden door de naam van de betreende modelklasse op te geven. Rails zal voor elke testmethode de betreende tabel leegmaken en opvullen met de data uit de xtures. Gezien een testsuite loopt onder de test environment (met eigen databank) is de developmenten productiedata automatisch beschermd. Dit betekent dat men in de testdatabank vrij spel heeft en data kan invoeren om alle randgevallen te testen, zonder werkelijke data te bevuilen. In het voorbeeld is ook te zien dat elke xture een unieke naam kan krijgen, die automatisch een lokale variabele met diezelfde naam genereert in de testmethodes. Fixtures zijn uitermate handig omwille van het feit dat ze door Rails automatisch herladen worden voor elke testmethode. Zo is de invloed van elke testmethode geïsoleerd van de andere testmethoden, wat heel wat werk bespaart. Extra handig is dat elk ActiveRecord object zichzelf kan uitschrijven in YAML formaat en het dus een koud kunstje is om een script te schrijven die alle data uit de databank omzet naar xtures zodat we de volledige Nubel tabel in xtureformaat voor handen hadden. Het lijkt misschien vreemd om deze data te gebruiken, maar van die data zijn we zeker dat ze consistent is door de validatieregels. Fixtures omzeilen namelijk ActiveRecord en gebruiken achterliggend rechtstreeks SQL, waardoor geen validatie van de data optreedt Het probleem van xtures Hoewel we in eerste instantie onder de indruk waren van de mogelijkheden van xtures, ging het enthousiasme snel over. Nadat de gehele databank werd omgezet naar xtures via een eenvoudig Ruby script, schoot de uitvoeringstijd van de testsuite naar 44 seconden. In zeker zin is dit niet te verwonderen, aangezien er voor bijvoorbeeld foodcontents meer dan xtures zijn. Dit van de harde schijf halen, parsen en voor elke methode uit de databank halen en herladen heeft onvermijdelijk een overhead. Maar het mag ook duidelijk zijn dat 44 seconden in de eerste iteratie onaanvaardbaar is om werkbaar te zijn. De oplossing bestaat er uit om een nieuw script te schrijven die niet de hele databank omzet naar xtures, maar slechts een fractie ervan. Hierdoor behouden we toch nog de voordelen van xtures. Het feit dat we onze xtures opzettelijk moesten beperken is zeker en vast een nadeel omdat we zo niet zeker zijn dat de tests conform zijn met de werkelijke data. In het ontwikkelingsteam van Rails hebben ze al meerdere malen met deze kritiek te maken gekregen en ze zijn nog steeds op zoek naar oplossingen of alternatieven. 57

74 Evaluatie van de iteratie Het is van uitermate groot belang dat bij het voltooien van een iteratie, deze geëvalueerd wordt. De evaluatie verloopt het best in samenspraak met de klant. De klant kan zo tijdig ingrijpen wanneer de applicatie dreigt te ontsporen, wat ook voordelig is voor de producent welke kosten bespaart door de reikwijdte van veranderingen te beperken tot de net voltooide iteratie. De evaluatie komt in feite neer op nagaan of de vooropgestelde functionaliteit verkregen is en het binnen de wensen van de klant valt. De evaluatie van deze iteratie verliep samen met dr. Kris De Schutter. De iteratie werd over het algemeen positief ontvangen en er dienden geen noemenswaardige aanpassingen doorgevoerd te worden. Dit is ook logisch, gezien deze iteratie amper werkelijk zichtbare functionaliteit bevat, wat uiteindelijk het enige interessepunt is van de klant. De nog vrij primitieve visualisatie werd als zeer positief ontvangen. Het is zeker een pluspunt om zo vroeg in de ontwikkeling al een resultaat, hoe eenvoudig ook, te kunnen tonen aan de klant. De iteratie werd afgewerkt op amper negen dagen, waarvoor de eer volledig voor het framework is. Hoewel we geen praktische ervaring hadden met Rails, blijkt het gebruik van conventies en vooral het consequent doorvoeren ervan al voor een hoge productiesnelheid te zorgen. Er werd wel steeds gekleurd binnen de lijnen van de niche-applicatie waarvoor Rails uitermate geschikt is. Het framework neemt dan een heleboel werk in eigen handen, waardoor er in deze iteratie eigenlijk zeer weinig code geschreven moest worden. In feite is negen dagen zelfs nog lang voor de hoeveelheid code, maar het gros van de tijd ging naar de testing en het vertrouwd raken met Rails. Er werd overeen gekomen dat de volgende iteratie om gebruikers zou gaan. Het gaat om basisoperaties op gebruikers dat toevoegen, verwijderen, editeren en bekijken omhelst. Bovendien werd gevraagd na te gaan of het mogelijk was om authenticatie en authorizatie van deze gebruikers te implementeren, want medische gegevens beveiligen is uiteraard een topprioriteit. 58

75 5.3 Iteratie 2: gebruikersbeheer, authenticatie en authorizatie Doelstelling Naast de data omtrent voeding is ook de data omtrent gebruikers even belangrijk in deze applicatie. Het is belangrijk dat deze informatie, zoals alle medische informatie goed beveiligd wordt tegen derden. Er moet dus met andere woorden gezorgd worden voor mogelijkheden om gebruikers te authenticeren om de applicatie te kunnen gebruiken. De volgende logische stap is de authenticatie om te vormen tot een authorizatiesysteem. Hierbij is er naast authenticatie ook nog een verschil tussen verschillende groepen van gebruikers. Zo kan de ene groep bepaalde delen van de applicatie zien terwijl de andere dit niet kan. Dit moet toelaten om in een latere iteratie bijvoorbeeld dokters toe te voegen die bepaalde delen van de applicatie kunnen gebruiken terwijl gewone patiënten dit niet kunnen Gebruikersbeheer: model Als we ons eerst even rationeel focussen op het beheer van gebruikers, zien we dat de wensen van de klant hieromtrent in feite zeer eenvoudig zijn. Bekijken, toevoegen, aanpassen en verwijderen van gebruikers is praktisch niets meer dan de standaard CRUD operaties. We weten uit de vorige iteratie dat Rails hier zeer geschikt voor is. We consulteerden de klant nogmaals om te bepalen welke informatie allemaal moest opgeslagen worden over gebruikers. Dit is ook iets dat eigen is aan Agile Development, bij de minste twijfel over iets dient de klant zo snel mogelijk geraadpleegd te worden. Ontwikkelaars hebben vaak een andere visie dan de klant, en een kleine moeite nu spaart misschien veel werk later. Het resultaat is de tabel zoals te zien op guur 5.3. De attributen voor het wachtwoord worden verder uitgediept in sectie Figuur 5.3: Tabel voor gebruikers Het model voor deze tabel is analoog aan alle andere modellen die we al aangemaakt hebben. Het enige extra dat we voor het User model hebben gedaan is de implementatie van een facade attribuut. Voor de gebruikers van de modelklasse is dit een attribuut zoals de andere, maar in feite bestaat er geen persistent equivalent voor dit attribuut zoals voor de andere wel. We hebben de techniek van facade attributen gebruikt om ervoor te zorgen dat men aan een gebruikersmodel de leeftijd van die gebruiker kan vragen. Dit is gewone Ruby code zoals hieronder weergegeven, die toelaat om user.age te gebruiken op dezelfde manier als user.name. 59

76 1 def age 2 i f s e l f. b i r t h d a t e 3 r eturn ( ( Date. today s e l f. b i r t h d a t e ) / ). to_i 4 end 5 r eturn n i l 6 end Codevoorbeeld 38: Implementatie van een facade attribuut (age) Gebruikersbeheer: controller en views Nu het model geïmplementeerd is, kunnen we de CRUD operaties implementeren door de controllers en views aan te maken. Aangezien het echter gaat om een tabel zonder relaties en we op dit moment enkel en alleen CRUD functionaliteit nodig hebben, biedt een scaold alles wat we momenteel vereisen via slechts één commando. Scaolds zijn werkelijk een handig hulpmiddel daar zowat elke webapplicatie nood heeft aan CRUD operaties, die nu geheel automatisch geïmplementeerd worden. Scaolds genereren makkelijk uitbreidbare controllercode die we zelf niet anders zouden geïmplementeerd hebben, maar de views zijn vrij eenvoudig. Dit is ook logisch aangezien de scaolds alle mogelijke modellen moeten kunnen ondersteunen en de view dus zo algemeen mogelijk moet zijn. Hoewel het geen vereiste is voor deze iteratie, zal de klant het des te meer apprecieren als het resultaat van deze iteratie in een mooier jasje gestoken wordt. Dé standaard voor de opmaak van webpagina's is W3C Cascading Style Sheets (CSS). Zonder dieper in te gaan op CSS, is het voordeel van CSS de scheiding van opmaak en inhoud. Concreet betekent dit dat de inhoud gegenereerd door de Rails applicatie opgemaakt wordt door regels vastgelegd in minstens één CSS bestand. Hoewel een goede layout zeer belangrijk is voor onze applicatie, wilden we er niet te veel tijd insteken omdat het ons zou aeiden van Rails. Een degelijke CSS-layout maken kost namelijk aanzienlijk wat tijd. In vele bedrijven hebben ze naast ontwikkelaars daarom ook team van designers die gespecialiseerd zijn in zulke zaken. We hebben daarom beroep gedaan op de artistieke kunsten van Luka Cvrk ( die zijn sobere doch elegante CSS-template vrijgegeven heeft via Open Source Web Design ( onder een open licentie. Rails maakt het zeer eenvoudig via één declaratie om een bepaalde CSS template te gebruiken in de views. Het zou echter on-dry zijn, moesten we de declaratie in elke view moeten herhalen. Rails lost dit mogelijk door zogenaamde layouts toe te laten. Een layout is een gewone RHTML le, die echter het keyword yield ergens op de pagina bevat. Een layout heeft een vaste plaats in de Rails directorystructuur en zal automatisch voor elke view gebruikt worden, tenzij expliciet opgegeven wordt om dit niet te doen. Op de plaats waar yield staat, wordt bij het renderen dan de output van de view gezet. Op deze manier kunnen we de algemene opmaak van onze pagina's vastleggen in de layout en hoeven de views hier geen rekening mee te houden. De view behorende bij een bepaalde controllermethode is dus meestal zeer kort en aanpassingen aan de layout voor elke pagina dienen slechts op één plaats te gebeuren. Door de CSS te includeren in de layout respecteren we het DRY principe en bekomen we een zeer elegante scheiding van opmaak, layout en views. De functionaliteit van vorige iteratie brachten we onder in de FoodController, die van deze paragraaf onder de AdminController, daar we van plan zijn om later enkel administrators gebruikersbeheer te laten doen. De scaolds uit vorige iteratie werden aangepast om samen te werken met de layout. 60

77 Figuur 5.4 toont bijvoorbeeld de lijst van gebruikers, gegegeneerd door de methode list_users van de AdminController. Om het geheel wat op te vrolijken hebben we tevens gebruik gemaakt van de open source Silk iconen set 1 zoals te zien is op de guur. Figuur 5.4: Screenshot: lijst van gebruikers Tot slot hebben we nog één techniek gebruikt, welke we nog niet besproken hebben. Dankzij het gebruik van layouts wordt de view code al aanzienlijk gereduceerd, maar het kan nog beter. Naast de layoutcode (die echter voor alle views gelijk is), is de code voor de screenshot op guur 5.4 werkelijk zeer klein: 1 <table > 2 <h3>users ( t o t a l = l e n g t h %>) </h3> 3 <%= render ( : p a r t i a l => ' user ', : c o l l e c t i o n ) %> 4 </table > Codevoorbeeld 39: View-code voor de lijst van gebruikers in de databank De tweede lijn is voor de blauwe titelbalk. De derde lijn zet alle gebruikers op het scherm door gebruik te maken van een partial template (ook partials genoemd). Partials zijn RHTML fragmenten die kunnen ingevoegd worden in andere RHTML views en die mogelijks parameters meekrijgen. Hun hoofddoel is om de view logica eenvoudiger te maken. Zoals de declaratie aangeeft wordt in het voorbeeld de collectie van gebruikers meegegeven als parameter aan de partial. De partial zelf ziet er als volgt uit:

78 1 <tr> 2 <td> <%= image_tag ( ' user. png ', : s i z e => ' 16 x16 ' ) %> </td> 3 <td> <%= user. name %> </td> 4 <td> <%= user. real_name %> </td> 5 <td> age : <%= user. age %> </td> Codevoorbeeld 40: Partial template voor de visualisatie van een gebruiker We hadden even goed geen partials kunnen gebruiken, de output zou er net hetzelfde uitzien. Partials hebben echter het voordeel dat ze extreem herbruikbaar zijn. Vele webapplicaties tonen vaak dezelfde informatie over bepaalde objecten op verschillende pagina's. Door gebruik te maken van dezelfde partials wordt duplicatie vermeden, een uiting van DRY dus. Partials kunnen het best beschouwd worden als een soort van subroutine die kan opgeroepen worden uit eender welke template. Zoals te zien in het voorbeeld zijn partials zeer geschikt voor het renderen van collecties van objecten. Partials kunnen ook gebruikt worden in controllers, waar ze vooral een rol spelen bij het gebruik van Ajax, waar we in de volgende iteratie dieper op in gaan. Partials zijn een eenvoudig, maar toch zeer krachtig mechanisme. Het laat werkelijk toe de view te ontkoppelen en hergebruik te stimuleren, wat niet zo een evidentie is in omgevingen die met HTML dienen te werken Agile Development: Rails, architectuur en consistentie Zoals de losoe van Agile Development voorschrijft, zijn we vertrokken zonder een initieel architectuurontwerp. Door echter enkel de conventies voorgesteld door Rails te volgen, heeft zich al een soort van architectuur ontwikkeld voorgesteld op guur 5.5. Deze architectuur is in wezen niet meer dan het klassieke MVC, maar geforceerd toegepast door Rails. Hier is niets mis mee, deze architectuur is degelijk en zeer modulair, wat steeds een pluspunt is naar onderhoud en teamwerk toe. Een kanttekening die we er echter nog dienen bij te plaatsen, is die van consistente keuzes. In Agile Development is het belangrijk om consistent best practices te volgen en deze ook toe te passen. We hadden bijvoorbeeld kunnen kiezen om het gebruiksbeheer in te voegen bij de vorige controllerlogica. Dan volgen we nog steeds de Rails conventies, maar het mag duidelijk zijn dat hier het separation of concerns principe allesbehalve is toegepast. Laat het duidelijk zijn dat men in Agile Development misschien meer gevaar loopt om inconsistent te werken, omdat men zich steeds beperkt tot de scope van de iteratie. In ons geval blijkt dat nog mee te vallen, maar gesteld dat men in teamverband werkt, lijkt ons dat niet zo een evidentie. Het lijkt ons ook van groot belang dat ontwikkelaars die Agile ontwikkelen, goed op de hoogte moeten zijn van design patterns en best practices. In Agile Development is het namelijk vaak makkelijker om aan Cowboy Coding te doen om iets snel te laten werken, maar dat dit intern een puinhoop is. Daar waar men in traditionele ontwikkelingsmethoden een architectuurontwerp rekening houdt met onderhoudbaarheid en toekomstbestendigheid, is dit bij Agile Development moeilijker te behalen zonder een gezonde portie vastberadenheid. Geconcludeerd vereist Agile Development dus een zekere discipline en ervaring in applicatie-ontwerp van de ontwikkelaar. 62

79 Figuur 5.5: MVC architectuur op dit moment in de ontwikkeling Persistentie van het wachtwoord Zoals te zien op guur 5.3 op bladzijde 59, slaan we niet gewoon het wachtwoord op. Indien we de wachtwoorden van de gebruikers zouden opslaan als gewone tekst in de databank, zou het kunnen dat malade gebruikers deze wachtwoorden kunnen achterhalen door gebruik te maken van fouten in de implementatie (bijvoorbeeld door SQL injection). Om dit te vermijden, maken we gebruik van de techniek die een wachtwoord salt en hashed. Het User model wordt hiertoe aangevuld met een facade attribuut password waarvoor we volgende setter deniëren: 1 def password=( pass ) 2 s e l f. password_salt = random_string (255) 3 s e l f. password_hash = User. encrypt ( pass, s e l f. password_salt ) 4 end 5 6 def s e l f. encrypt ( pass, s a l t ) 7 Digest : : SHA256. h e x d i g e s t ( pass + s a l t ) 8 end Codevoorbeeld 41: Setter methode voor het facade attribuut password In plaats van het wachtwoord te persisteren, genereren we op regel twee een random string (de salt) en wordt de concatenatie van het werkelijke wachtwoord en de salt gehashed via de SHA256 encryptie techniek (regel drie). SHA256 is een encryptiestandaard die vaak gebruikt wordt in de industrie. De reden waarom dit werkt is vrij wiskundig, maar bestaat eruit dat het nemen van de hashfunctie van eenzelfde string steeds dezelfde hash oplevert, maar dat het onmogelijk blijkt om uit de hash de oorspronkelijke string te halen. Eenvoudig gezegd zal de hashfunctie van een wachtwoord steeds dezelfde hash bekomen, 63

80 maar zal vanuit de hash het wachtwoord niet gereconstrueerd kunnen worden. Door het wachtwoord eerst nog eens te salten met een random string vermijden we dat zwakke wachtwoorden gemakkelijk gekraakt worden. Uiteraard dienen we de salt wel te persisteren om later de hash te kunnen berekenen, maar hiermee is een kraker praktisch niets, omdat deze ook niet weet hoe deze salt gecombineerd wordt met het wachtwoord. Indien we nu willen nagaan bij het inloggen of een gebruiker correcte gegevens ingeeft, dient het ingegeven wachtwoord geconcateneerd te worden met de salt uit de databank en hiervan de hash te berekenen. Indien de bekomen hash en de hash in de databank identiek zijn, zijn de logingegevens correct. Onderstaande code toont hoe dit principe zich vertaalt in Railslogica: 1 # Geeft de g e b r u i k e r terug i n d i e n c o r r e c t e l o g i n g e g e v e n s, anders n i l 2 def s e l f. a u t h e n t i c a t e ( username, pass ) 3 user = s e l f. find_by_name ( username ) 4 i f user!= n i l and user. password_hash!= User. encrypt ( pass, user. password_salt ) 5 user = n i l 6 end 7 r eturn user 8 end Codevoorbeeld 42: Authenticatie van een gebruiker Authenticatie De volgende stap bestaat er nu in om er voor te zorgen dat niet eender wie toegang heeft tot de applicatie. Authenticatie komt er op neer dat voor bepaalde delen te bekijken de gebruiker correcte credentials moet opgeven. Er bestaan tal van plugins voor het Rails framework om authenticatie te verwezenlijken, zoals de act_as_authenticated plugin 2, maar we hebben er voor gekozen om het authenticatiesysteem zelf te schrijven. We doen dit omdat we zo een perfect zicht hebben op de beveiliging van de medische gegevens, waar met plugin de controle veel lager ligt. Had het niet om medische gegevens gegaan, hadden we hoogstwaarschijnlijk om tijd te besparen geopteerd voor een plugin. Voor de implementatie van het authenticatiesysteem baseren we ons op de mechanismen zoals beschreven door Chad Fowler en Tom Moertel in [4]. Het grootste probleem dat we dienen te overbruggen is de inherente toestandsloosheid van het web, wat zou betekenen dat op iedere nieuwe of ververste pagina de gebruiker opnieuw zou moeten authenticeren. Dit is uiteraard onaanvaardbaar, maar dit kunnen we makkelijk oplossen door gebruik te maken van sessies en lters (lters werd reeds besproken in sectie 3.4.3). Per gebruiker is er namelijk juist één sessie-object, dat in feite niets meer is dan een container voor allerhande soorten informatie. Rails zorgt achter de schermen ervoor dat een inkomende request, die in feite niet kan onderscheiden worden van een willekeurig andere, gelinkt wordt met het juiste sessie-object. De manier waarop Rails dit doet, is door gebruik te maken van een client-side bestand (cookie) met een unieke id die mee wordt gestuurd met de request. Om het sessie-object aan serverzijde op te slaan, is er een scala aan mogelijkheden van bestandsgebaseerd tot oplossingen met distributed Ruby. We hebben gekozen voor de ActiveRecordStore manier, waarbij de

81 sessie-objecten opgeslagen worden in de databank. De reden voor deze keuze is dat deze oplossing makkelijk schaalt naar meerdere Rails servers toe en niet al te complex is. De sessie-objecten zelf steunen op het gebruik van een unieke identicatiecode en cookies aan clientzijde. De werking van authenticatiesysteem wordt voorgesteld op guur 5.6. Figuur 5.6: Authenticatie Omdat de logica voor het inloggen niet behoort tot de Admin- of FoodControllers hebben we een aparte controller (LoginController ) aangemaakt met daarin de login en logout methoden. Gesteld dat de gebruiker de normale weg volgt en via de login pagina van de login methode probeert toegang te krijgen tot de applicatie kunnen we volgende stappen onderscheiden (zie guur 5.6). Stap 1: de gebruiker vraagt via de url /login de login pagina op. Indien de corresponderende login methode een HTTP GET binnenkrijgt, zal enkel de view gerendered worden (zie code stap 2). Stap 2: de gebruiker geeft zijn gebruikersnaam en wachtwoord in via het formulier in de view van stap één. Onderstaande code toont de logica voor de login methode en wordt verder besproken in de volgende stappen. De request.post? declaratie op regel drie gaat na of het om een HTTP-POST bericht gaat. Indien het een HTTP-GET bericht zou zijn, wordt de conditionele code niet uitgevoerd en wordt gewoon de view gerendered. Deze view toont een loginpagina met een loginformulier. Indien gegevens worden doorgegeven via dat formulier, zal het zoals standaard is voor HTML formulieren via een HTTP-POST bericht verstuurd worden naar dezelfde login methode. Deze werkwijze is typisch voor Rails controllermethoden, om de hoeveelheid acties te beperken zonder complex te worden. 65

82 1 def l o g i n 2 s e s s i o n [ : user ] = n i l 3 i f r e q u e s t. post? 4 user = User. a u t h e n t i c a t e ( params [ : username ], params [ : password ] ) 5 i f user!= n i l 6 s e s s i o n [ : user ] = user. id 7 r e d i r e c t _ t o ( : c o n t r o l l e r => ' food ', : a c t i o n => ' index ' ) 8 end 9 end 10 end Codevoorbeeld 43: Loginmethode van de LoginController Stap 3: mogelijks ingelogde gebruikers op dezelfde client worden verwijderd (lijn twee). Stap 4: De logingegevens worden gecontroleerd (lijn vier) door middel van de methode zoals besproken in sectie Indien de logingegevens correct blijken, wordt de id van de gebruiker in het sessie-object gestoken en wordt de gebruiker doorgestuurd (redirect op regel zeven naar een indexpagina). Stap 5: Indien de logingegevens incorrect blijken te zijn, wordt de loginpagina herladen met een foutmelding (niet getoond in code). Dit gebeurt doordat de view van de login methode automatisch gerendered wordt op het einde van de methode. Merk tevens op dat het met deze code perfect mogelijk is dat een gebruiker op verschillende fysieke systemen ingelogd kan zijn met dezelfde inloggegevens. We dienen echter ook nog te voorzien dat een gebruiker een willekeurige URL kan intypen en zodoende de authenticatie omzeilen. Dit kan opgelost worden door gebruik te maken van het sessie-object en lters. De rechterzijde van guur 5.6 toont de werking aan. Stap a: een gebruiker geeft een willekeurige URL in. Aangezien een URL overeenkomt met een bepaalde controller en methode, zal de request dus eerst de ApplicationController passeren daar dit de superklasse is van alle controllers. Stap b: zoals in onderstaande code te zien is, deniëren we in de ApplicationController een before- lter (die dus wordt opgeroepen voor elke methode) die nagaat of de gebruiker de pagina mag bekijken. Merk op dat enkel de login en logout pagina geen invloed ondervinden van de lter op regel twee). 66

83 1 c l a s s A p p l i c a t i o n C o n t r o l l e r < A c t i o n C o n t r o l l e r : : Base 2 b e f o r e _ f i l t e r : check_authentication, : except =>[: l o g i n, : l o g o u t ] 3 4 def check_authentication 5 u n l e s s s e s s i o n [ : user ] 6 r e d i r e c t _ t o : c o n t r o l l e r => ' l o g i n ', : a c t i o n => ' l o g i n ' 7 r eturn f a l s e # Break o f f i l t e r chain i f r e d i r e c t f a i l s 8 end 9 end 10 end Codevoorbeeld 44: Authenticate verloopt via een lter in de ApplicationController Stap c: De lter gaat voor iedere methode na of er in de sessie een gebruikers-id zit. Stap d: Indien dit niet zo is, wordt de gebruiker doorgestuurd naar de loginpagina. Stap e: Indien wel, mag de gebruiker de gewenste pagina bekijken. De lter heeft dan geen eect. Met bovenstaand mechanisme zijn we verzekerd dat onze webapplicatie alvast beschermd is tegen ongeregistreerde gebruikers. Filters zijn bij de implementatie een prachtig hulpmiddel gebleken om het DRY principe te blijven hanteren. Hadden we immers geen lters gehad, dienden we misschien in iedere methode een oproep naar check_authentication te plaatsen Authorizatie Het authenticatiesysteem uit sectie kan eenvoudig en met dezelfde mechanismen uitgebreid worden om naast authenticatie ook authorizatie van gebruikers toe te laten. Authorizatie komt neer op een onderscheid maken tussen groepen van gebruikers, waarbij de ene groep meer of minder mogelijkheden heeft dan andere. Om authorizatie te implementeren, maken we gebruik van het klassieke rollen-rechten systeem zoals beschreven in [4]. Op het laagste niveau zijn er rechten, waarbij een recht de toegang tot een bepaald gedeelte van de applicatie voorstelt. We hebben geopteerd om de authorizatie toe te passen op het niveau van controllers. Dit betekent dat een recht bijvoorbeeld het recht op gebruik van de FoodController voorstelt. We zullen de applicatie opdelen in een aantal modules (AdminModule, DieetModule) zodanig dat de functionaliteit van de module overeenkomt met die van een controller en we dus in feite rechten toekennen op het gebruik van modules. Op het volgende niveau zijn er de rollen. Een rol is in feite een benoemde collectie van rechten, die op het hoogste niveau worden toegekend aan een gebruiker. Zo kunnen we de rol 'Administrator' met rechten op alle modules aanmaken en deze toekennen aan een gebruiker. Deze gebruiker zal dan toegang hebben tot de gehele applicatie. Een gebruiker is niet beperkt tot één rol, maar kan zoveel rollen hebben als nodig. Uiteraard dienen we deze informatie persistent bij te houden in de databank. De structuur van de tabellen is te zien op guur 5.7. De implementatie van de tabellen werd wederom uitgevoerd via een migratie. De tabellen van guur 5.7 voldoen allen aan de Rails conventies. Een conventie die voorheen nog niet gebruikt is de conventie van de pure jointabellen (foodcontents was geen pure jointabel maar ook een 67

84 Figuur 5.7: Tabellen voor authorizatiesysteem entiteit op zich, hier is de tabel enkel aanwezig om een veel-veel relatie uit te drukken). Zowel roles_users en rights_roles hebben ten eerste geen primaire sleutel en hebben enkel en alleen vreemde sleutels als attribuut. In Rails conventies betekent dit dat het hier niet gaat om een domeinentiteit en er dus ook geen overeenkomstig model voor bestaat. Ten tweede is de naamgeving wederom niet toevallig. De naam voor een jointabel voor de relatie tussen twee tabellen in Rails is samengesteld uit de naam van de eerste en de tweede tabel, gescheiden door een underscore en alfabetisch geordend. Indien we deze conventies volgen zullen we na de implementatie van de migratie nooit meer te maken krijgen met de jointabellen. Rails zal achter de schermen de hele logica regelen, zoals bijvoorbeeld automatische join operaties. Nadat we in de modelklassen de relatiedeclaraties hebben ingevoerd, kunnen we bijvoorbeeld de rollen van een gebruiker opvragen (user.roles) alsof het om gewone attributen gaat. Hetzelfde geldt uiteraard voor de rechten van een rol. Rails maakt dus het vaak niet zo triviale werken met jointabellen wel zéér eenvoudig door zoals gewoonlijk de juiste conventies te volgen. Doorheen de vorige en deze iteratie is het nu al meermaals gebleken dat het Convention over Conguration principe in Rails veelvuldig kan gebruikt worden, maar tegelijk ook voor een grote productiviteitswinst zorgt. Het nagaan of een gebruiker recht heeft op het gebruik van een bepaalde controller is pure business logica. We brengen dit dan ook onder in de User model klasse. Om echter toekomstige aanpassingen aan het authorizatiesysteem aan te kunnen, zullen we de beslissing delegeren naar de rollen. Op die manier schermen we het User model af van de kennis van rechten en zijn de modellen niet rechtstreeks gekoppeld. De logica staat hieronder weergegeven. Dit is eenvoudige Ruby code die alle rollen overloopt en detecteert of één van de rollen de juiste rechten heeft. Zoals te zien is, wordt de beslissing inderdaad doorgegeven naar het Role model. 1 def has_right_for?( controller_name ) 2 r o l e s. d e t e c t { r o l e r o l e. has_right_for?( controller_name ) } 3 end Codevoorbeeld 45: Controle of een gebruiker een juiste rol heeft Om de modellen nog minder te verweven in elkaar, wordt ook in het Role model de beslissing doorgegeven naar het lager niveau. Op deze manier kunnen we de implementatie van het rechtensysteem later wijzigen (bijvoorbeeld naar methodeniveau in plaats van controllerniveau) zonder dat de bovenliggende lagen er last van zullen hebben: 1 def has_right_for?( controller_name ) 2 r i g h t s. d e t e c t { r i g h t r i g h t. has_right_for?( controller_name ) } 3 end Codevoorbeeld 46: Controle of een rol het juiste recht heeft 68

85 Op het laagste niveau, het niveau van de rechten, is de beslissing triviaal. Er dient enkel nagegaan te worden of de controller meegegeven als parameter gelijk is aan de controller in de databank voor dat recht: 1 def has_right_for?( controller_name ) 2 r eturn c o n t r o l l e r == controller_name 3 end Codevoorbeeld 47: Controle of een recht toegang geeft tot het gebruik van een controller De implementatie van authorizatie is nu triviaal. Op geheel analoge wijze als het authenticatiesysteem in sectie kunnen we nagaan of de huidige gebruiker de nodige rechten heeft om de gewenste module te gebruiken. We maken wederom gebruik van een before_lter in de ApplicationController die de hierboven beschreven logica gebruikt. De code staat hieronder weergegeven, maar toont geen nieuwigheden ten opzichte van authenticatie. Merk op dat we authenticeren voor authorizatie, wat logisch is. Merk ook op dat de lters false teruggeven bij faling. Dit is nodig om de ketting van lters af te breken, zodat bijvoorbeeld geen authorizatie meer wordt gedaan nadat de authenticatie mislukt is. 1 c l a s s A p p l i c a t i o n C o n t r o l l e r < A c t i o n C o n t r o l l e r : : Base 2 b e f o r e _ f i l t e r : check_authentication, 3 : check_authorization, 4 : except => [ : l o g i n, : l o g o u t ] 5 p r o t e c t e d 6 def check_authorization 7 user = User. f i n d ( s e s s i o n [ : user ] ) 8 u n l e s s user. has_right_for?( s e l f. c l a s s. c o n t r o l l e r _ p a t h ) 9 f l a s h [ : message ] = "You are not a u t h o r i z e d to view t h i s page " 10 r e d i r e c t _ t o ( : c o n t r o l l e r => " food ", : a c t i o n => " index " ) 11 r eturn f a l s e # Break f i l t e r chain 12 end 13 end 14 end Codevoorbeeld 48: Authorizatie via een lter in de ApplicationController Vervolgens hebben we enkele standaard rechten, rollen en gebruikers aangemaakt via een eenvoudige datamigratie. We implementeerden alle CRUD operaties voor rechten en rollen in de AdminController. We pasten de view aan om de nieuwe functionaliteit aan te bieden. Hier is toch een drietal dagen werk in gegaan, maar daar dit alles niets nieuws aanbrengt in de discussie, gaan we dit hier niet verder uitwerken. De code is steeds te inspecteren op bijgeleverde CD-ROM bij deze scriptie Functional Testing In de vorige iteratie hebben we ons toegespitst op unit testing, aangezien het bestaan van de toenmalige controllers en corresponderende views toen nog onzeker was. Deze beslissing is opportuun gebleken, daar we in deze iteratie de controllers hebben herschreven en een echte view hebben ontwikkeld. Tests zijn in zekere mate afhankelijk van implementatie en zijn dus niet sterk in het opvangen van verandering. In software-ontwikkeling en in Agile Development in het bijzonder kan men nooit voor 100% zeker zijn van 69

86 toekomstige veranderingen, maar we menen dat deze controllers en views in grote lijnen toch de nale versie zullen halen. In deze iteratie hebben we vooral controllercode geschreven, die we dan ook willen getest zien. Controllers dirigeren het orkest: ze ontvangen een request van een gebruiker, halen data op via modelobjecten en beantwoorden de aanvraag door de corresponderende view aan te roepen. Het testen van controllers is dus van een geheel andere soort dan het testen van de modellaag. Daar waar voor modellen de business logica dient getest te worden, moet voor controllers nagegaan worden of een bepaalde request het juiste antwoord als resultaat heeft. Het testen van controllers gebeurt in Rails door middel van zogenaamde functionele testen (Functional Tests). We hebben voor de nieuwe modellen (User, Role & Right) ook voldoende unit tests geïmplementeerd. We hebben echter Unit Tests reeds besproken in sectie 5.2.9, waardoor we deze hier niet meer behandelen. Waar Unit Tests de logica testen vanuit het standpunt van de ontwikkelaar, kunnen functionele testen eerder gezien worden vanuit het standpunt van de gebruiker. Aangezien de controllerfunctionaliteit voor de gebruiker bereikbaar is via URL's moet een soortgelijke aanpak vanuit het testing framework mogelijk zijn. Het Rails testing framework biedt voor functionele testen alle mogelijkheden van unit tests aan. Dit betekent dat functionele tests ook gebaseerd zijn op asserties, een setup en xtures. Extra voor de functionele testen zijn methoden die HTTP-berichten emuleren (get, post, etc.) en de specieke controllerasserties. Voor de drie controllers schreven we voldoende functionele testen om ons ervan te verzekeren dat de controllers correct werken. Onderstaand codevoorbeeld toont de functionele test voor de creatie van een gebruiker. Op regels dertien en negentien is duidelijk te zien dat een HTTP-bericht geëmuleerd wordt: de parameters die anders zouden verzameld worden via een HTML-formulier worden nu rechtstreeks doorgegeven via methoden van het testframework. Het voorbeeld toont ook duidelijk aan dat alle asserties van unit tests gebruikt kunnen worden (assert_equal bijvoorbeeld), maar er ook specieke asserties voor functionele tests zijn (assert_response bijvoorbeeld). Merk op dat we in de setup-methode een ingelogde gebruiker simuleren. Aangezien de loginmethode getest wordt in de functionele test voor de LoginController, kunnen we in deze test er van uitgaan dat het inloggen correct verloopt. 70

87 1 def setup c o n t r o l l e r = AdminController. new = A c t i o n C o n t r o l l e r : : TestRequest. new = A c t i o n C o n t r o l l e r : : TestResponse. new 5 # Fake the l o g i n, by manually s e t t i n g the s e s s i o n = User. find_by_name ( ' admin ' ) s e s s i o n [ : user ] id 8 end 9 10 def test_create_user 11 # 1) c o r r e c t user 12 nr_of_users = User. count 13 post : create_user, : user => { : name => ' some_user ', 14 : password => ' eenwachtwoord ', : r o l e _ i d s =>[1] } 15 a s s e r t _ r e s p o n s e : s u c c e s s 16 assert_template ' l i s t _ u s e r s ' 17 assert_equal nr_of_users + 1, User. count 18 # 2) i n c o r r e c t user ( password too s h o r t ) 19 post : create_user, : user => { : name => ' i n c o r r e c t _ u s e r ', 20 : password => ' b l a a t ', : r o l e _ i d s =>[1] } 21 a s s e r t _ r e s p o n s e : r e d i r e c t 22 a s s e r t _ r e d i r e c t e d _ t o : a c t i o n => ' add_user ' 23 end Codevoorbeeld 49: Setup en testmethode van de functionele test voor AdminController Agile Development draagt het omgaan met veranderingen hoog in het vaandel. Tests zijn echter vrij gevoelig voor veranderingen, daar ze noodzakelijk dicht bij de implementatie aanleunen. Veranderingen die problemen introduceren die voorheen ongekend waren, zullen dus zeker invloed hebben op de tests. In deze iteratie hebben we de modellen uit vorige iteratie licht aangepast op sommige plaatsen om consistent te zijn met naamgevingen. Door de tests opnieuw uit te voeren en na te gaan of er geen foutmeldingen ontstaan, zijn we verzekerd dat de veranderingen geen invloed hebben gehad op de functionaliteit. Hoe groter de applicatie en hoe meer iteraties, hoe belangrijker dit wordt. Enig heikel punt blijft dan nog dat men nooit zeker weet dat men alles getest heeft. Maar gesteld dat geautomatiseerd testen niet bestond, zou men na iedere iteratie alles manueel moeten testen, plus dat men nog steeds niet mag vergeten om alle gevallen te testen Integration Testing Zoals Rails core ontwikkelaar Jamis Buck opmerkt in [2], bevatten zowel unit als functionele tests een inherent nadeel: Unit tests zijn gefocust op het testen van één model. Functionele tests hebben als doel het testen van één controller en de gebruikte modellen. Maar wat met bugs die ontstaan door interacties over verschillende controllers heen? Wat als bugs veroorzaakt worden doordat twee of meer concurrente gebruikers inwerken op dezelfde data? De oplossing 71

88 hiervoor werd geleverd in versie 1.1 van het Rails framework met de toevoeging van integratie tests (Integration Tests). Merk op dat integratie testen geen vervanging vormen voor unit en functionele tests. Integratie tests opereren op een hoger niveau en zijn pas zinvol wanneer de functionaliteit apart grondig getest is in de unit en functionele tests. Integratie tests bevatten dezelfde mogelijkheden als beide op het vlak van asserties, HTTP-emulatie en xtures. Nieuw in integratie tests is de mogelijkheid om verschillende sessies (niet te verwarren met het sessie-object) op te starten die de interactie met applicatie door verschillende concurrente gebruikers voorstellen. Weinig van de door ons gekende testframeworks bieden heden soortgelijke functionaliteit aan. We hebben geen integratie tests zoals hier beschreven geïmplementeerd om redenen die duidelijk zullen worden in sectie Als dusdanig zullen we hier ook geen articieel voorbeeld tonen. In principe is er praktisch bijna geen verschil ten opzichte van functionele tests, behalve het gebruik van de sessies en het testen van meerdere acties in een testmethode. Zeer goede codevoorbeelden kunnen terug gevonden worden in [17] en [2] Scenario Testing Integratie tests zijn uitermate handig om customer requirements te testen. Vaak zijn deze requirements gegeven in de vorm van use-case scenario's na het uitvoeren van een functionele analyse. Beschouwen we bijvoorbeeld volgend mogelijk scenario: Een administrator logt in. De pagina die hij nu te zien krijgt, is die van het overzicht van de categorieën van voedsel. Wanneer hij op de 'Admin Module' link klikt, ziet hij de index pagina van deze module. Vervolgens klikt hij op de link 'add user' en voegt een nieuwe gebuiker toe. Hij logt dan uit. Dit scenario is perfect te implementeren en testbaar met een integratietest daar het gaat over verschillende controllers heen. Bij de implementatie hadden we echter het gevoel dat we vaak tests uit de unit en functionele tests aan het herhalen waren, behalve nu na elkaar. Doordat integratietests daarbij ook vrij lang zijn (net doordat ze verschillende controllers en modellen testen) lijken ze allerminst nog op de scenario's waar van vertrokken werd, maar zijn ze eerder een te lange opsomming van asserties en is het geheel moeilijk onderhoudbaar door de onoverzichtelijkheid. De oplossing, aangegeven door Mike Clark in [17], bestaat eruit gebruik te maken van Ruby's aangeboren capaciteit om eenvoudig DSL's te implementeren (zie sectie 2.4). De techniek die we hiertoe gebruiken is het at runtime uitbreiden van objecten, zoals beschreven in sectie Voor we dieper ingaan op het implementeren van de DSL, beschouwen we eerst het eindresultaat: 72

89 1 c l a s s AdminUseCasesTest < A c t i o n C o n t r o l l e r : : I n t e g r a t i o n T e s t 2 i n c l u d e AdminUseCasesDsl 3 4 FILIP = { : name => ' F i l i p ', : real_name => ' F i l i p Blondeel ', 5 : password => ' p i l i f ' } 6 7 d e f test_admin_logs_in_and_adds_user 8 bram = admin 9 bram. get ' / nephrology ' 10 bram. is_viewing ( ' l o g i n / l o g i n ' ) 11 bram. logs_in_with ( ' admin ', ' admin123 ' ) 12 bram. i s _ r e d i r e c t e d _ t o ( ' food ', ' show_meal ' ) 13 bram. clicks_on_link_to ( ' admin/ index ' ) 14 bram. is_viewing ( ' admin/ index ' ) 15 bram. clicks_on_link_to ( ' admin/add_user ' ) 16 bram. is_viewing ( ' admin/add_user ' ) 17 bram. adds_user ( FILIP ) 18 bram. logs_out 19 end 20 end Codevoorbeeld 50: Uitwerking van een scenario met de eigen gedenieerde DSL Het bekomen resultaat is een zeer goed voorbeeld van een DSL en is op zijn minst indrukwekkend eenvoudig te noemen. In de eerste plaats valt de zeer hoge leesbaarheid en bijhorende onderhoudbaarheid op. Het is bijna geen programmacode meer, maar het lijkt al op spreektaal. Merk op dat het hier wel degelijk om tests gaat. Elke regel hierboven is in feite een vermomming voor een heleboel asserties. De sleutel om zulke functionaliteit op zo'n hoog niveau te kunnen bekomen ligt in de eerste coderegel van de testmethode. De 'admin' declaratie is in feite een methode-oproep die een sessie teruggeeft en waarin at runtime de mogelijkheden van deze sessie worden aangemaakt. Anders gezegd, het object 'bram' is een sessie, waar gebruikersachtige methoden voor gedenieerd zijn. De 'admin' declaratie wordt in een aparte le gedeclareerd, die ingemixt wordt in de testklasse (tweede regel). Dit klinkt misschien vreemd, maar de code van de module 'AdminUseCasesDsl' (waarvan hieronder slechts een relevant stuk getoond) maakt veel duidelijk. Wanneer we 'bram = admin' schrijven, zien we dat achter de schermen een nieuwe sessie wordt gestart (open_session). Vervolgens deniëren we op deze sessie at runtime een methode (admin.adds_user ) waarachter de normale asserties en operaties zitten. De methodes van deze gemodiceerde sessie (bram) kunnen dan gebruikt worden in de oorspronkelijke testklasse (zoals logs_in_with, is_viewing, etc). 73

90 1 module AdminUseCasesDsl 2 def admin 3 open_session do admin 4 def admin. adds_user ( u s e r _ d e t a i l s ) 5 nr_of_users = User. count 6 post ' nephrology /admin/ create_user ', : user => u s e r _ d e t a i l s 7 assert_equal nr_of_users + 1, User. count 8 assert_not_nil User. find_by_name ( u s e r _ d e t a i l s [ : name ] ) 9 a s s e r t _ r e s p o n s e : r e d i r e c t 10 a s s e r t _ r e d i r e c t e d _ t o : c o n t r o l l e r => ' admin ', : a c t i o n => ' l i s t _ u s e r s ' 11 end Codevoorbeeld 51: Denitie van een DSL declaratie voor de scenario tests Aangezien we nog steeds bezig zijn met integratietesten kan de bekomen DSL dus ook concurrent gebruikt worde, waardoor zelfs de meest complexe scenario's kunnen uitgewerkt worden. Hieronder staat een voorbeeld van hoe bovenstaande code in concurrente versie er uitzien. Dankzij het sessie-mechanisme is dit amper extra werk. 1 d e f test_with_2_admins 2 bram = admin 3 k r i s = admin 4 bram. get ' / nephrology ' 5 bram. logs_in_with ( ' bram ', ' bram123 ' ) 6 k r i s. get ' / neprhology ' end Codevoorbeeld 52: Unit test voor het controleren van de callback in vorig voorbeeld Hoewel deze uitleg niet echt meer om Rails ging, vonden we dit zeker en vast vernoemenswaardig daar het ons een serieuze productiviteitswinst bij het schrijven van integratietests heeft opgeleverd. De algemene term voor het manipuleren van zichzelf of andere programma's is metaprogramming. Hoewel bovenstaand voorbeeld vrij eenvoudig is, is Ruby in staat tot meer ingewikkelde zaken zoals blijkt uit de weblog 3 van Ola Bini (hoofddeveloper van JRuby) Evaluatie De deadline voor deze iteratie werd nipt gehaald, omdat het testen een aanzienlijk deel van de tijd had ingenomen. De evaluatie verliep wederom met dr. Kris De Schutter. Het resultaat van de iteratie werd algeheel positief ontvangen. De overgang naar de professionelere layout was onverwacht, maar dat was geen probleem aangezien de deadline gehaald werd. Er dienden wel kleine aanpassingen aan de layout gedaan te worden, maar niets wereldschokkends. Ontwikkelaars zijn nu eenmaal vaak geen artistieke hoogvliegers. In feite was dit wel een beetje een overtreding ten opzicht van het Agile Development

91 principe, maar ons idee is dat het verder uitstellen van de layout later voor meer werk zou zorgen dan het nu in orde brengen. Volgens ons moet men niet blindelings de Agile Development losoën volgen, maar dient men ook de nodige portie gezond verstand te gebruiken. De nieuwe CRUD functionaliteit alsook de authenticatie en authorizatie werden aan een grondige test onderworpen. Alles bleek te werken zoals verwacht en de klant was tevreden. Een opmerking van de klant was dat nu eender welke gebruiker de links naar alle modules kan zien (bovenaan staat een menubalk met een link naar de verschillende delen van de webapplicatie), maar weliswaar er soms geen toegang toe had. De klant had graag gezien dat de links naar modules die ontoegankelijk zijn, verborgen worden. We zullen de wens van de klant naleven en dit implementeren. 75

92 5.4 Iteratie 3: maaltijd functionaliteit Doelstelling In de vorige iteraties zijn de basiscomponenten en bijhorende functionaliteit voor voeding en gebruikers volledig afgewerkt. In deze iteratie gaan we pogen deze twee te verbinden door de maaltijdfunctionaliteit toe te voegen. Concreet wil dit zeggen dat gebruikers na authenticatie voedingsmiddelen kunnen kiezen en hiermee een maaltijd samenstellen die gepersisteerd zal worden. Het ontwerp van de databank en modellen zou normaal geen problemen meer mogen opleveren na de ervaring met vorige iteraties. Tijdens de bespreking met de klant voorafgaande deze iteratie, bleek dat voor dit onderdeel een dynamische, moderne interface zou moeten ontworpen worden, naar analogie met de originele applicatie. Het probleem stelt zich dat een soortgelijke GUI voor het web moeilijker te bereiken zal worden, daar het statische HTML weinig ruimte voor dynamiek laat. Samen met de klant werd het uitzicht van de grasche interface geschetst en de vereiste functionaliteit besproken. De iteratietijd werd vastgelegd op drie weken (de vorige iteraties was dit twee weken), aangezien de hoeveelheid werk toch aanzienlijk leek. Gelukkig bleek na literatuurstudie dat Rails enkele voorzieningen aan boord heeft om exibele interfaces te ontwikkelen, waarover in volgende delen meer Standaard Rails: Modellen, Controllers en Views Het mag duidelijk zijn uit de vorige twee iteraties dat de implementatie van de basis van een iteratie steeds dezelfde werkwijze volgt in Rails. Hoewel het toch om aanzienlijk wat code gaat, gaan we er hier summier overgaan omdat er geen nieuwe zaken aan bod komen. Het is het vrij eenvoudig om snel resultaat te boeken wanneer men eenmaal vertrouwd is met de conventies van Rails. Voor deze iteratie was het slechts een kwestie van enkele uren voor de eerste vruchten van arbeid toonbaar waren. Op de tweede plaats betekent de consistentie ook een vereenvoudiging ten aanzien van toekomstig onderhoud of uitbreidbaardheid. Mits het volgen van de conventies, zal een ontwikkelaar die vertrouwd is met deze conventies snel zijn of haar weg vinden doorheen de code. In deze iteratie dienen we het mogelijk te maken om maaltijden per gebruiker te persisteren. Een maaltijd bestaat in feite uit een verzameling van voedingsmiddelen, met een bepaalde hoeveelheid. Deze vereisten vertalen zich in een vrij voor de hand liggend databankschema, zoals te zien op guur 5.8. Figuur 5.8: Databankschema iteratie 3 De functionaliteit werd ondergebracht in de FoodController, daar het conceptueel bij de Dieet Module behoort. Er werd niet afgeweken van het klassieke Rails stramien en conventies, zodanig dat de vereiste functionaliteit op zeer korte periode kon worden geïmplementeerd. De functionaliteit werd in eerste plaats ontwikkeld met behulp van een zeer eenvoudige visualisatie (via scaolds). De volgende stap bestond er dan uit het grasch aantrekkelijk en intuïtief maken van de 76

93 interface. Dit was echter makkelijker gezegd dan gedaan. Het betreft hier een dynamische component, de maaltijd, die groeit en krimpt naargelang de handelingen van de gebruiker. De oplossing werd uiteindelijk gevonden met hulp van dr. Kris De Schutter, door gebruik te maken van vrij ingewikkelde CSS. Voor dit onderdeel te implementeren zijn toch enkele dagen verloren gegaan. De fout ligt hier evenwel niet bij Rails, maar bij onze onervarenheid met webdesign. Het is dan ook niet te verwonderen dat vele bedrijven een apart departement voor design hebben. Het uiteindelijke resultaat is te zien op guur 5.9. De percentagebalken bovenaan geven de nutriëntengrenzen voor de dag aan. Dit is ook een pure CSS oplossing, grotendeels gebaseerd op code van Georey Rosenbach 4, door ons omgezet naar een view-helper. Figuur 5.9: Screenshot: dynamisch groeiende maaltijd Een nieuwe generatie van het web: Web 2.0 Sinds enkele jaren is er een evolutie aan de gang in het weblandschap. Waar het oorspronkelijk ging om informatievoorziening, neigt het web tegenwoordig naar een platform voor functionaliteit. Naar analogie met de versienummering in de softwarewereld, noemt men deze transitie Web 2.0. Hoewel dit meer een buzzword of marketingterm is, zijn er toch duidelijke veranderingen aan de manier waarop het web gebruikt wordt. Typisch voor Web 2.0 is gebruikersinteractie. Het web evolueert steeds meer van een hulpmiddel voor informatiewinning naar een platform voor allerlei zaken, dat op vrij korte tijd goed ingeburgerd is geraakt in de maatschappij. Men kan tegenwoordig terecht spreken over web applicaties, in plaats van de traditionele web sites. Enkele typische voorbeelden van Web 2.0 zijn weblogs, wikis, RSS feeds, podcast, social bookmarking en nog veel meer. Geen van deze toepassingen kan geïncorporeerd worden in onze applicatie, daar het niet echt past in de geest en doel van de applicatie. Typisch echter voor webapplicaties die bestempeld worden als Web 2.0, zijn de interactieve en moderne gebruikersinterfaces. In de komende secties zullen we pogen om de interface Web 2.0 te maken. We zullen nagaan of Rails, die bij lancering de slogan Web 2.0 ready meekreeg, hiervoor voorzieningen aan boord heeft

94 5.4.4 Ajax Het traditionele model voor browser-server interactie is het request-reply paradigma. Dit betekent concreet dat na gebruikersinteractie de browser een request naar een webserver stuurt, eventueel gevolgd door verwerking aan serverzijde waarna een volledige HTML pagina wordt teruggestuurd. Dit betekent ook dat de pagina fysisch (en visueel) ververst wordt, wat allesbehalve modern aanvoelt. Begin 2005 beschreef Jesse James Garrett in een invloedrijke publicatie [6] een techniek benoemd Ajax, bedoeld om het ouderwetse web een grasche facelift te geven. Ajax staat voor Asynchronous JavaScript and XML en laat toe om webpagina's aan te passen zonder dat een hele pagina dient ververst te worden. De technologie werd reeds tien jaar geleden geïmplementeerd door Microsoft, maar kent pas sinds kort een ongeloofelijke populariteit bij webontwikkelaars. Het idee werd later ook overgenomen in Netscape en door het Mozilla Team, zodat men er van kan uitgaan dat Ajax functioneert op de meest courante systemen. Hoewel XML voorkomt in Ajax, is dit geen absolute vereiste en Rails kan dan ook een breed scala aan dataformaten via Ajax verwerken. De voordelen van Ajax zijn vrij logisch: Een webapplicatie met Ajax gedraagt zich meer zoals een klassieke desktop-applicatie, waar ook geen verversingen plaatsvinden. Typisch is de grootte van de data die verzonden wordt met Ajax kleiner dan een gehele webpagina. Hierdoor zal de applicatie ook sneller reageren omdat er minder data dient verwerkt te worden. Voor de gebruiker voelt de applicatie als veel sneller aan. Tegelijk wordt om zelfde reden ook minder bandbreedte verbruikt. Zoals op guur 5.10 te zien is, verschilt het gebruik van Ajax in feite niet zo veel met het traditionele web applicatie model. Concreet komt het er op neer dat men aan browserkant een JavaScript object (XmlHttpRequest) ter beschikking heeft waarmee men data in XML vorm kan uitwisselen met de webserver. Door middel van manipulatie van het DOM-object (Document Object Model) via JavaScript kan men het antwoord van de server inbrengen in de reeds gerenderede webpagina (via de Ajax engine op de guur). Het nadeel van Ajax of van JavaScript in het algemeen is dat het gebruik ervan repetitief en foutgevoelig is. Er ontstonden echter vrij snel tal van Ajax bibliotheken om de ontwikkeling met behulp van Ajax eenvoudiger te maken. Rails gebruikt intern het Prototype JavaScript Framework 5, en voorziet viewhelpers die toelaten om Ajax te gebruiken zonder zelf ooit één letter JavaScript te hoeven schrijven. Het is zelfs zo dat elke actie van elke controller gebruikt kan worden met een asynchrone oproep via Ajax. De vertaling achter de schermen is dan geheel voor de rekening van Rails. In feite is het gebruik van Ajax dus transparant vanuit het standpunt van de ontwikkelaar, het enige dat wijzigt zijn de methodenamen van de view-helpers. Beschouw als voorbeeld onderstaande code. In dit voorbeeld wordt een HTML-link aangemaakt die, wanneer er op geklikt wordt, het onderdeel op de website genaamd meals zal invullen met de output van de show_saved_meal actie. 1 t e x t = link_to_remote ( date. day, : u r l => { : a c t i o n => ' show_saved_meal ', : meal_date => date }, : update => ' meals ' ) 5 Codevoorbeeld 53: Ajax hyperlink 78

95 Figuur 5.10: Traditioneel model versus het Ajax model voor webapplicaties (uit [6]) Deze regel is afkomstig uit de viewcode voor de kalender die men kan gebruiken om de historie van maaltijden te raadplegen. Gesteld dat bijvoorbeeld date in bovenstaande code gelijk is aan 10 april, zal de view-helper link_to_remote onderstaande (JavaScript- en HTML)code genereren (date.day in de code zorgt ervoor dat de huidige dag getoond wordt, in dit geval dus 10). 1 <a h r e f="#" o n c l i c k="new Ajax. Updater ( ' meals ', '/ nephrology / food / show_saved_meal? meal_date = ', { asynchronous : true, e v a l S c r i p t s : true }) ; r eturn f a l s e ; ">10</a> Codevoorbeeld 54: Gegenereerde JavaScript Het mag duidelijk zijn dat dit al veel minder leesbaar en onderhoudbaar is dan de eerste regel. Merk op dat Rails hier op zijn beurt gebruik maakt van het prototype framework (Ajax.Updater ) en er dus eigenlijk nog veel meer JavaScript gegenereerd wordt achter de schermen. Merk tevens op dat, indien men geen gebruik wil maken van Ajax, hier gewoon link_to_remote dient te vervangen door link_to. Op geheel analoge manier kunnen remote formulieren of Ajax-observers (controleren een expressie en voeren een actie uit indien de expressie een bepaalde waarde oplevert) geïmplementeerd worden zonder noemenswaardige inspanning. We hebben dan ook gretig gebruik gemaakt van Ajax voor de view in deze iteratie, omdat de inspanningen minimaal zijn en het resultaat des te groter. Het is jammer dat we Ajax niet ontdekt hadden in de vorige iteratie, want dankzij Rails is er absoluut geen reden om de view te implementeren op de ouderwetse manier. Het resultaat is dan ook dat de dieet-module bijzonder snel en modern aanvoelt Visuele eecten Ajax op zich is al een serieuze verbetering om snelle en gebruiksvriendelijke GUI's te ontwikkelen, maar qua grasche eecten voegt Ajax niets toe aan HTML. Dankzij opnieuw JavaScript en het DOM, is het 79

96 mogelijk om visuele eecten te bekomen. Dit gaat van subtiele kleurikkeringen tot explosies van bepaalde delen van de pagina. Wederom zijn er tal van JavaScript bibliotheken beschikbaar die het gebruik van JavaScript tot een minimum herleiden. Net zoals voor Ajax gebruikt Rails intern een bibliotheek en biedt view-helpers aan om deze te gebruiken. De bibliotheek die Rails gebruikt, is de Script.aculo.us 6 JavaScript bibliotheek. Deze bibliotheek gebruikt intern ook het Prototype framework, waardoor visuele eecten in combinatie met Ajax kunnen gebruikt worden. Beschouw hiertoe onderstaand voorbeeld uit de zoekfunctie voor gebruikers. Deze helper genereert in eerste plaats een standaard HTML tekstveld. Wanneer men echter iets invult in dit veld zal met Ajax een oproep naar de server gebeuren, die de data in de databank met de input vergelijkt en de overeenkomstige data terugstuurt. De resultaten worden dan onder het tekstveld opengeklapt met Script.aculo.us die hier kleurschakeringen en doorzichtigheid aan toe voegt. 1 <%= text_field_with_auto_complete : user, : name %> Codevoorbeeld 55: Tekstvelddeclaratie met auto completion We zijn geen fan van al te veel visuele eecten, maar we zijn er van overtuigd dat subtiele eecten juist een meerwaarde kunnen zijn in het gebruik van de webapplicatie. In die zin hebben we vooral eecten gebruikt om bepaalde zaken te accentueren. Zo zal bij het toevoegen van een voedingsmiddel op guur 5.9 dit voedingsmiddel subtiel enkele keren ikkeren waardoor de gebruiker door heeft dat dit het nieuwe voedingsmiddel is. We hebben nog zulke eecten toegevoegd aan de applicatie, maar dit is uiteraard beter te zien in de applicatie zelf dan ze hier op te sommen. Beschouw onderstaande code ter illustratie. Het betreft hier een zoekfunctie van voedingsmiddelen via een eenvoudig tekstveld. Wanneer dit formulier met Ajax naar de server wordt gezonden (zie form_remote), zullen de resultaten worden weergegeven in een lijst (:update => 'quick_search_results ') die langzaam naar onder uitbreidt (visual_eect(:blinddown,...), wat aangenaam overkomt voor de gebruiker. Merk op dat dit stukje code zal vertaald worden naar een heleboel JavaScript die we nu niet meer zelf hoeven te schrijven. 1 <% form_remote_tag : u r l => { : a c t i o n => " quick_search " }, 2 : update => ' quick_search_results ', 3 : complete => v i s u a l _ e f f e c t ( : blinddown, ' quick_search_results ' ) do %> 4 <%= t e x t _ f i e l d : food, : name, : s i z e => 18 %> 5 <% end %> 6 <div id= ' quick_search_results '></div> Codevoorbeeld 56: Viewcode voor de (Ajax) zoekfunctionaliteit van voedingsmiddelen Hoewel Rails standaard reeds zeer veel voorzieningen aan boord heeft, zijn er ook vele enthousiastelingen die gebruik maken van het open-source karakter van Rails om eigen plugins te ontwikkelen. Plugins zijn eenvoudig te aan te maken, zolang (wederom) conventies gevolgd worden. Op die manier kunnen zelfs heuse patches voor Rails via een plugin aangebracht worden. Overdraagbaarheid is geen probleem, omdat de plugin fysiek een onderdeel is van de applicatie en als dusdanig mee overgedragen wordt met de applicatie. Eén van de plugins die we in de applicatie gebruikt hebben, is de redbox plugin geschreven

97 door Craig Ambrose 7 die een pop-up venster zoals op guur 5.11 mogelijk maakt. Doordat plugins meestal open-source zijn, hebben we de code eenvoudig kunnen aanpassen om in de eerste plaats van toepassing te zijn op onze noden maar tegelijk ook zo licht mogelijk te zijn. Figuur 5.11: Screenshot van de Redbox plugin in werking RJS Templates Zowel de visuele eecten als de Ajaxhelpers betreen één (DOM) element van de webpagina. Dit is een logische keuze, maar er bestaan zeker toepassingen die er baat zouden bij hebben dat verschillende elementen op dezelfde pagina tegelijk met andere inhoud worden aangepast. Sinds versie 1.1 bevat Rails daarom de mogelijkheid om naast RHTML en RXML ook RJS als antwoord naar de browser terug te sturen. RJS staat voor Ruby JavaScript en dat is meteen ook de beste denitie voor een RJS template. In plaats van de traditionele HTML naar de browser terug te sturen, wordt met RJS pure JavaScript code naar de browser gestuurd die onmiddelijk uitgevoerd wordt. Net zoals in vorige gevallen voorziet Rails wederom in een heleboel hulpmethoden die ervoor zorgen dat men nooit met JavaScript in contact hoeft te komen. Vandaar dat men in feite JavaScript schrijft, maar dan in Ruby. Dankzij RJS zijn er volgens ons bijna geen grenzen meer tussen de mogelijkheden van een desktop- en webapplicatie. Het is moeilijk om de kracht van RJS te beschrijven op papier, men dient dit met eigen ogen in werking te zien. Het is dan ook geen verrassing dat we meermaals in de implementatie gebruik hebben gemaakt van RJS. Als illustratie geven we hieronder een vrij uitgebreid voorbeeld (de meeste van onze RJS is 2 tot 3 regels) waar een consumptie wordt toegevoegd aan de maaltijd. Merk op dat deze code zowel kan opgenomen worden in controlleracties als in een apart.rjs bestand. In onderstaande code wordt de inhoud van het element rightcontent1 visueel verborgen via de Script.aculo.us shrink methode (regel één), gevolgd door het zichtbaar maken van rightcontent2 (regel twee). In de praktijk lijkt het alsof deze twee elementen worden verwisseld waarbij de eerste onder de andere schuift. In de volgende regels

98 worden verschillende elementen van de pagina ingevuld met verschillende inhouden (page.replace_html). Bovendien voeren we een visueel eect uit op de voedingsteller van de pagina (regel zes). Merk op dat RJS in feite gewoon een toepassing is van Ajax, waarbij een asynchrone request een JavaScript antwoord als gevolg heeft. Het is ook duidelijk dat RJS templates zeer krachtige toepassingen kan hebben, maar onderstaand voorbeeld (dat het meest complexe uit onze applicatie is) toont ook aan dat het schrijven van RJS bijna kinderspel is. 1 page. v i s u a l _ e f f e c t : shrink, ' r i g h t c o n t e n t 2 ', : duration => page. v i s u a l _ e f f e c t : grow, ' r i g h t c o n t e n t 1 ', : duration => food = Food. f i n d ( params [ : id ] ) 4 i f Consumption. c r e a t e ( : food => food, : quantity => quantity, : meal ). save 5 page. replace_html ' nr_of_consumptions ', : p a r t i a l => ' nr_of_consumptions ' 6 page. v i s u a l _ e f f e c t : pulsate, ' nr_of_consumptions ', : p u l s e s => 5 7 page. replace_html ' f l a s h ', "Added '#{ food. name } ' to meal with quantity = #{quantity }" 8 e l s e 9 page. replace_html ' f l a s h ', ' F a i l e d to add consumption to meal ' 10 end Codevoorbeeld 57: RJS template voor het toevoegen van een consumptie aan de maaltijd Testen & Code Coverage In deze iteraties hebben we ons vooral toegelegd op het ontwikkelen van views en bijhorende controllers. In sectie en zagen we reeds dat functionele tests en integratie tests uitermate hiervoor geschikt zijn. Men zou kunnen denken dat Ajax of RJS moeilijk te testen is door het asynchrone karakter ervan. De waarheid is echter verrassend, want er is geen wezenlijk onderscheid tussen de Ajax en niet-ajax acties in een controller, enkel de naam van de testmethoden wijzigen licht. Beschouw onderstaande testmethode als voorbeeld. Regels drie en acht zijn speciek voor Ajax en RJS. In regel drie gebruiken we xhr (Xml Http Request) in plaats van een gewone http get of post. Op regel acht wordt dan nagegaan of de RJS wel degelijk zijn werk heeft gedaan en het element van de pagina vervangen heeft. Het is duidelijk dat tests schrijven met of zonder Ajax in Rails dus amper verschil kent. 1 d e f test_ add_ quicksearched_ consumption 2 nr_of_consumptions = Consumption. count 3 xhr ( : post, : add_ quicksearched_ consumption, 4 : id id, 5 : consumption => { : quantity => ' 1234 ' }) 6 a s s e r t _ r e s p o n s e : s u c c e s s 7 assert_equal nr_of_consumptions + 1, Consumption. count 8 assert_select_rjs : replace_html, ' currentmeal ' 9 end Codevoorbeeld 58: Functionele test met Ajax-oproepen en RJS asserties Tijdens het schrijven van de tests voor deze iteratie zochten we naar een manier om na te gaan of alle code 82

99 ook daadwerkelijk getest is. Standaard bevat Rails geen voorzieningen om dit te bereiken. We hebben gekozen om de standaard code coverage tool van de Rubywereld, RCov, onder de loep te nemen. RCov is een ruby tool geschreven door Mauricio Fernandez 8, voor het bepalen van de code dekking van Ruby Unit Test zoals gebruikt voor Rails Unit tests. Gelukkig bleek dat sinds recent ook ondersteuning voor functionele- en integratietests is toegevoegd en dit alles beschikbaar is als plugin voor het Rails framework. RCov is zeker en vast een interessante tool om te gebruiken bij het testen. RCov geeft een visueel resultaat in HTML zoals te zien op guur Op elk van de links kan geklikt worden om te zien welke coderegels getest worden door onze tests en welke niet. Figuur 5.12: Resultaat van uitvoering RCov op de Unit Tests Code coverage is echter geen absoluut middel bij het schrijven van tests. Het geeft weliswaar een bepaalde indicatie, maar meer dan aangeven of alle code eens aangeraakt wordt in de tests doet het niet. Men weet nog altijd niet of alle mogelijkheden zijn nagegaan en alle randgevallen getest zijn. Of dit echter ooit mogelijk zal zijn, durven we ten zeerste betwijfelen. Maar het kan dus zeker geen kwaad om naast het gezond verstand ook code coverage tools te gebruiken bij het schrijven van tests Evaluatie De evalutie van deze iteratie verliep wederom in samenspraak met de klant, vertegenwoordigd door dr. Kris De Schutter. De visualisatie van de maaltijd werd goedgekeurd en het gebruik van Ajax werd als zeer positief ervaren. Het feit dat we onze tests konden bewijzen met RCov was zeker een pluspunt. Na een grondig nazicht van de applicatie, waren er toch enkele opmerkingen: 1. De klant verwachtte dat het klikken op de afbeelding van een voedingsmiddel betekent dat dit voedingsmiddel moet worden toegevoegd aan de maaltijd. In de huidige implementatie wordt echter informatie over de nutriënten van dat voedingsmiddel getoond. Dit moet aangepast worden

100 2. Er zat een bug in het berekenen van de leeftijd van een gebruiker. Blijkbaar werd een geboortedatum voor 1970 niet aanvaard. 3. Het zoeken van gebruikers gaf soms geen resultaten. 4. De klant had graag de mogelijkheid gehad om een top 10 van meest gekozen voedingsmiddelen te raadplegen. 5. De klant had graag gezien dat de patiënt de getoonde nutriënten (zie guur 5.9 op bladzijde 77) kan instellen. Het mag duidelijk zijn dat deze evaluatie zeer nuttig was voor ons. Het is duidelijk dat de fouten in punt 2 en 3 te vermijden waren met nog meer tests. Hoewel de code coverage van de testen voor de betreende zeer hoog is (+ 90%), blijken er nog ongeteste gevallen te bestaan. Hiermee wordt nogmaals aangetoond dat er geen kant en klaar recept voor het schrijven van tests bestaat. 84

101 5.5 Intermezzo: Rails & webservices Inleiding In de loop van de derde iteratie werd ons gevraagd om te onderzoeken of Rails mogelijkheden bood omtrent webservices. Webservices worden steeds belangrijker in het bedrijfslandschap en het is dus een mooie kans om na te gaan of Rails hiervoor klaar is. Zoals gezien in sectie 3.1, bevat Rails een component genaamd Action Web Service (AWS) voor dit doel. In de volgende secties bekijken we eerst kort het idee achter webservices, gevolgd door een bespreking van de implementatie gebruik makende van AWS Service-georiënteerde architecturen & Webservices De laatste jaren is het gebruik van service-geörienteerde architecturen (SOA) steeds populairder geworden. Zonder in detail te treden, is zo een architectuur gebaseerd op het gebruik van verspreide services (diensten) over een netwerk in plaats van een centralisatie van services. Dit betekent geen algehele omschakeling van architectuurmodellen, integendeel: SOA's zijn meer evolutie van bestaande architecturen dan een revolutie. De services kunnen in eender welke taal op eender welk platform geschreven worden, zolang de communicatie op een gestandaardiseerde manier verloopt. Webservices zijn zo een standaard, gedenieerd door het World Wide Web Consortium (W3C). De specicatie van webservices is modulair en omvat verscheidene protocollen en formaten. De twee meest gebruikte componenten zijn: SOAP (Simple Object Access Protocol): een XML-gebaseerd formaat voor de berichten die worden uitgewisseld tussen aanbieders en afnemers van services. SOAP kan gezien worden als een gestandaardiseerde enveloppe waarin een bericht met standaard regels voor opmaak zit. SOAP gebruikt voor het transport van de berichten meestal het populaire HTTP(S), vandaar de naam webservices. WSDL (Web Services Description Language): een XML-gebaseerde taal die toelaat om de interface van de services op een gestandaardiseerde manier te beschrijven. De interface zelf wordt ook de WSDL genoemd. Eenvoudig gezegd toont de WSDL van een applicate welke services naar buiten toe worden aangeboden en hoe deze moeten gebruikt worden. De derde component, UDDI (Universal Description Discovery and Integration), die vaak in één adem met de vorige twee wordt genoemd is een gestandaardiseerde manier om centrale repositories voor WSDLs aan te maken. Via deze repositories kunnen webservices gepubliceerd en ontdekt worden, zonder dat aanbieders of afnemers elkaars adres kennen. UDDI is echter nog niet doorgebroken op het moment van schrijven en wordt als dusdanig amper gebruikt. Webservices zijn stilaan uitgegroeid tot een vaste waarde in vele bedrijven. De best gekende voorbeelden zijn zonder twijfel de productzoekdienst van Amazon en de vele applicaties van Google die te benaderen zijn via een open webservice. 85

102 5.5.3 Aanbieden van webservices via Rails AWS ondersteunt zo goed als de gehele W3C specicatie voor SOAP en WSDL (en ook voor XML-RPC, een alternatief voor SOAP). Figuur 5.13 toont de werking van AWS, gezien vanuit het standpunt van de ontwikkelaar. Achter de schermen gebeuren een heleboel handelingen zoals XML- en SOAPparsing, maar dat is voor ons van geen belang. Merk op dat buiten AWS we maar één andere hoofdcomponent van Rails oproepen, namelijk ActiveRecord. De geïmplementeerde views en controllers zijn niet nodig, aangezien deze bedoeld zijn om de data op een scherm te visualiseren. Zoals echter op de guur 5.13 te zien worden wel nieuwe controllers gedenieerd, speciek voor de webservice. Figuur 5.13: Werking van AWS De eerste stap om een Rails-webservice aan te maken is na te gaan welke functionaliteit we naar de buitenwereld willen aanbieden. Idealiter zou dit via een eenvoudige declaratie moeten kunnen gebeuren in de betreende klassen, zoals gebruikelijk in Rails. De wereld buiten Ruby is echter overheersend statisch getypeerd, waaronder ook de SOAP standaard, wat onvermijdelijk conicteert met de ongetypeerde methodes van Ruby. AWS lost dit probleem op door een API denitie klasse te gebruiken. Een API kan het beste vergeleken worden met een Java of C# interface. Een API bevat de methode waarvoor parameter- en returntypes expliciet opgegeven worden, waardoor AWS de type conversie kan toepassen. Een stuk van de API van onze applicatie ziet er als volgt uit: 1 c l a s s WsApi < ActionWebService : : API : : Base 2 wsdl_service_name ' d i e t _ s e r v i c e ' 3 api_method : f i n d _ a l l _ c a t e g o r i e s, : r e t u r n s => [ [ Category ] ] 4 api_method : find_category, : e x p e c t s => [ : i n t ], : r e t u r n s => [ Category ] 5 end Codevoorbeeld 59: API voor de webservice Op basis van deze API denitie kan AWS automatisch een WSDL genereren. De bekomen WSDL voor deze eenvoudige code is al 80 regels lang en zodoende hebben we hem hier niet afgeprint. De aanzienlijke overhead van het gebruik van XML is een nadeel van de huidige webservices standaard, waarover later meer (sectie 5.5.5). Het punt is dat ontwikkelaars in feite nooit rechtstreeks in contact komen met WSDL of SOAP. Alles wordt geregeld op methode- en objectniveau. Of het nu gaat om het ontwikkelen van webservices of een normale applicatie, veel verschil is er dankzij AWS niet. 86

103 Uiteraard is een API denitie niet genoeg om een werkende service te verkijgen. De logica achter de API wordt op dezelfde manier geïmplementeerd als voorheen. Men denieert een controller die overerft van de AplicationController, maar AWS komt tussen wanneer er normaal een view zou gerendered moeten worden. Aangezien de business logica verweven is in de ActiveRecord klassen, is de taak van een AWS controller dus ook niets meer dan data ophalen en teruggeven. Zodoende zijn onze controllers zeer eenvoudig, zoals te zien in onderstaande code. Wat we schrijven is gewone Rails code zoals voorheen, waarbij AWS een enorme hoeveelheid achter de schermen uitvoert om normale code om te zetten naar de SOAP of WSDL standaard. 1 c l a s s WsController < A p p l i c a t i o n C o n t r o l l e r 2 web_ service_ api WsApi 3 web_service_scaffold : invoke 4 def f i n d _ a l l _ c a t e g o r i e s 5 r eturn Category. f i n d ( : a l l, : order => 'name ' ) 6 end 7 def find_category ( id ) 8 r eturn Category. f i n d ( id ) 9 end 10 end Codevoorbeeld 60: Controller voor de webservice Op de derde lijn is te zien dat ook voor webservices gebruik kan gemaakt worden van scaolds. Hier gaat het om een dynamische scaold die at runtime een heuse collectie controllers en views genereert om de webservices via een browser te kunnen uittesten. Tevens kunnen voor webservices dezelfde functionele testen geschreven worden als voor normale controllers. In onze applicatie hebben we de webservices functionaliteit beperkt gehouden tot een tiental functies, gezien het om een tussentijds onderzoek ging en niet de bedoeling was om webservices diep uit te werken. In de implementatie hebben we gebruik gemaakt van één API die rechtstreeks gehecht is aan aan één controller. In de praktijk zijn complexere mechanismen zoals layered en delegated dispatching beter geschikt om verschillende functionaliteiten te scheiden naar analogie met de verschillende controllers. Dit wordt zeer goed door Leon Breedt beschreven in [17]. Het mag echter uit bovenstaande tekst en code blijken dat het opzetten van een webservice met Rails ongelofelijk eenvoudig is en zeer weinig tijd vraagt. Zodra de logica beschreven is in een ActiveRecord, is het maar enkele minuten werk om de functionaliteit ook aan te bieden via een webservice Webservices gebruiken in Rails Uiteraard is het nodig dat niet enkel het aanbieden van webservices eenvoudig gaat, ook het gebruik van webservices in eigen code moet ondersteund worden. Onderstaande code maakt duidelijk dat het slechts een kwestie van één declaratie in de betreende controller is om dit te kunnen doen. Verder dienen amper aanpassingen gedaan te worden aan de controllercode, alles wordt wederom achter de schermen geparsed en geconverteerd door AWS. 87

104 1 c l a s s SomeController < A p p l i c a t i o n C o n t r o l l e r 2 web_ client_ api : Category, 3 : soap, http : / /my. u r l. com/ws/ api 4 5 def l i s t = Category. f i n d _ a l l _ c a t e g o r i e s 7 end 8 end Codevoorbeeld 61: Het gebruik van webservices in Rails REST Er zijn al een tijd geruchten dat de Rails ontwikkelaars plannen om ActionWebservice uit het framework te halen en verder door het leven te laten gaan als een plugin. Sinds de komst van Rails 1.2 heeft men het idee van Representational State Transfer (REST) naar voor geschoven als dé manier om SOA's te ontwerpen. REST is gebaseerd op een idee van Roy Fielding, één van de grondleggers van het HTTP, in zijn doctoraatsartikel [15]. We hebben bewust geen gebruik gemaakt van REST in onze applicatie, omdat op dit moment de techniek nog te jong is om te zien in welke richting het zal evolueren. REST is opgebouwd rondom het concept van een resource, die uniek geïdenticeerd is door een URL. Op deze URL kunnen de vier standaard HTTP opdrachten (GET, POST, PUT en DELETE) toegepast worden. Aangezien er geen andere mogelijkheden zijn dan de basisoperaties maakt Rails het mogelijk om de REST CRUD functionaliteit in de controller voor een ActiveRecord klasse te schrijven met één regel code. Gesteld dat we zulke declaratie zouden hebben geschreven voor het model Food, zijn volgende operaties op bijhorende URL mogelijk: GET /foods: geeft een collectie van alle Foods terug. POST /foods: maakt een nieuwe Food aan met de informatie in het HTTP bericht. GET /foods/123: geeft de Food met id gelijk aan 123 terug. PUT /foods/123: update de Food met id gelijk aan 123 met de informatie in het HTTP bericht. DELETE /foods/123: verwijdert de Food met id gelijk aan 123. De onmiddellijke voordelen van de REST aanpak zijn: Past perfect in de Rails architectuur. Waar AWS soms nog geforceerd overkomt (het gebruik van de API), is het alsof REST geboren is voor Rails. Zowel Rails als REST zijn URL-gebaseerd voor hun werking. Bovendien zorgt de automatische CRUD-generatie voor nog minder werk dan voorheen zonder één enkele aanpassing aan de applicatie. REST is vele malen lichter dan SOAP, omdat gewerkt wordt op URL basis en de complexe XMLparsing stap kan overgeslagen worden. Er kan gebruik gemaakt worden van een WSDL, maar dit is niet noodzakelijk. Vaak worden rechtstreekse URL's gebruikt in applicaties die de services gebruiken. REST is perfect cachebaar, omwille van de toestandloosheid van HTTP. 88

105 De keuze van Rails om voluit voor REST te gaan, lijkt ons riskant. Rails, en laat staan REST, zijn op dit moment onvoldoende doorgebroken om er reeds zulke ruchtbaarheid aan te geven. Er zijn reeds enkele frameworks in andere talen zoals Java en C# die REST ondersteunen, maar de industriestandaard is nu eenmaal webservices. Ons lijkt het beter te focussen op een bewezen en veelvuldig gebruikte standaard, om doorbraak van Rails te forceren in de bedrijfswereld. 89

106 5.6 Iteratie 4: Dokter functionaliteit Doelstelling Op dit moment rest ons nog één functionaliteit vooraleer we alle functionaliteit van de originele applicatie bereikt hebben. Meer bepaald moet het mogelijk zijn dat een dokter de applicatie kan gebruiken om de eetgewoonten van zijn patiënten op te volgen. Na een gesprek met de klant werd bovendien duidelijk dat enkel de correcte dokter de gegevens van een patiënt mag bekijken. Het is dus niet zo dat een dokter de gegevens van elke patiënt kan bekijken. Een dokter moet ook de nutriënten kunnen instellen die getoond worden aan de patiënt Implementatie De implementatie van de iteratiefunctionaliteit verliep volgens het klassieke stramien: eerst de modellen, daarna de controllers en de views. We bespreken hieronder de gevolgde stappen summier, omdat ze vrij standaard zijn en de gebruikte technieken reeds vroeger werden besproken. Het eerste probleem dat we dienden op te lossen is gebruikers op één of de andere wijze associëren met elkaar in een dokter-patiënt relatie. De eerste optie die dan onmiddelijk in de gedachten springt, is het gebruik van een jointabel met twee vreemde sleutels naar dezelfde tabel. Hoewel we alle mogelijke conventies volgden, leek Rails niet kunnen om te gaan met deze vorm van zelfreferentie, wat we ook probeerden. Ten slotte namen we genoegen met een minder exibele oplossing door Rails automatisch een boomstructuur van gebruikers te laten onderhouden, zoals reeds gebruikt voor de categorieën in iteratie één (zie sectie 5.2.2). Via een migratie wijzigden we dan op eenvoudige wijze de tabel voor gebruikers om nu ook een vreemde sleutel parent_id te bevatten. Dit betekent dat we per gebruiker één (andere) gebruiker kunnen instellen die de dokter van deze gebruiker is. Zo zullen we uiteindelijk een vrij brede boomstructuur bekomen. We pasten vervolgens de AdminController en bijhorende views aan om voor een gebruiker een dokter te kunnen deniëren en omgekeerd (met Ajax). Op modelniveau moet er verder niets gewijzigd worden. De informatie over de eetgewoonten zit reeds in de databank onder de vorm van gepersisteerde maaltijden en kunnen ook aangewend worden om statistieken hiervan te tonen aan een dokter. Voor de controllers en de views is het werk ook vrij beperkt. We deniëren een nieuwe controller, de DoctorController, om de functionaliteit te scheiden van de rest. We gebruiken eenzelfde opbouw van visuele componten als voor de maaltijden om de uniformiteit te bewaren. We passen de Ajax-techniek op analoge wijze toe zoals in de vorige iteratie (zie sectie 5.4.4). Uiteraard zit hier wat denk- en codeerwerk achter, maar niets dat nog niet is aangehaald in voorgaande besprekingen. Wederom schreven we voor alle nieuwe logica voldoende tests om ons van een correcte werking te vergewissen. Het uiteindelijke resultaat is in screenshotvorm te zien op guur De graek die te zien is op guur 5.14 wordt at runtime aangemaakt met de Gru Graphic Library 9 van Georey Grosenbach. Vermits Rails uiteindelijk gewoon Ruby code is, kunnen Ruby bibliotheken gewoon gebruikt worden door Rails klassen. Om de portabiliteit van onze applicatie te vergroten, hebben we deze bibliotheek ondergebracht in de map /lib die bij conventie tijdens het starten van Rails automatisch geladen wordt

107 Figuur 5.14: Screenshot van doktermodule (graek met calciumwaarden van een patiënt) Op geheel analoge wijze kan het gehele Rails framework ondergebracht worden in een map die bij conventie is vastgelegd. Op die manier zijn we zeker dat er geen incompatibele versie van het framework gebruikt wordt op het systeem waar we onze applicatie zullen installeren. Jammergenoeg is gebleken dat we onze applicatie niet 100% porteerbaar kunnen maken. Zo is steeds een werkende installatie van Ruby en ImageMagick (intern gebruikt door Gru) vereist Evaluatie Het resultaat van de iteratie werd positief ontvangen. De door Gru geproduceerde graeken werden mooi bevonden, maar de klant merkte terecht op dat de schaal van de tijdsas niet echt correct is. Helaas bleek na inspectie van de Gru API hier geen afdoend middel tegen te bestaan. Aangezien het zoeken naar een andere bibliotheek hoogstwaarschijnlijk veel werk zou betekenen werd onderling besloten om het resultaat zo te laten. Uiteindelijk is de graek perfect interpreteerbaar. De applicatie is op dit moment op functioneel vlak gelijk of zelfs meer functioneel dan de originele applicatie. Het doel van de case study is bereikt en in principe kan de ontwikkeling nu gestaakt worden. We wensen echter Rails werkelijk bloot te stellen aan de buitenwereld. Op het internet circuleren namelijk artikels (voornamelijk via blogs) waarin Rails qua performantie niet zo goed uit de verf komt. Na overleg met de klant werd dan ook besloten dat de resterende ontwikkelingstijd zou geschonken worden aan een optimalisatie iteratie. 91

Capita Selecta Design Patterns voor administratieve applicaties

Capita Selecta Design Patterns voor administratieve applicaties Capita Selecta voor administratieve applicaties Bij afstudeerproject: Generiek framework voor administratieve toepassingen in een webgeörienteerde omgeving Henk van de Ridder 26 augustus 2006 Inhoud 26

Nadere informatie

General info on using shopping carts with Ingenico epayments

General info on using shopping carts with Ingenico epayments Inhoudsopgave 1. Disclaimer 2. What is a PSPID? 3. What is an API user? How is it different from other users? 4. What is an operation code? And should I choose "Authorisation" or "Sale"? 5. What is an

Nadere informatie

Activant Prophet 21. Prophet 21 Version 12.0 Upgrade Information

Activant Prophet 21. Prophet 21 Version 12.0 Upgrade Information Activant Prophet 21 Prophet 21 Version 12.0 Upgrade Information This class is designed for Customers interested in upgrading to version 12.0 IT staff responsible for the managing of the Prophet 21 system

Nadere informatie

Software Processen. Ian Sommerville 2004 Software Engineering, 7th edition. Chapter 4 Slide 1. Het software proces

Software Processen. Ian Sommerville 2004 Software Engineering, 7th edition. Chapter 4 Slide 1. Het software proces Software Processen Ian Sommerville 2004 Software Engineering, 7th edition. Chapter 4 Slide 1 Het software proces Een gestructureerd set van activiteiten nodig om een software systeem te ontwikkelen Specificatie;

Nadere informatie

open standaard hypertext markup language internetprotocol transmission control protocol internet relay chat office open xml

open standaard hypertext markup language internetprotocol transmission control protocol internet relay chat office open xml DOWNLOAD OR READ : OPEN STANDAARD HYPERTEXT MARKUP LANGUAGE INTERNETPROTOCOL TRANSMISSION CONTROL PROTOCOL INTERNET RELAY CHAT OFFICE OPEN XML PDF EBOOK EPUB MOBI Page 1 Page 2 relay chat office open xml

Nadere informatie

Risico s van Technologisch Succes in digitale transformatie S T R A T E G I C A D V I S O R

Risico s van Technologisch Succes in digitale transformatie S T R A T E G I C A D V I S O R Risico s van Technologisch Succes in digitale transformatie 2e Risk Event 2019 11 april 2019 The S T R A T E G I C A D V I S O R Ymanagement school of the autonomous University of Antwerp 2 Prof. dr. Hans

Nadere informatie

MyDHL+ Van Non-Corporate naar Corporate

MyDHL+ Van Non-Corporate naar Corporate MyDHL+ Van Non-Corporate naar Corporate Van Non-Corporate naar Corporate In MyDHL+ is het mogelijk om meerdere gebruikers aan uw set-up toe te voegen. Wanneer er bijvoorbeeld meerdere collega s van dezelfde

Nadere informatie

Model Driven Software Development: Geen toekomst maar realiteit. 4 juni 2009, WTC, Amsterdam.

Model Driven Software Development: Geen toekomst maar realiteit. 4 juni 2009, WTC, Amsterdam. Model Driven Software Development: Geen toekomst maar realiteit. 4 juni 2009, WTC, Amsterdam. Welke hoort in dit rijtje niet thuis? Weg- en waterbouw Huizen- en kantoorbouw Stedenbouw Auto- en vliegtuigbouw

Nadere informatie

Is APEX a worthy substitute for Oracle Forms?

Is APEX a worthy substitute for Oracle Forms? your oracle solu+ons partner Is APEX a worthy substitute for Oracle Forms? APEX for mission critical applications: the Groupm business-case By Ronny Boeykens & Stijn Van Raes iadvise o Opgericht in 2004

Nadere informatie

Model driven Application Delivery

Model driven Application Delivery Model driven Application Delivery Fast. Flexible. Future-proof. How Agis streamlines health procurement using Mendix Model driven Application Platform Mendix in a nutshell Mendix delivers the tools and

Nadere informatie

Risk & Requirements Based Testing

Risk & Requirements Based Testing Risk & Requirements Based Testing Tycho Schmidt PreSales Consultant, HP 2006 Hewlett-Packard Development Company, L.P. The information contained herein is subject to change without notice Agenda Introductie

Nadere informatie

Introductie in flowcharts

Introductie in flowcharts Introductie in flowcharts Flow Charts Een flow chart kan gebruikt worden om: Processen definieren en analyseren. Een beeld vormen van een proces voor analyse, discussie of communicatie. Het definieren,

Nadere informatie

Gebruik van cryptografie voor veilige jquery/rest webapplicaties. Frans van Buul Inter Access

Gebruik van cryptografie voor veilige jquery/rest webapplicaties. Frans van Buul Inter Access Gebruik van cryptografie voor veilige jquery/rest webapplicaties Frans van Buul Inter Access 1 Frans van Buul frans.van.buul@interaccess.nl 2 De Uitdaging Rijke en veilige webapplicaties Een onveilig en

Nadere informatie

Continuous testing in DevOps met Test Automation

Continuous testing in DevOps met Test Automation Continuous ing in met Continuous testing in met Marco Jansen van Doorn Tool Consultant 1 is a software development method that emphasizes communication, collaboration, integration, automation, and measurement

Nadere informatie

SAMPLE 11 = + 11 = + + Exploring Combinations of Ten + + = = + + = + = = + = = 11. Step Up. Step Ahead

SAMPLE 11 = + 11 = + + Exploring Combinations of Ten + + = = + + = + = = + = = 11. Step Up. Step Ahead 7.1 Exploring Combinations of Ten Look at these cubes. 2. Color some of the cubes to make three parts. Then write a matching sentence. 10 What addition sentence matches the picture? How else could you

Nadere informatie

Software Test Plan. Yannick Verschueren

Software Test Plan. Yannick Verschueren Software Test Plan Yannick Verschueren November 2014 Document geschiedenis Versie Datum Auteur/co-auteur Beschrijving 1 November 2014 Yannick Verschueren Eerste versie 1 Inhoudstafel 1 Introductie 3 1.1

Nadere informatie

ANGSTSTOORNISSEN EN HYPOCHONDRIE: DIAGNOSTIEK EN BEHANDELING (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM

ANGSTSTOORNISSEN EN HYPOCHONDRIE: DIAGNOSTIEK EN BEHANDELING (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM Read Online and Download Ebook ANGSTSTOORNISSEN EN HYPOCHONDRIE: DIAGNOSTIEK EN BEHANDELING (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM DOWNLOAD EBOOK : ANGSTSTOORNISSEN EN HYPOCHONDRIE: DIAGNOSTIEK STAFLEU

Nadere informatie

ETS 4.1 Beveiliging & ETS app concept

ETS 4.1 Beveiliging & ETS app concept ETS 4.1 Beveiliging & ETS app concept 7 juni 2012 KNX Professionals bijeenkomst Nieuwegein Annemieke van Dorland KNX trainingscentrum ABB Ede (in collaboration with KNX Association) 12/06/12 Folie 1 ETS

Nadere informatie

De grondbeginselen der Nederlandsche spelling / Regeling der spelling voor het woordenboek der Nederlandsche taal (Dutch Edition)

De grondbeginselen der Nederlandsche spelling / Regeling der spelling voor het woordenboek der Nederlandsche taal (Dutch Edition) De grondbeginselen der Nederlandsche spelling / Regeling der spelling voor het woordenboek der Nederlandsche taal (Dutch Edition) L. A. te Winkel Click here if your download doesn"t start automatically

Nadere informatie

FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE. Toets Inleiding Kansrekening 1 8 februari 2010

FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE. Toets Inleiding Kansrekening 1 8 februari 2010 FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE Toets Inleiding Kansrekening 1 8 februari 2010 Voeg aan het antwoord van een opgave altijd het bewijs, de berekening of de argumentatie toe. Als je een onderdeel

Nadere informatie

Appendix A: List of variables with corresponding questionnaire items (in English) used in chapter 2

Appendix A: List of variables with corresponding questionnaire items (in English) used in chapter 2 167 Appendix A: List of variables with corresponding questionnaire items (in English) used in chapter 2 Task clarity 1. I understand exactly what the task is 2. I understand exactly what is required of

Nadere informatie

MyDHL+ ProView activeren in MyDHL+

MyDHL+ ProView activeren in MyDHL+ MyDHL+ ProView activeren in MyDHL+ ProView activeren in MyDHL+ In MyDHL+ is het mogelijk om van uw zendingen, die op uw accountnummer zijn aangemaakt, de status te zien. Daarnaast is het ook mogelijk om

Nadere informatie

Taco Schallenberg Acorel

Taco Schallenberg Acorel Taco Schallenberg Acorel Inhoudsopgave Introductie Kies een Platform Get to Know the Jargon Strategie Bedrijfsproces Concurrenten User Experience Marketing Over Acorel Introductie THE JARGON THE JARGON

Nadere informatie

Stephanie van Dijck De integrale aanpak maakt complexiteit hanteerbaar

Stephanie van Dijck De integrale aanpak maakt complexiteit hanteerbaar Titel, samenvatting en biografie Stephanie van Dijck De integrale aanpak maakt complexiteit hanteerbaar Samenvatting: Nieuwe projecten nemen toe in complexiteit: afhankelijkheden tussen software componenten,

Nadere informatie

Settings for the C100BRS4 MAC Address Spoofing with cable Internet.

Settings for the C100BRS4 MAC Address Spoofing with cable Internet. Settings for the C100BRS4 MAC Address Spoofing with cable Internet. General: Please use the latest firmware for the router. The firmware is available on http://www.conceptronic.net! Use Firmware version

Nadere informatie

Object Oriented Programming

Object Oriented Programming Object Oriented Programming voor webapplicaties Door Edwin Vlieg Waarom OOP? Basis uitleg over OOP Design Patterns ActiveRecord Model View Controller Extra informatie Vragen OOP Object Oriented Programming

Nadere informatie

CTI SUITE TSP DETAILS

CTI SUITE TSP DETAILS CTI SUITE TSP DETAILS TAPI allows an application to access telephony services provided by a telecom PABX. In order to implement its access to ETRADEAL, a TAPI interface has been developed by Etrali. As

Nadere informatie

Sparse columns in SQL server 2008

Sparse columns in SQL server 2008 Sparse columns in SQL server 2008 Object persistentie eenvoudig gemaakt Bert Dingemans, e-mail : info@dla-os.nl www : http:// 1 Content SPARSE COLUMNS IN SQL SERVER 2008... 1 OBJECT PERSISTENTIE EENVOUDIG

Nadere informatie

Opdrachtformulering (pagina 3 van 7)

Opdrachtformulering (pagina 3 van 7) Afstudeerovereenkomst van Tim Wils Bijlage 1 Opdrachtformulering (pagina 3 van 7) Dit project betreft een eigen framework (soort API) waarmee relatief gemakkelijk en in korte tijd eindproducten opgezet

Nadere informatie

S e v e n P h o t o s f o r O A S E. K r i j n d e K o n i n g

S e v e n P h o t o s f o r O A S E. K r i j n d e K o n i n g S e v e n P h o t o s f o r O A S E K r i j n d e K o n i n g Even with the most fundamental of truths, we can have big questions. And especially truths that at first sight are concrete, tangible and proven

Nadere informatie

VALUE ENGINEERING: THE H E G A G ME! E

VALUE ENGINEERING: THE H E G A G ME! E VALUE ENGINEERING: THE GAME! Involvement Process for Technical Projects Feedback/Learning/Knowledge Management Involvem ment Business Process Engineering Estimating Project Director Detailed Engineering

Nadere informatie

Next Generation Poultry Health Redt Innovatie de Vleeskuikenhouder?

Next Generation Poultry Health Redt Innovatie de Vleeskuikenhouder? Next Generation Poultry Health Redt Innovatie de Vleeskuikenhouder? Paul Louis Iske Professor Open Innovation & Business Venturing, Maastricht University De wereld wordt steeds complexer Dit vraagt om

Nadere informatie

Tim Akkerman - Head of Mobile

Tim Akkerman - Head of Mobile Tim Akkerman - Head of Mobile Emesa is the largest e-commerce company for searching, comparing and booking travel and leisure packages in the following categories: Holidays - Other accommodations - Hotels

Nadere informatie

Zelftest Informatica-terminologie

Zelftest Informatica-terminologie Zelftest Informatica-terminologie Document: n0947test.fm 01/07/2015 ABIS Training & Consulting P.O. Box 220 B-3000 Leuven Belgium TRAINING & CONSULTING INTRODUCTIE Deze test is een zelf-test, waarmee u

Nadere informatie

Data Handling Ron van Lammeren - Wageningen UR

Data Handling Ron van Lammeren - Wageningen UR Data Handling 1 2010-2011 Ron van Lammeren - Wageningen UR Can I answer my scientific questions? Geo-data cycle Data handling / introduction classes of data handling data action models (ISAC) Queries (data

Nadere informatie

(1) De hoofdfunctie van ons gezelschap is het aanbieden van onderwijs. (2) Ons gezelschap is er om kunsteducatie te verbeteren

(1) De hoofdfunctie van ons gezelschap is het aanbieden van onderwijs. (2) Ons gezelschap is er om kunsteducatie te verbeteren (1) De hoofdfunctie van ons gezelschap is het aanbieden van onderwijs (2) Ons gezelschap is er om kunsteducatie te verbeteren (3) Ons gezelschap helpt gemeenschappen te vormen en te binden (4) De producties

Nadere informatie

Cambridge Assessment International Education Cambridge International General Certificate of Secondary Education. Published

Cambridge Assessment International Education Cambridge International General Certificate of Secondary Education. Published Cambridge Assessment International Education Cambridge International General Certificate of Secondary Education DUTCH 055/02 Paper 2 Reading MARK SCHEME Maximum Mark: 45 Published This mark scheme is published

Nadere informatie

Ervaringen met begeleiding FTA cursus Deployment of Free Software Systems

Ervaringen met begeleiding FTA cursus Deployment of Free Software Systems Ervaringen met begeleiding FTA cursus Deployment of Free Software Systems Frans Mofers Nederland cursusmateriaal & CAA's alle cursusmateriaal vrij downloadbaar als PDF betalen voor volgen cursus cursussite

Nadere informatie

Invloed van het aantal kinderen op de seksdrive en relatievoorkeur

Invloed van het aantal kinderen op de seksdrive en relatievoorkeur Invloed van het aantal kinderen op de seksdrive en relatievoorkeur M. Zander MSc. Eerste begeleider: Tweede begeleider: dr. W. Waterink drs. J. Eshuis Oktober 2014 Faculteit Psychologie en Onderwijswetenschappen

Nadere informatie

HANDBOEK HARTFALEN (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM

HANDBOEK HARTFALEN (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM HANDBOEK HARTFALEN (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM READ ONLINE AND DOWNLOAD EBOOK : HANDBOEK HARTFALEN (DUTCH EDITION) FROM BOHN Click button to download this ebook READ ONLINE AND DOWNLOAD

Nadere informatie

Dit voorbeeldproject beschrijft het gebruik van web services (open standaarden) voor de ontsluiting van kernregistraties bij de gemeente Den Haag.

Dit voorbeeldproject beschrijft het gebruik van web services (open standaarden) voor de ontsluiting van kernregistraties bij de gemeente Den Haag. Voorbeeldproject Een Haagse SOA Dit voorbeeldproject beschrijft het gebruik van web services (open standaarden) voor de ontsluiting van kernregistraties bij de gemeente Den Haag. Aanleiding Vanuit de visie

Nadere informatie

Continuous Delivery. Sander Aernouts

Continuous Delivery. Sander Aernouts Continuous Delivery Sander Aernouts Info Support in een notendop Maatwerk softwareontwikkeling van bedrijfskritische kantoorapplicaties Business Intelligence oplossingen Managed IT Services Eigen Kenniscentrum

Nadere informatie

Het beheren van mijn Tungsten Network Portal account NL 1 Manage my Tungsten Network Portal account EN 14

Het beheren van mijn Tungsten Network Portal account NL 1 Manage my Tungsten Network Portal account EN 14 QUICK GUIDE C Het beheren van mijn Tungsten Network Portal account NL 1 Manage my Tungsten Network Portal account EN 14 Version 0.9 (June 2014) Per May 2014 OB10 has changed its name to Tungsten Network

Nadere informatie

Software Test Plan. Yannick Verschueren

Software Test Plan. Yannick Verschueren Software Test Plan Yannick Verschueren Maart 2015 Document geschiedenis Versie Datum Auteur/co-auteur Beschrijving 1 November 2014 Yannick Verschueren Eerste versie 2 December 2014 Yannick Verschueren

Nadere informatie

End-to-End testen: de laatste horde

End-to-End testen: de laatste horde End-to-End testen: de laatste horde Dieter Arnouts Agenda Begrip End-to-End testen in het test proces Praktische aanpak End-to-End Test Omgeving Uitdagingen End-to-End testen: De laatste horde 11/10/2010

Nadere informatie

COGNITIEVE DISSONANTIE EN ROKERS COGNITIVE DISSONANCE AND SMOKERS

COGNITIEVE DISSONANTIE EN ROKERS COGNITIVE DISSONANCE AND SMOKERS COGNITIEVE DISSONANTIE EN ROKERS Gezondheidsgedrag als compensatie voor de schadelijke gevolgen van roken COGNITIVE DISSONANCE AND SMOKERS Health behaviour as compensation for the harmful effects of smoking

Nadere informatie

Innovaties in de chronische ziekenzorg 3e voorbeeld van zorginnovatie. Dr. J.J.W. (Hanneke) Molema, Prof. Dr. H.J.M.

Innovaties in de chronische ziekenzorg 3e voorbeeld van zorginnovatie. Dr. J.J.W. (Hanneke) Molema, Prof. Dr. H.J.M. Innovaties in de chronische ziekenzorg 3e voorbeeld van zorginnovatie Dr. J.J.W. (Hanneke) Molema, Prof. Dr. H.J.M. (Bert) Vrijhoef Take home messages: Voor toekomstbestendige chronische zorg zijn innovaties

Nadere informatie

Identity & Access Management & Cloud Computing

Identity & Access Management & Cloud Computing Identity & Access Management & Cloud Computing Emanuël van der Hulst Edwin Sturrus KPMG IT Advisory 11 juni 2015 Cloud Architect Alliance Introductie Emanuël van der Hulst RE CRISC KPMG IT Advisory Information

Nadere informatie

icafe Project Joeri Verdeyen Stefaan De Spiegeleer Ben Naim Tanfous

icafe Project Joeri Verdeyen Stefaan De Spiegeleer Ben Naim Tanfous icafe Project Joeri Verdeyen Stefaan De Spiegeleer Ben Naim Tanfous 2006-2007 Inhoudsopgave 1 2 1.1 Programmeertaal PHP5..................... 2 1.2 MySQL database......................... 3 1.3 Adobe Flash...........................

Nadere informatie

Enterprisearchitectuur

Enterprisearchitectuur Les 2 Enterprisearchitectuur Enterprisearchitectuur ITarchitectuur Servicegeoriënteerde architectuur Conceptuele basis Organisatiebrede scope Gericht op strategie en communicatie Individuele systeemscope

Nadere informatie

is front-end kennis relevant voor een UX designer

is front-end kennis relevant voor een UX designer In hoeverre is front-end kennis relevant voor een UX designer tijdens een designproces? Door: Wessel Grift Onderzoeksvraag In hoeverre is het hebben van front-end development kennis relevant voor een

Nadere informatie

PRIVACYVERKLARING KLANT- EN LEVERANCIERSADMINISTRATIE

PRIVACYVERKLARING KLANT- EN LEVERANCIERSADMINISTRATIE For the privacy statement in English, please scroll down to page 4. PRIVACYVERKLARING KLANT- EN LEVERANCIERSADMINISTRATIE Verzamelen en gebruiken van persoonsgegevens van klanten, leveranciers en andere

Nadere informatie

Generiek framework voor administratieve toepassingen in een webgeörienteerde omgeving

Generiek framework voor administratieve toepassingen in een webgeörienteerde omgeving Generiek framework voor administratieve toepassingen in een webgeörienteerde omgeving Henk van de Ridder Stand van zaken 17 Maart 2007 Inhoud Probleemgebied afstudeerproject Oplossingsgebied afstudeerproject

Nadere informatie

Impact en disseminatie. Saskia Verhagen Franka vd Wijdeven

Impact en disseminatie. Saskia Verhagen Franka vd Wijdeven Impact en disseminatie Saskia Verhagen Franka vd Wijdeven Wie is wie? Voorstel rondje Wat hoop je te leren? Heb je iets te delen? Wat zegt de Programma Gids? WHAT DO IMPACT AND SUSTAINABILITY MEAN? Impact

Nadere informatie

Y.S. Lubbers en W. Witvoet

Y.S. Lubbers en W. Witvoet WEBDESIGN Eigen Site Evaluatie door: Y.S. Lubbers en W. Witvoet 1 Summary Summary Prefix 1. Content en structuur gescheiden houden 2. Grammaticaal correcte en beschrijvende markup 3. Kopregels 4. Client-

Nadere informatie

Four-card problem. Input

Four-card problem. Input Four-card problem The four-card problem (also known as the Wason selection task) is a logic puzzle devised by Peter Cathcart Wason in 1966. It is one of the most famous tasks in the study of deductive

Nadere informatie

Travel Survey Questionnaires

Travel Survey Questionnaires Travel Survey Questionnaires Prot of Rotterdam and TU Delft, 16 June, 2009 Introduction To improve the accessibility to the Rotterdam Port and the efficiency of the public transport systems at the Rotterdam

Nadere informatie

FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE

FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE Tentamen Analyse 6 januari 203, duur 3 uur. Voeg aan het antwoord van een opgave altijd het bewijs, de berekening of de argumentatie toe. Als je een onderdeel

Nadere informatie

The first line of the input contains an integer $t \in \mathbb{n}$. This is followed by $t$ lines of text. This text consists of:

The first line of the input contains an integer $t \in \mathbb{n}$. This is followed by $t$ lines of text. This text consists of: Document properties Most word processors show some properties of the text in a document, such as the number of words or the number of letters in that document. Write a program that can determine some of

Nadere informatie

GETTING THE BEST OUT OF YOUR SOURCE CODE MODERNISEREN MET UNIFACE

GETTING THE BEST OUT OF YOUR SOURCE CODE MODERNISEREN MET UNIFACE GETTING THE BEST OUT OF YOUR SOURCE CODE MODERNISEREN MET UNIFACE 2 OMNEXT IN HET KORT Broncode als bron van informatie Gevestigd in NL, UK en USA Kennis van meer dan 40 diverse technologieën Verschillende

Nadere informatie

Wat is Interaction Design?

Wat is Interaction Design? Wat is Interaction Design? Wat is interaction design? Designing interactive products to support the way people communicate and interact in their everyday and working lives. Preece, Sharp and Rogers (2015)

Nadere informatie

Engels op Niveau A2 Workshops Woordkennis 1

Engels op Niveau A2 Workshops Woordkennis 1 A2 Workshops Woordkennis 1 A2 Workshops Woordkennis 1 A2 Woordkennis 1 Bestuderen Hoe leer je 2000 woorden? Als je een nieuwe taal wilt spreken en schrijven, heb je vooral veel nieuwe woorden nodig. Je

Nadere informatie

De Invloed van Religieuze Coping op. Internaliserend Probleemgedrag bij Genderdysforie. Religious Coping, Internal Problems and Gender dysphoria

De Invloed van Religieuze Coping op. Internaliserend Probleemgedrag bij Genderdysforie. Religious Coping, Internal Problems and Gender dysphoria De Invloed van Religieuze Coping op Internaliserend Probleemgedrag bij Genderdysforie Religious Coping, Internal Problems and Gender dysphoria Ria de Bruin van der Knaap Open Universiteit Naam student:

Nadere informatie

z x 1 x 2 x 3 x 4 s 1 s 2 s 3 rij rij rij rij

z x 1 x 2 x 3 x 4 s 1 s 2 s 3 rij rij rij rij ENGLISH VERSION SEE PAGE 3 Tentamen Lineaire Optimalisering, 0 januari 0, tijdsduur 3 uur. Het gebruik van een eenvoudige rekenmachine is toegestaan. Geef bij elk antwoord een duidelijke toelichting. Als

Nadere informatie

RECEPTEERKUNDE: PRODUCTZORG EN BEREIDING VAN GENEESMIDDELEN (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM

RECEPTEERKUNDE: PRODUCTZORG EN BEREIDING VAN GENEESMIDDELEN (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM Read Online and Download Ebook RECEPTEERKUNDE: PRODUCTZORG EN BEREIDING VAN GENEESMIDDELEN (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM DOWNLOAD EBOOK : RECEPTEERKUNDE: PRODUCTZORG EN BEREIDING VAN STAFLEU

Nadere informatie

2010 Integrated reporting

2010 Integrated reporting 2010 Integrated reporting Source: Discussion Paper, IIRC, September 2011 1 20/80 2 Source: The International framework, IIRC, December 2013 3 Integrated reporting in eight questions Organizational

Nadere informatie

My Benefits My Choice applicatie. Registratie & inlogprocedure

My Benefits My Choice applicatie. Registratie & inlogprocedure My Benefits My Choice applicatie Registratie & inlogprocedure Welkom bij de My Benefits My Choice applicatie Gezien de applicatie gebruik maakt van uw persoonlijke gegevens en salarisinformatie wordt de

Nadere informatie

Open Onderwijs API. De open standaard voor het delen van onderwijs data. 23 juni 2016 Frans Ward - SURFnet Architectuurraad - Utrecht

Open Onderwijs API. De open standaard voor het delen van onderwijs data. 23 juni 2016 Frans Ward - SURFnet Architectuurraad - Utrecht Open Onderwijs API De open standaard voor het delen van onderwijs data https://www.flickr.com/photos/statefarm/19349203414 23 juni 2016 Frans Ward - SURFnet Architectuurraad - Utrecht Missie Onderwijs

Nadere informatie

OPEN TRAINING. Onderhandelingen met leveranciers voor aankopers. Zeker stellen dat je goed voorbereid aan de onderhandelingstafel komt.

OPEN TRAINING. Onderhandelingen met leveranciers voor aankopers. Zeker stellen dat je goed voorbereid aan de onderhandelingstafel komt. OPEN TRAINING Onderhandelingen met leveranciers voor aankopers Zeker stellen dat je goed voorbereid aan de onderhandelingstafel komt. Philip Meyers Making sure to come well prepared at the negotiation

Nadere informatie

Technisch ontwerp. Projectteam 6. Project "Web Essentials" 02 april 2009. Versie 2.1.0

Technisch ontwerp. Projectteam 6. Project Web Essentials 02 april 2009. Versie 2.1.0 Projectteam 6 Faculteit Natuur en Techniek Hogeschool Utrecht Projectleider: Hans Allis, hans.allis@student.hu.nl Technisch ontwerp Project "Web Essentials" 02 april 2009 Versie 2.1.0 Teamleden: Armin

Nadere informatie

Wat heeft een tester aan ASL en BiSL?

Wat heeft een tester aan ASL en BiSL? TestNet Noord, Heerenveen, 20 november 2012 Wat heeft een tester aan ASL en BiSL? Eibert Dijkgraaf Intro Wie zit er in een typische beheer omgeving? Wat is kenmerkend voor testen : IN BEHEER? IN ONDERHOUD?

Nadere informatie

Installatie instructies

Installatie instructies OpenIMS CE Versie 4.2 Installatie instructies OpenSesame ICT BV Inhoudsopgave 1 INLEIDING... 3 2 INSTALLATIE INSTRUCTIES... 4 3 OPENIMS SITECOLLECTIE CONFIGURATIE... 6 OpenIMS CE Installatie instructies

Nadere informatie

Kikkers en Heilige Koeien UvAConext & standaarden voor het primaire onderwijs en onderzoek proces

Kikkers en Heilige Koeien UvAConext & standaarden voor het primaire onderwijs en onderzoek proces Kikkers en Heilige Koeien UvAConext & standaarden voor het primaire onderwijs en onderzoek proces SURF Seminar September 2015 Frank Benneker, ICTS Universiteit van Amsterdam Perspectief ICTS & OO dienstverlening

Nadere informatie

Intermax backup exclusion files

Intermax backup exclusion files Intermax backup exclusion files Document type: Referentienummer: Versienummer : Documentatie 1.0 Datum publicatie: Datum laatste wijziging: Auteur: 24-2-2011 24-2-2011 Anton van der Linden Onderwerp: Documentclassificatie:

Nadere informatie

Satellite Orbit Determination with the Global Educational Network for Satellite Operations

Satellite Orbit Determination with the Global Educational Network for Satellite Operations Satellite Orbit Determination with the Global Educational Network for Satellite Operations Het project in het kort en de opgedane ervaringen Open Universiteit Nederland Faculteit Computer Science TouW

Nadere informatie

Klanten en Leveranciers moeten samen groeien voor en succesvol toekomst

Klanten en Leveranciers moeten samen groeien voor en succesvol toekomst Evolving Together Klanten en Leveranciers moeten samen groeien voor en succesvol toekomst Optitrade Van inkooporganisatie naar SAAS provider VAN INKOOPORGANISATIE NAAR SAAS PROVIDER Optitrade Retailgroep

Nadere informatie

Congres Social Media, Stichting Corporate Communicatie

Congres Social Media, Stichting Corporate Communicatie Dexia & social media Frank Van ssche, Head of Brand & Project Office, Communicatie Gent, 28/04/2011 Congres Social Media, Stichting Corporate Communicatie Quotes Social media isn't (just) about the media,

Nadere informatie

Best Practice Seminar 14 NOVEMBER 2013

Best Practice Seminar 14 NOVEMBER 2013 Best Practice Seminar 14 NOVEMBER 2013 14.00: Welkom Best Practice Seminar 14.10: Centraal PMO als middelpunt van projecten en programma s Yvonne Veenma, Stedin 14.50: Pauze 15.30: Governance in een Enterprise

Nadere informatie

Geslacht, Emotionele Ontrouw en Seksdrive. Gender, Emotional Infidelity and Sex Drive

Geslacht, Emotionele Ontrouw en Seksdrive. Gender, Emotional Infidelity and Sex Drive 1 Geslacht, Emotionele Ontrouw en Seksdrive Gender, Emotional Infidelity and Sex Drive Femke Boom Open Universiteit Naam student: Femke Boom Studentnummer: 850762029 Cursusnaam: Empirisch afstudeeronderzoek:

Nadere informatie

FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE

FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE Tentamen Bewijzen en Technieken 1 7 januari 211, duur 3 uur. Voeg aan het antwoord van een opgave altijd het bewijs, de berekening of de argumentatie toe.

Nadere informatie

i ll take off to the cloud

i ll take off to the cloud i ll take off to the cloud Webbased applicaties gebouwd door ILE programmeurs Gepresenteerd door: Drs. Martijn van Breden Lead software architect Pantheon Automatisering 26-4-2017 1 Historie Pantheon Ontstaan

Nadere informatie

Enterprise Portfolio Management

Enterprise Portfolio Management Enterprise Portfolio Management Strategische besluitvorming vanuit integraal overzicht op alle portfolio s 22 Mei 2014 Jan-Willem Boere Vind goud in uw organisatie met Enterprise Portfolio Management 2

Nadere informatie

Published in: Onderwijs Research Dagen 2013 (ORD2013), mei 2013, Brussel, Belgie

Published in: Onderwijs Research Dagen 2013 (ORD2013), mei 2013, Brussel, Belgie Samenwerkend leren van leerkrachten : leeropbrengsten gerelateerd aan activiteiten en foci van samenwerking Doppenberg, J.J.; den Brok, P.J.; Bakx, A.W.E.A. Published in: Onderwijs Research Dagen 2013

Nadere informatie

LONDEN MET 21 GEVARIEERDE STADSWANDELINGEN 480 PAGINAS WAARDEVOLE INFORMATIE RUIM 300 FOTOS KAARTEN EN PLATTEGRONDEN

LONDEN MET 21 GEVARIEERDE STADSWANDELINGEN 480 PAGINAS WAARDEVOLE INFORMATIE RUIM 300 FOTOS KAARTEN EN PLATTEGRONDEN LONDEN MET 21 GEVARIEERDE STADSWANDELINGEN 480 PAGINAS WAARDEVOLE INFORMATIE RUIM 300 FOTOS KAARTEN EN PLATTEGRONDEN LM2GS4PWIR3FKEP-58-WWET11-PDF File Size 6,444 KB 117 Pages 27 Aug, 2016 TABLE OF CONTENT

Nadere informatie

Business Process Management

Business Process Management Business Process Management Prof. dr. Manu De Backer Universiteit Antwerpen Katholieke Universiteit Leuven Hogeschool Gent Wat is een bedrijfsproces? Een verzameling van (logisch) gerelateerde taken die

Nadere informatie

Understanding and being understood begins with speaking Dutch

Understanding and being understood begins with speaking Dutch Understanding and being understood begins with speaking Dutch Begrijpen en begrepen worden begint met het spreken van de Nederlandse taal The Dutch language links us all Wat leest u in deze folder? 1.

Nadere informatie

Functioneel Ontwerp / Wireframes:

Functioneel Ontwerp / Wireframes: Functioneel Ontwerp / Wireframes: Het functioneel ontwerp van de ilands applicatie voor op de iphone is gebaseerd op het iphone Human Interface Guidelines handboek geschreven door Apple Inc 2007. Rounded-Rectangle

Nadere informatie

Usability evaluation of a guideline implementation systym for cardiac rehabilitation: Think aloud study

Usability evaluation of a guideline implementation systym for cardiac rehabilitation: Think aloud study Usability evaluation of a guideline implementation systym for cardiac rehabilitation: Think aloud study Mariëtte VAN ENGEN-VERHEUL, Linda PEUTE, Ellen KILSDONK, Niels PEEK, Monique JASPERS Mariëtte van

Nadere informatie

Contents. Introduction Problem Definition The Application Co-operation operation and User friendliness Design Implementation

Contents. Introduction Problem Definition The Application Co-operation operation and User friendliness Design Implementation TeleBank Contents Introduction Problem Definition The Application Co-operation operation and User friendliness Design Implementation Introduction - TeleBank Automatic bank services Initiates a Dialog with

Nadere informatie

My Inspiration I got my inspiration from a lamp that I already had made 2 years ago. The lamp is the you can see on the right.

My Inspiration I got my inspiration from a lamp that I already had made 2 years ago. The lamp is the you can see on the right. Mijn Inspiratie Ik kreeg het idee om een variant te maken van een lamp die ik al eerder had gemaakt. Bij de lamp die in de onderstaande foto s is afgebeeld kun je het licht dimmen door de lamellen open

Nadere informatie

Van Big Data tot waardevolle informatie op maat van de (interne)gebruiker en de burger

Van Big Data tot waardevolle informatie op maat van de (interne)gebruiker en de burger Van Big Data tot waardevolle informatie op maat van de (interne)gebruiker en de burger Tijdens deze sessie krijgt u een inzicht in een specifieke visie over hoe men op basis van grote hoeveelheden ongestructureerde

Nadere informatie

Applicatie-Architecturen

Applicatie-Architecturen Applicatie-Architecturen joost.vennekens@kuleuven.be http://www.cs.kuleuven.be/~joost/dn/ Onderwerp Programming in the large! ( programming in the small)! Bijvoorbeeld: KU Leuven Veel verschillende functionaliteit

Nadere informatie

Hoe met Windows 8 te verbinden met NDI Remote Office (NDIRO) How to connect With Windows 8 to NDI Remote Office (NDIRO

Hoe met Windows 8 te verbinden met NDI Remote Office (NDIRO) How to connect With Windows 8 to NDI Remote Office (NDIRO Handleiding/Manual Hoe met Windows 8 te verbinden met NDI Remote Office (NDIRO) How to connect With Windows 8 to NDI Remote Office (NDIRO Inhoudsopgave / Table of Contents 1 Verbinden met het gebruik van

Nadere informatie

Handleiding Zuludesk Parent

Handleiding Zuludesk Parent Handleiding Zuludesk Parent Handleiding Zuludesk Parent Met Zuludesk Parent kunt u buiten schooltijden de ipad van uw kind beheren. Hieronder vind u een korte handleiding met de mogelijkheden. Gebruik

Nadere informatie

GETTING THE BEST OUT OF YOUR SOURCE CODE FIT TEST VOOR UNIFACE

GETTING THE BEST OUT OF YOUR SOURCE CODE FIT TEST VOOR UNIFACE GETTING THE BEST OUT OF YOUR SOURCE CODE FIT TEST VOOR UNIFACE 2 DIGITALISATIE VEREIST: Toegevoegde waarde Agility en snelheid Security en betrouwbaarheid 3 COMBINATIE BUSINESS & IT BUSINESS TECHNOLOGY

Nadere informatie

150 ECG-problemen (Dutch Edition)

150 ECG-problemen (Dutch Edition) 150 ECG-problemen (Dutch Edition) John R. Hampton, Piet Machielse Click here if your download doesn"t start automatically 150 ECG-problemen (Dutch Edition) John R. Hampton, Piet Machielse 150 ECG-problemen

Nadere informatie

Add the standing fingers to get the tens and multiply the closed fingers to get the units.

Add the standing fingers to get the tens and multiply the closed fingers to get the units. Digit work Here's a useful system of finger reckoning from the Middle Ages. To multiply $6 \times 9$, hold up one finger to represent the difference between the five fingers on that hand and the first

Nadere informatie

Opleiding PECB ISO 9001 Quality Manager.

Opleiding PECB ISO 9001 Quality Manager. Opleiding PECB ISO 9001 Quality Manager www.bpmo-academy.nl Wat is kwaliteitsmanagement? Kwaliteitsmanagement beoogt aan te sturen op het verbeteren van kwaliteit. Tevens houdt het zich bezig met het verbinden

Nadere informatie