diff --git a/tex/chapters/0-abstract_fi.tex b/tex/chapters/0-abstract_fi.tex index 6bbcb2c..0f0297c 100644 --- a/tex/chapters/0-abstract_fi.tex +++ b/tex/chapters/0-abstract_fi.tex @@ -30,9 +30,9 @@ \multicolumn{2}{|p{15cm}|}{\vspace{-22pt} Tämän insinöörityön tavoitteena oli selvittää Rust-ohjelmointikielen soveltuvuutta web-ohjelmointiin.\newline - Insinöörityössä käydään läpi web-ohjelmoinnin perusteet, sekä Rustin pääominaisuudet. Lukijalta oletetaan hyvin vähän tietämystä ja kaikki olennaiset käsitteet käydään läpi perusteista alkaen.\newline + Insinöörityössä käydään läpi web-ohjelmoinnin perusteet sekä Rustin pääominaisuudet. Lukijalta oletetaan hyvin vähän tietämystä ja kaikki olennaiset käsitteet käydään läpi perusteista alkaen.\newline - Insinöörityön yhteydessä tehtiin projekti, missä palvelin- ja asiakaspuoli toteutettiin molemmat Rustilla. Projektin kaikki osa-alueet ja kaikki käytetyt riippuvaisuudet, sekä syyt niiden valitsemiseen on käyty läpi perusteellisesti. Myös kehitysympäristön asentaminen ja projektin aloittaminen on käyty läpi alusta alkaen.\newline + Insinöörityön yhteydessä tehtiin projekti, missä palvelin- ja asiakaspuoli toteutettiin Rustilla. Projektin kaikki osa-alueet ja kaikki käytetyt riippuvaisuudet sekä syyt niiden valitsemiseen on käyty läpi perusteellisesti. Myös kehitysympäristön asentaminen ja projektin aloittaminen on käyty läpi alusta alkaen.\newline Projektista kerätyn käytännön tiedon avulla on arvioitu Rustin soveltuvuutta web-ohjelmointiin erikseen sekä asiakas- että palvelinpuolella. Lopuksi on siirretty katsetta hieman tulevaan ja arvioitu, miten kielen soveltuvuus tulee todennäköisesti muuttumaan tulevaisuudessa. Lopussa on myös suosituksia Rustin sisällyttämisestä uusiin ja olemassa oleviin web-ohjelmointiprojekteihin. diff --git a/tex/chapters/1-johdanto.tex b/tex/chapters/1-johdanto.tex index b3ef5c8..26552c9 100644 --- a/tex/chapters/1-johdanto.tex +++ b/tex/chapters/1-johdanto.tex @@ -1,9 +1,9 @@ % Johdanto \chapter{Johdanto} -Älylaitteiden ja tietokoneiden kehityksen sekä internetin parantuneen saatavuuden seurauksena verkon kautta käytettävien palveluiden kysyntä ja tarjonta ovat kasvaneet todella kovalla vauhdilla viime vuosina. Tästä johtuen koko internetin luonne on muuttunut täysin. Olemme todella kaukana 90-luvun dokumenttipohjaisesta internetistä. Nykyaikaiset nettisivut ovat täysiverisiä ohjelmia. +Älylaitteiden ja tietokoneiden kehityksen sekä internetin parantuneen saatavuuden seurauksena verkon kautta käytettävien palveluiden kysyntä ja tarjonta ovat kasvaneet kovalla vauhdilla viime vuosina. Tästä johtuen koko internetin luonne on muuttunut täysin. Olemme todella kaukana 90-luvun dokumenttipohjaisesta internetistä. Nykyaikaiset nettisivut ovat täysverisiä ohjelmia. -Tämän takia myös web-ohjelmoinnissa käytettävissä teknologioissa on tapahtunut paljon muutoksia. Jotta nettisivuista saatiin pelkkien HTML-dokumenttien sijasta ohjelmia, 90-luvun puolivälissä julkaistiin PHP (palvelinpuolen kieli) ja JavaScript (asiakaspuolen kieli). Tänä päivänä näistä kahdesta JavaScript on ehdottomasti suositumpi, minkä takia keskityn tässä insinöörityössä sen vertailuun Rustin kanssa. +Tämän takia myös web-ohjelmoinnissa käytettävissä teknologioissa on tapahtunut paljon muutoksia. Jotta nettisivuista saatiin pelkkien HTML-dokumenttien sijasta ohjelmia, 90-luvun puolivälissä julkaistiin PHP (palvelinpuolen kieli) ja JavaScript (asiakaspuolen kieli). Nykyään näistä kahdesta JavaScript on ehdottomasti suositumpi, minkä takia keskityn tässä insinöörityössä sen vertailuun Rustin kanssa. Vaikka JavaScript onkin suosittu, siinä on paljon ongelmia\cite{medium:javascript}. Suurimmat ongelmat liittyvät kielen heikkoon tyypitykseen. Se saattaa aiheuttaa ongelmia, jotka ilmenevät vasta ajon aikana ja vain tietyissä rajatapauksissa. Tämän takia koodin testaamiseen ja laadun ylläpitämiseen pitää panostaa enemmän. Toinen iso ongelma on se, että JavaScript ei ole tarkka koodin tyylistä. Jo pelkästään funktion kirjoittamiseen on useita eri tyylejä. Tämä hankaloittaa projekteissa kommunikointia ja työtoverien koodin lukemista. diff --git a/tex/chapters/2-web-ohjelmointi.tex b/tex/chapters/2-web-ohjelmointi.tex index 849869d..d4adeac 100644 --- a/tex/chapters/2-web-ohjelmointi.tex +++ b/tex/chapters/2-web-ohjelmointi.tex @@ -9,7 +9,7 @@ Käyttöliittymän ulkonäköä ja toiminnallisuutta voi myös muokata palvelinp \section{Yhden sivun ohjelmat} \label{sect:spa} -Yhden sivun ohjelmat \cite{wiki:spa} ovat tapa toteuttaa asiakaspuolen ohjelmia, missä selain lataa yhden ohjelman ja HTML-dokumentin, minkä jälkeen sivujen uudelleenlatauksia ei enää tapahdu. HTML-dokumentin DOM:ia muutetaan niin sanotusti lennosta. Tämä mahdollistaa sulavamman käyttökokemuksen asiakkaan näkökulmasta, sekä helpottaa sovelluksen tilanhallintaa kehittäjän näkökulmasta. +Yhden sivun ohjelmat \cite{wiki:spa} ovat tapa toteuttaa asiakaspuolen ohjelmia, missä selain lataa yhden ohjelman ja HTML-dokumentin, minkä jälkeen sivujen uudelleenlatauksia ei enää tapahdu. HTML-dokumentin DOM:ia muutetaan niin sanotusti lennosta. Tämä mahdollistaa sulavamman käyttökokemuksen asiakkaan näkökulmasta sekä helpottaa sovelluksen tilanhallintaa kehittäjän näkökulmasta. \clearpage @@ -18,7 +18,6 @@ Ohjelman tilaa hallinnoidaan usein lukemalla ja muuttamalla asiakkaan nettiselai \section{Palvelinpuoli} Palvelinpuolella tarkoitetaan ohjelman osaa, johon asiakaspuoli yhdistyy noutaakseen tai tallettaakseen tietoa. Palvelinpuolen ohjelmointikielissä on huomattavasti enemmän vaihtoehtoja, sillä palvelin kommunikoi asiakkaan kanssa käyttämällä HTTP-protokollaa, johon löytyy kirjastoja lähes jokaiselle ohjelmointikielelle. -\subsection{REST} Palvelinpuoleen viitataan usein myös rajapintana (eng. Application Programming Interface, API), sillä tämänkaltaisia HTTP-protokollalla ohjattavia ohjelmia voi käyttää myös muuallakin kuin asiakaspuolen web-ohjelmissa. Rajapinnat voivat myös yhdistyä toisiinsa internetin välityksellä ja näin jakaa tietoja eri palveluiden välillä. Rajapintojen yleistyessä on kehitetty standardeja, joiden mukaan rajapintoja voi rakentaa. Näin rajapinnoista tulee helpompia käyttää kaikille. Yksi näistä standardeista on Representational state transfer (REST) \cite{wiki:rest}, jota pyrin käyttämään tämän insinöörityön projektia ohjelmoidessa. diff --git a/tex/chapters/3-kielet.tex b/tex/chapters/3-kielet.tex index 0d05d3a..426aa78 100644 --- a/tex/chapters/3-kielet.tex +++ b/tex/chapters/3-kielet.tex @@ -1,7 +1,7 @@ \chapter{Kielet} \section{Rust} -Rust \cite{rust:lang} on Mozillan 2010 julkaisema ohjelmointikieli. Se on hyvin suorituskykyinen järjestelmätason ohjelmointikieli, muistuttaen monilta osin C- ja C++ -kieliä. Rustin tarkoituksena on säilyttää näiden vanhojen kielien suorituskyky, mutta kuitenkin tarjota samalla muun muassa vahva tyypitys ja taattu turvallinen rinnakaisajo. Lisäksi tyypilliset C-kielen muistinhallintaongelmat on pyritty ratkaisemaan käytännöillä, jotka ovat samalla tehokkaita suorituskyvyn näkökulmasta, mutta myös helppoja käyttää ohjelmoijalle. +Rust \cite{rust:lang} on Mozillan 2010 julkaisema ohjelmointikieli. Se on hyvin suorituskykyinen järjestelmätason ohjelmointikieli, joka muistuttaa monilta osin C- ja C++ -kieliä. Rustin tarkoituksena on säilyttää näiden vanhojen kielien suorituskyky, mutta kuitenkin tarjota samalla muun muassa vahva tyypitys ja taattu turvallinen rinnakaisajo. Lisäksi tyypilliset C-kielen muistinhallintaongelmat on pyritty ratkaisemaan käytännöillä, jotka ovat samalla tehokkaita suorituskyvyn näkökulmasta, mutta myös helppoja käyttää ohjelmoijalle. \subsection{Muistinhallinta} @@ -17,7 +17,7 @@ Rustin yhtenä pääominaisuutena on mainostettu sen uudenlaista näkökulmaa mu \label{code:rust:ownership} \end{code} -Koodiesimerkin \ref{code:rust:ownership} rivillä 9 tapahtuva ''name''-arvon tulostus ei toimi, koska arvon omistajuus on siirretty funktiolle ''say\char`_hello''. Funktion suorittamisen jälkeen se poistuu näkyvyysalueelta ja kaikki sen omistamat arvot vapautetaan muistista. Näin ollen arvoa ''name'' ei ole enää olemassa, kun sitä yritetään käyttää rivillä 9. Koodi saadaan toimimaan pienellä muutoksella. +Koodiesimerkin \ref{code:rust:ownership} rivillä 9 tapahtuva ''name''-arvon tulostus ei toimi, koska arvon omistajuus on siirretty funktiolle ''say\char`_hello''. Kun funktio on suoritettu, se poistuu näkyvyysalueelta ja kaikki sen omistamat arvot vapautetaan muistista. Näin ollen arvoa ''name'' ei ole enää olemassa, kun sitä yritetään käyttää rivillä 9. Koodi saadaan toimimaan pienellä muutoksella. \clearpage @@ -27,7 +27,7 @@ Koodiesimerkin \ref{code:rust:ownership} rivillä 9 tapahtuva ''name''-arvon tul \label{code:rust:borrow} \end{code} -Koodiesimerkissä \ref{code:rust:borrow} omistajuuden siirtäminen on korvattu lainauksella. Tämä muutos pitää tehdä sekä funktion parametrien määritykseen riville 1, että funktion kutsuun riville 7. Lainauksessa arvon omistajuus säilyy nykyisellään ja lainaaja antaa itse arvon sijasta viitteen (eng. reference). Viite on osoitin, joka osoittaa samaan muistipaikkaan kuin missä alkuperäinen arvo on. Lainaaminen tehdään käyttämällä merkkiä ''\char`&''. +Koodiesimerkissä \ref{code:rust:borrow} omistajuuden siirtäminen on korvattu lainauksella. Tämä muutos pitää tehdä sekä funktion parametrien määritykseen riville 1 että funktion kutsuun riville 7. Lainauksessa arvon omistajuus säilyy nykyisellään ja lainaaja antaa itse arvon sijasta viitteen (eng. reference). Viite on osoitin, joka osoittaa samaan muistipaikkaan kuin missä alkuperäinen arvo on. Lainaaminen tehdään käyttämällä merkkiä ''\char`&''. \subsection{Vahva tyypitys} Rust on vahvasti tyypitetty kieli, mikä tarkoittaa sitä, että kaikkien arvojen tyypit pitää olla tiedossa ohjelman kääntämisen aikana. Tähän sisältyy myös funktioiden parametrit ja paluuarvot. Usein Rustin kääntäjä osaa päätellä (eng. inference) arvojen tyypit itse, varsinkin yksinkertaisissa tapauksissa. @@ -50,7 +50,7 @@ Niissä tapauksissa, joissa tyypille voi olla useita vaihtoehtoja, tai silloin j \label{code:rust:type-notation} \end{code} -Koodiesimerkissä \ref{code:rust:type-notation} valitsin arvolle ''age'' tyypin u8, koska se on pienin kokonaislukutyypeistä ja sen arvo voi olla välillä 0-255. Näin voi potentiaalisesti vähentää ohjelman muistin käyttöä. Lisäksi tyypeillä voi myös karkeasti rajata funktion parametrien arvojen vaihteluväliä. Esimerkiksi jos on kirjoittamassa funktiota, joka ottaa parametrina henkilön iän, u8 on hyvä valinta, koska ihmisen ikä ei voi olla negatiivinen ja yksikään ihminen tuskin elää yli 255 vuotta. +Koodiesimerkissä \ref{code:rust:type-notation} valitsin arvolle ''age'' tyypin u8, koska se on pienin kokonaislukutyypeistä ja sen arvo voi olla välillä 0-255. Näin voi potentiaalisesti vähentää ohjelman muistin käyttöä. Lisäksi tyypeillä voi karkeasti rajata funktion parametrien arvojen vaihteluväliä. Esimerkiksi jos on kirjoittamassa funktiota, joka ottaa parametrina henkilön iän, u8 on hyvä valinta, koska ihmisen ikä ei voi olla negatiivinen ja yksikään ihminen tuskin elää yli 255 vuotta. Edellä mainittu tyyppi u8 on niin kutsuttu etumerkitön kokonaisluku (eng. \textbf{u}nsigned integer). Etumerkillisissä kokonaisluvuissa (eng. signed integer) käytetään yksi bitti merkkaamaan sitä, onko luku positiivinen vai negatiivinen. Etumerkittömissä luvuissa tätä ei tehdä, joten luku voi olla hieman isompi kuin etumerkillinen luku, mutta se ei voi olla negatiivinen. @@ -125,7 +125,7 @@ Myös Rustin kääntäjään on panostettu paljon, ja virheiden sattuessa se on \end{code} \subsection{Suorituskyky} -Rustin monista luotettavuuteen liittyvistä ominaisuuksista johtuen voisi luulla, että kaikki nämä ominaisuudet vaikuttaisivat negatiivisesti suorituskykyyn. Monissa kielissä onkin runtime-kirjasto \cite{wiki:runtime-library}. Se on kääntäjän jatke, joka lisää käännettyyn binääritiedostoon ajon aikana suoritettavia käskyjä, jotka liittyvät juuri tällaisten luotettavuusominaisuuksien takaamiseen. Nämä käskyt suoritetaan siis itse ohjelman suorituksen yhteydessä, joten ne käyttävät prosessoriaikaa ja muistia aina kun ohjelma on käynnissä. +Rustin monista luotettavuuteen liittyvistä ominaisuuksista johtuen voisi luulla, että kaikki nämä ominaisuudet vaikuttaisivat negatiivisesti suorituskykyyn. Monissa kielissä onkin runtime-kirjasto \cite{wiki:runtime-library}. Se on kääntäjän jatke, joka lisää käännettyyn binääritiedostoon ajon aikana suoritettavia käskyjä, jotka liittyvät juuri tällaisten luotettavuusominaisuuksien takaamiseen. Nämä käskyt suoritetaan siis itse ohjelman suorituksen yhteydessä, joten ne käyttävät prosessoriaikaa ja muistia aina, kun ohjelma on käynnissä. Rustissa ei ole runtime-kirjastoa. Kääntäjä tekee kaikki runtime-kirjaston tehtävät kääntämisen yhteydessä. Jos jokin tarkistus ei mene läpi, esimerkiksi omistajuusmallia on käytetty väärin, ohjelman kääntäminen keskeytetään ja kehittäjälle näytetään virheviesti. @@ -140,7 +140,7 @@ Rust takaa turvallisen rinnakkaisajon luvussa \ref{sect:luotettavuus} mainituill \subsection{Makrot} \label{chap:macros} -Yksi todella mielenkiintoinen ominaisuus Rustissa on makrot. Se on toiminnallisuus mikä mahdollistaa metaohjelmoinnin \cite{wiki:metaprogramming}. Metaohjelmoinnissa koodia voi generoida kääntämisen aikana, mikä on erityisen hyödyllistä esimerkiksi silloin, kun ohjelmoija tarvitsee useita toiminnallisuudeltaan samankaltaisia funktioita. +Yksi todella mielenkiintoinen ominaisuus Rustissa on makrot. Se on toiminnallisuus, mikä mahdollistaa metaohjelmoinnin \cite{wiki:metaprogramming}. Metaohjelmoinnissa koodia voi generoida kääntämisen aikana, mikä on erityisen hyödyllistä esimerkiksi silloin, kun ohjelmoija tarvitsee useita toiminnallisuudeltaan samankaltaisia funktioita. \bigskip @@ -169,7 +169,7 @@ Aloitin itsekin opiskelemaan Rustia vain hieman ennen tämän insinöörityön a \subsection{Paketinhallinta} \label{sect:paketinhallinta} -Rustin paketinhallinta on toteutettu Cargo-nimisellä ohjelmalla. Sitä voi käyttää koko ohjelmiston elinkaaren ajan aina projektin luomisesta sen julkaisemiseen. Cargon käsittelemiä paketteja kutsutaan laatikoiksi (eng. crate), jotka julkaistaan crates.io \cite{rust:cratesio} pakettirekisterissä. Laatikot voivat myös olla riippuvaisia toisista laatikoista. Laatikon tiedot ja riippuvuudet määritetään Cargo.toml-tiedostossa [koodiesimerkki \ref{code:rust:cargo-toml}]. +Rustin paketinhallinta on toteutettu Cargo-nimisellä ohjelmalla. Sitä voi käyttää koko ohjelmiston elinkaaren ajan aina projektin luomisesta sen julkaisemiseen. Cargon käsittelemiä paketteja kutsutaan laatikoiksi (eng. crate), jotka julkaistaan crates.io-pakettirekisterissä \cite{rust:cratesio}. Laatikot voivat myös olla riippuvaisia toisista laatikoista. Laatikon tiedot ja riippuvuudet määritetään Cargo.toml-tiedostossa [koodiesimerkki \ref{code:rust:cargo-toml}]. \clearpage diff --git a/tex/chapters/4-projekti.tex b/tex/chapters/4-projekti.tex index 1909ca0..de819dc 100644 --- a/tex/chapters/4-projekti.tex +++ b/tex/chapters/4-projekti.tex @@ -1,5 +1,5 @@ \chapter{Projekti} -Tein insinöörityön yhteydessä full-stack projektin, jossa sekä palvelin- että asiakaspuolen ohjelmointi tehtiin Rustilla. +Tein insinöörityön yhteydessä full-stack-projektin, jossa sekä palvelin- että asiakaspuolen ohjelmointi tehtiin Rustilla. \section{Tavoitteet} Tavoitteena ei ollut saada aikaiseksi mitään todella monimutkaista ohjelmaa, vaan puhtaasti arvioida Rustin soveltuvuutta web-ohjelmointiin yksinkertaisella esimerkkiprojektilla. @@ -11,20 +11,20 @@ Tavoitteena ei ollut saada aikaiseksi mitään todella monimutkaista ohjelmaa, v \label{fig:architecture} \end{figure} -Kuvassa \ref{fig:architecture} näkee sovelluksen arkkitehtuurin. Sovelluksen asiakaspuoli koostuu mallin keltaisista laatikoista ja palvelinpuoli sininisistä. Nuolet merkitsevät datan liikkumista, mikä on kaikkialla kaksisuuntaista. Yew ja Actix ovat valitsemani asiakas- ja palvelinpuolen sovelluskehykset. +Kuvassa \ref{fig:architecture} näkee sovelluksen arkkitehtuurin. Sovelluksen asiakaspuoli koostuu mallin keltaisista laatikoista ja palvelinpuoli sinisistä. Nuolet merkitsevät datan liikkumista, mikä on kaikkialla kaksisuuntaista. Yew ja Actix ovat valitsemani asiakas- ja palvelinpuolen sovelluskehykset. \section{Kehitysympäristön asennus} Rust-projektin aloittamiseksi kehittäjä tarvitsee koneelleen Rustin paketinhallintatyökalun, Cargon (katso luku \ref{sect:paketinhallinta}). Olen itse Linux-käyttäjä, joten sain asennettua Cargon Linux-jakeluni ohjelmavarastosta. Mac- ja Windows-käyttäjille suosittelen Rustup-asennusohjelman käyttämistä. Lisää tietoa Rustin asentamisesta saa Rustin kotisivuilta \cite{rust:install}. Projektin saa initialisoitua komennolla \mintinline{shell}{cargo init projektinnimi}. Tämä komento luo hakemiston ''projektinnimi'', minkä sisällä on Cargon konfiguraatiotiedosto Cargo.toml, jossa voi määrittää projektin tiedot ja riippuvuudet. -Lähdekoodi sijaitsee hakemistossa ''src''. Cargo kirjoittaa hakemistoon valmiiksi ''main.rs'' -tiedoston, jossa on ''Hello world!'' esimerkkikoodi. ''main.rs'' on aina Rust-ohjelman ensimmäiseksi suoritettava tiedosto, eli niin kutsuttu entrypoint. ''main.rs''-tiedoston sisällä pitää olla ''main()''-funktio, josta ohjelman suoritus alkaa. Projektin voi suorittaa komennolla \mintinline{shell}{cargo run}. +Lähdekoodi sijaitsee hakemistossa ''src''. Cargo kirjoittaa hakemistoon valmiiksi ''main.rs'' -tiedoston, jossa on ''Hello world!'' -esimerkkikoodi. ''main.rs'' on aina Rust-ohjelman ensimmäiseksi suoritettava tiedosto, eli niin kutsuttu entrypoint. ''main.rs''-tiedoston sisällä pitää olla ''main()''-funktio, josta ohjelman suoritus alkaa. Projektin voi suorittaa komennolla \mintinline{shell}{cargo run}. \clearpage -Suosittelen asentamaan myös muutaman Cargon liitännäisen helpottamaan kehitystä. cargo-watchin avulla voi ajaa komennon aina, kun lähdekoodi muuttuu. Esimerkiksi komennolla \mintinline{shell}{cargo watch -x run} voi kääntää ja käynnistää projektin aina uudelleen, kun lähdekoodi muuttuu. cargo-add -liitännäisellä voi helposti lisätä uusia riippuvuuksia projektiinsa. Esimerkiksi Actix webin saa lisättyä komennolla \mintinline{shell}{cargo add actix-web}. +Suosittelen asentamaan myös muutaman Cargon liitännäisen helpottamaan kehitystä. cargo-watchin avulla voi ajaa komennon aina, kun lähdekoodi muuttuu. Esimerkiksi komennolla \mintinline{shell}{cargo watch -x run} voi kääntää ja käynnistää projektin aina uudelleen, kun lähdekoodi muuttuu. cargo-add-liitännäisellä voi helposti lisätä uusia riippuvuuksia projektiinsa. Esimerkiksi Actix webin saa lisättyä komennolla \mintinline{shell}{cargo add actix-web}. -Näillä ohjeilla pääsee alkuun palvelinpuolen kehityksessä. Asiakaspuolen kehityksen käynnistäminen vaati vielä muutaman lisävaiheen, siitä lisää luvussa \ref{sect:asiakaspuoli:asennus}. +Näillä ohjeilla pääsee alkuun palvelinpuolen kehityksessä. Asiakaspuolen kehityksen käynnistäminen vaati vielä muutaman lisävaiheen. Siitä lisää luvussa \ref{sect:asiakaspuoli:asennus}. \section{Palvelinpuoli} \subsection{Kehys} @@ -63,7 +63,7 @@ Todentamiseen päätin käyttää JSON Web Tokeneita. Ne ovat standardoitu (RFC \begin{figure}[h] \centering \includegraphics[width=\linewidth]{illustration/jwt.png} - \caption{JWT:n osat havainnollistettuna käyttämällä jwt.io -sivustoa \cite{jwt:home}} + \caption{JWT:n osat havainnollistettuna käyttämällä jwt.io-sivustoa \cite{jwt:home}} \label{fig:jwt} \end{figure} @@ -79,7 +79,7 @@ Ylätunniste koostuu JSON-objektista [koodiesimerkki \ref{code:json:jwt-header}] \bigskip -Tokenin toisessa osassa, hyötykuormassa, on itse tokenin sisältö [koodiesimerkki \ref{code:json:jwt}]. Tokenin sisällön voi päättää kokonaan itse, vaikkakin joitakin standardeja kenttiä on määritetty. Esimerkiksi iss (issuer), sub (subject) ja exp (expiration time). Päätin sisällyttää myös tiedon siitä, että onko käyttäjä ylläpitäjä, koska tätä tietoa voi sitten käyttää asiakaspuolella esimerkiksi käyttöliittymän muokkaamiseen käyttäjän roolin perusteella. Usein myös käyttäjän nimi sisällytetään tokeniin. Tokenin sisältöä suunnitellessa kannattaa pitää mielessä, että sen sisältö on nähtävissä kaikille, joten se ei ole oikea paikka tallettaa salaista tietoa, kuten vaikka käyttäjän salasana. +Tokenin toisessa osassa, hyötykuormassa, on itse tokenin sisältö [koodiesimerkki \ref{code:json:jwt}]. Tokenin sisällön voi päättää kokonaan itse, vaikkakin joitakin standardeja kenttiä on määritetty. Esimerkiksi iss (issuer), sub (subject) ja exp (expiration time). Päätin sisällyttää myös tiedon siitä, onko käyttäjä ylläpitäjä, koska tätä tietoa voi sitten käyttää asiakaspuolella esimerkiksi käyttöliittymän muokkaamiseen käyttäjän roolin perusteella. Usein myös käyttäjän nimi sisällytetään tokeniin. Tokenin sisältöä suunnitellessa kannattaa pitää mielessä, että sen sisältö on nähtävissä kaikille, joten se ei ole oikea paikka tallettaa salaista tietoa, kuten vaikka käyttäjän salasana. \bigskip @@ -94,7 +94,7 @@ Tokenin toisessa osassa, hyötykuormassa, on itse tokenin sisältö [koodiesimer Tokenin kolmesta osasta viimeinen on allekirjoitus. Se on koostettu ylätunnisteessa [koodiesimerkki \ref{code:json:jwt-header}] määritellyllä algoritmilla käyttämällä parametreina tokenin hyötykuormaa [koodiesimerkki \ref{code:json:jwt}] ja vain palvelimen tiedossa olevaa salasanaa. Koko tokenin turvallisuus perustuu juuri tähän allekirjoitukseen. Sen avulla palvelin voi varmistua siitä, että tokenia ei ole muokannut kukaan, kenellä ei ole tätä salasanaa. -Tokenia varmennettaessa hyötykuorma allekirjoitetaan uudelleen ja tätä uutta allekirjoitusta verrataan tokenin mukana tulleeseen allekirjoitukseen. Jos ne ovat samat, palvelin voi olla varma siitä että tokenin sisältöön voi luottaa. Tämän edellytyksenä tietysti on, että salasanaa on säilytetty turvallisella tavalla. +Tokenia varmennettaessa hyötykuorma allekirjoitetaan uudelleen ja tätä uutta allekirjoitusta verrataan tokenin mukana tulleeseen allekirjoitukseen. Jos ne ovat samat, palvelin voi olla varma siitä, että tokenin sisältöön voi luottaa. Tämän edellytyksenä tietysti on, että salasanaa on säilytetty turvallisella tavalla. Otetaan esimerkkinä tämän sovelluksen käyttäjä Pasi. Pasi ei ole ylläpitäjä, mutta hän koittaa tehdä itsestään ylläpitäjän muuttamalla omasta tokenistaan parametrin \mintinline{JavaScript}{"admin": false} arvoksi \mintinline{JavaScript}{true}. Kun Pasi lähettää tämän muokatun tokenin palvelimelle, se viedään JWT-validointifunktioon. Palvelin huomaa, että token ei ole enää validi, koska sen sisältöä ei ole allekirjoitettu palvelimen salasanalla. @@ -104,7 +104,7 @@ Projektin JWT-toteutus on liitteessä \ref{appx:jwt}. \subsection{CORS} -Lisäsin palvelimelle myös Cross-Origin Resource Sharing (CORS) \cite{wiki:cors} -toiminnallisuuden. Oletuksena selaimen lataama ohjelma saa ladata resursseja vain samasta osoitteesta, kuin mistä itse ohjelma on ladattu. Tämä on turvallisuuskäytäntö, joka kulkee nimellä Same-origin policy (SOP) \cite{wiki:sop}. Näihin resursseihin sisältyvät muunmuassa CSS-tyylimääritykset, kuvat ja JavaScript-ohjelmat. +Lisäsin palvelimelle myös Cross-Origin Resource Sharing (CORS) \cite{wiki:cors} -toiminnallisuuden. Oletuksena selaimen lataama ohjelma saa ladata resursseja vain samasta osoitteesta, kuin mistä itse ohjelma on ladattu. Tämä on turvallisuuskäytäntö, joka kulkee nimellä Same-origin policy (SOP) \cite{wiki:sop}. Näihin resursseihin sisältyvät muun muassa CSS-tyylimääritykset, kuvat ja JavaScript-ohjelmat. \begin{figure}[h] \centering @@ -132,6 +132,8 @@ CORS:n lisääminen tähän projektiin hoitui Actixin liitännäisellä actix\ch \subsection{Tietokanta} \label{project:database} +Tietokannaksi valikoitui itselleni tuttu MySQL. Koska projekti on yksinkertainen todennusdemo, tietokantaan tuli vain yksi taulu. Users-taulu sisältää käyttäjän tiedot: käyttäjänimen, salatun salasanan, tiedon käyttäjän ylläpitäjyydestä sekä käyttäjän luomisajankohta. Tietokannan ER-mallin näkee kuvasta \ref{fig:er-model}. + \begin{figure}[h] \centering \includegraphics[width=12cm]{illustration/er-model.pdf} @@ -139,15 +141,13 @@ CORS:n lisääminen tähän projektiin hoitui Actixin liitännäisellä actix\ch \label{fig:er-model} \end{figure} -Tietokannaksi valikoitui itselleni tuttu MySQL. Koska projekti on yksinkertainen todennusdemo, tietokantaan tuli vain yksi taulu. Users-taulussa on käyttäjän tiedot: käyttäjänimi, salattu salasana, tieto käyttäjän ylläpitäjyydestä sekä käyttäjän luomisajankohta. Tietokannan ER-mallin näkee kuvasta \ref{fig:er-model}. - Relaatiotietokannan sai helposti yhdistettyä Rust-koodiini Diesel ORM-kirjastolla. Diesel on tähän mennessä käyttämistäni ORM-kirjastoista selkeästi mukavin käyttää. Käytännöllisimmät ominaisuudet kehittäjän näkökulmasta olivat Dieselin mukana tuleva komentorivikäyttöliittymä ja migraatiot. Jokaiselle taululle luodaan uusi migraatio, esimerkiksi \mintinline{shell}{diesel migration generate users}, jonka jälkeen Dieselin luomaan hakemistoon kirjoitetaan up.sql- ja down.sql-tiedostot [liite \ref{appx:diesel}], eli ohjeet siitä, miten tämä taulu luodaan ja poistetaan. Taulu viedään tietokantaan komennolla \mintinline{shell}{diesel migration run} ja taulun voi poistaa ja luoda uudelleen komennolla \mintinline{shell}{diesel migration redo}. Tämä mahdollistaa myös samalla sen, että versiohallintaan voi tallentaa useita versioita samasta taulusta ja palata helposti takaisin vanhempaan versioon, jos uudemman kanssa ilmenee ongelmia. \clearpage -Edellä mainitut työkalut helpottivat tietokannan kehitystä huomattavasti. Usein varsinkin projektin alkuvaiheilla tietokanta muuttuu jatkuvasti ja usein tulee tarve poistaa ja luoda tietokanta uudelleen. Monet kehittäjät pitävät juurikin tällaista Dieselin up.sql kaltaista tiedostoa versiohallinnassa ja tarpeen mukaan poistavat tietokannan käsin ja liittävät komennon tiedostosta tietokannan komentorivikäyttöliittymään. Dieseliä käytettäessä tämä tulee tehtyä automaattisesti ja se tuntuu todella luontevalta. +Edellä mainitut työkalut helpottivat tietokannan kehitystä huomattavasti. Usein varsinkin projektin alkuvaiheilla tietokanta muuttuu jatkuvasti ja usein tulee tarve poistaa ja luoda tietokanta uudelleen. Monet kehittäjät pitävät juurikin tällaista Dieselin up.sql:n kaltaista tiedostoa versiohallinnassa ja tarpeen mukaan poistavat tietokannan käsin ja liittävät komennon tiedostosta tietokannan komentorivikäyttöliittymään. Dieseliä käytettäessä tämä tulee tehtyä automaattisesti ja se tuntuu todella luontevalta. Päätin myös käyttää Dieselin kanssa yhteyksien yhdistämistä (eng. connection pooling). Tämä tarkoittaa sitä, että palvelin luo käynnistyessään prosessin, joka ottaa yhteyden tietokantaan. Tämä prosessi puolestaan jakaa tätä yhteyttä sitä tarvitseville ohjelman osille. Tämä on tehokkaampaa kuin vaihtoehto, jossa otetaan uusi yhteys jokaista tietokantakyselyä varten. @@ -161,14 +161,14 @@ Valitsemani sovelluskehys tukee molempia kirjastoja, mutta päädyin kuitenkin v WebAssembly-koodin suorittamiseksi selain tarvitsee pienen pätkän JavaScriptiä, joka tekee tarvittavat toimet WebAssembly-ohjelman käynnistämiseksi. Tämän koodin generoi puolestani cargo-web. Lisäksi JavaScriptin suorittamiseen tarvitaan yksi HTML-tiedosto. Tähän tiedostoon voi myös sisällyttää metadataa, kuten esimerkiksi sivuston otsikon, joka näkyy selaimen välilehdessä. Tähän tiedostoon linkitetään myös kaikki muu staattinen data, kuten tyylimääritykset. Nämä tiedostot laitoin käytännön mukaisesti static-nimiseen hakemistoon asiakaspuolen projektin juureen. Tämän hakemiston linkitin symbolisella linkillä palvelinpuolen projektiin, jonka HTTP-serveri voi sitten lähettää HTML-dokumentin, JavaScript-tiedoston ja WebAssembly-binäärin käyttäjän selaimelle. \subsection{Kehys} -Asiakaspuolen sovelluskehykseksi valitsin Yew:n\cite{yew:home}. Yew muistuttaa hyvin paljon JavaScript-maailmassa suosittua Reactia, eli sen on komponenttipohjainen. Tämä tarkoittaa sitä, että kaikkien ohjelman osien, joilla halutaan näyttää jotakin käyttöliittymän osaa, täytyy implementoida Yew:n Component-rajapintaa. Tässä rajapinnassa on funktiot create, change, update ja view. Näiden funktioiden avulla Yew pystyy orkestroimaan kullakin hetkellä näytettävien komponenttien tilaa. +Asiakaspuolen sovelluskehykseksi valitsin Yew'n\cite{yew:home}. Yew muistuttaa hyvin paljon JavaScript-maailmassa suosittua Reactia, eli sen on komponenttipohjainen. Tämä tarkoittaa sitä, että kaikkien ohjelman osien, joilla halutaan näyttää jotakin käyttöliittymän osaa, täytyy implementoida Yew'n Component-rajapintaa. Tässä rajapinnassa on funktiot create, change, update ja view. Näiden funktioiden avulla Yew pystyy orkestroimaan kullakin hetkellä näytettävien komponenttien tilaa. Tässä projektissa komponentteja oli kolme: ohjelman juuri [liite \ref{appx:frontend-main}], kirjautumiskomponentti [liite \ref{appx:login}] ja suojatun datan noutamiseen tehty komponenti [liite \ref{appx:protected}]. \subsection{Ulkonäkö} \label{sect:ulkonäkö} -Olen JavaScript-maailmassa tottunut siihen, että käyttöliittymäkehyksiin löytyy usein kirjasto, joka tarjoaa valmiit tyylimääritykset, mutta en löytänyt vastaavaista kirjastoa, joka toimisi Yew:n kanssa. Kaikki käyttöliittymän elementit olivat siis selaimen oletustyylisiä. En halunnut ryhtyä tässä projektissa kirjoittamaan CSS-määrityksiä alusta alkaen, joten päädyin käyttämään UIkittiä\cite{uikit}. UIkit on CSS-kirjasto, missä on paljon valmiita ja hyvännäköisiä tyylimäärityksiä. UIkitin dokumentaatio on laaja ja siellä on paljon esimerkkejä, joten alkuun pääsi todella nopeasti [kuva \ref{fig:login}]. +Olen JavaScript-maailmassa tottunut siihen, että käyttöliittymäkehyksiin löytyy usein kirjasto, joka tarjoaa valmiit tyylimääritykset, mutta en löytänyt vastaavaista kirjastoa, joka toimisi Yew'n kanssa. Kaikki käyttöliittymän elementit olivat siis selaimen oletustyylisiä. En halunnut ryhtyä tässä projektissa kirjoittamaan CSS-määrityksiä alusta alkaen, joten päädyin käyttämään UIkittiä\cite{uikit}. UIkit on CSS-kirjasto, missä on paljon valmiita ja hyvännäköisiä tyylimäärityksiä. UIkitin dokumentaatio on laaja ja siellä on paljon esimerkkejä, joten alkuun pääsi todella nopeasti [kuva \ref{fig:login}]. \begin{figure}[h] \centering @@ -177,16 +177,16 @@ Olen JavaScript-maailmassa tottunut siihen, että käyttöliittymäkehyksiin lö \label{fig:login} \end{figure} -Login-komponentti löytyy kokonaisuudessaan liitteestä \ref{appx:login}. Liitteestä näkee esimerkin Yew:n komponenttirajapinnan käytöstä ja palvelimeen yhdistämisestä. Lisäksi siellä on käytetty UIKit-tyylityksiä. +Login-komponentti löytyy kokonaisuudessaan liitteestä \ref{appx:login}. Liitteestä näkee esimerkin Yew'n komponenttirajapinnan käytöstä ja palvelimeen yhdistämisestä. Lisäksi siellä on käytetty UIKit-tyylityksiä. \subsection{Reititys} -Reititykseen käytin Yew:n liitännäistä, yew\char`_routeria. Reititys asiakaspuolen ohjelmassa tarkoittaa sitä, että selaimen osoitepalkissa olevan polun mukaan käyttäjä reititetään oikeaan ohjelman osaan. Tämä liittyy käsitykseen yhden sivun ohjelmista (katso luku \ref{sect:spa}), joissa selain suorittaa yhden ohjelman, jonka jälkeen perinteisiä sivujen latauksia ei enää tapahdu. Ohjelma muokkaa osoitepalkissa näkyvää osoitetta, jonka pohjalta sitten reititys oikeaan komponenttiin tapahtuu. Käyttöliittymää muokataan käyttämällä DOM:ia, joka mahdollistaa sivun sisällön muokkaamisen ilman sivun uudelleen lataamista. +Reititykseen käytin Yew'n liitännäistä, yew\char`_routeria. Reititys asiakaspuolen ohjelmassa tarkoittaa sitä, että selaimen osoitepalkissa olevan polun mukaan käyttäjä reititetään oikeaan ohjelman osaan. Tämä liittyy käsitykseen yhden sivun ohjelmista (katso luku \ref{sect:spa}), joissa selain suorittaa yhden ohjelman, jonka jälkeen perinteisiä sivujen latauksia ei enää tapahdu. Ohjelma muokkaa osoitepalkissa näkyvää osoitetta, jonka pohjalta sitten reititys oikeaan komponenttiin tapahtuu. Käyttöliittymää muokataan käyttämällä DOM:ia, joka mahdollistaa sivun sisällön muokkaamisen ilman sivun uudelleenlataamista. Asiakaspuolen reititys vaatii palvelinpuolelta sen, että kaikki mahdolliset asiakaspuolen reitit palauttavat ohjelman. Toinen edellytys on se, että palvelin ei palauta uudelleenohjausta (HTTP 302), koska silloin myös osoitepalkissa oleva osoite muuttuu, eikä sitä silloin voida välittää asiakaspuolen ohjelmalle. Yritin pitkään toteuttaa tällaista logiikkaa palvelinpuolelle siinä onnistumatta, mutta onnekseni yew\char`_routerin esimerkeistä löytyi esimerkkikoodia tämän saavuttamiseksi käyttämäni palvelinkehyksen kanssa. Päädyin laittamaan kaikki palvelimen omat reitit polun \mintinline{shell}{/api/} alle, ja asiakaspuolen ohjelman juureen (\mintinline{shell}{/}). Sitten määritin ns. ''catch-all'' reitin, joka palauttaa asiakaspuolen ohjelman ilman uudelleenohjausta. Tämä on Actixissa nimeltään \mintinline{shell}{default_service}. -Myös yew\char`_routerin kanssa oli omat haasteensa. Alkuun en saanut sitä toimimaan ollenkaan, reititin ohjasi kaikki osoitteet ensimmäisenä määritettyyn reittiin, mikä oli tässä tapauksessa ''/''. Ongelman syyksi paljastui yew\char`_routerin Switch-komponentin kokoava ''to'-makro (katso luku \ref{chap:macros}). Ratkaisuna oli reitin ''/'' makron siirtäminen viimeiseksi listassa. Tarkempaa tietoa tästä ongelmasta voi lukea Yew:n dokumentaatiosta \cite{yew:router-problem}. +Myös yew\char`_routerin kanssa oli omat haasteensa. Alkuun en saanut sitä toimimaan ollenkaan, reititin ohjasi kaikki osoitteet ensimmäisenä määritettyyn reittiin, mikä oli tässä tapauksessa ''/''. Ongelman syyksi paljastui yew\char`_routerin Switch-komponentin kokoava ''to'-makro (katso luku \ref{chap:macros}). Ratkaisuna oli reitin ''/'' makron siirtäminen viimeiseksi listassa. Tarkempaa tietoa tästä ongelmasta voi lukea Yew'n dokumentaatiosta \cite{yew:router-problem}. Asiakaspuolen reitityksen toteutuksen voi nähdä liitteessä \ref{appx:frontend-main}. @@ -196,7 +196,7 @@ Projektin aikana ilmeni muutamia ongelmia, joiden takia projekti vaatii vielä j \subsection{Evästeet} \label{sect:problems:cookies} -Palvelinpuolella evästeen kokoava kirjasto, Actixin liitännäinen actix\char`_identity, kirjoittaa oletuksena evästeeseen parametrin \mintinline{JavaScript}{"HttpOnly": true}, mikä tarkoittaa sitä, että selain ei anna sen sisällä suoritettaville ohjelmille pääsyä tähän evästeeseen. Tämä on todella hyvä turvallisuusominaisuus, joka estää haitallisia ohjelmia varastamasta käyttäjän kirjautumistietoja. +Palvelinpuolella evästeen kokoava kirjasto, Actixin liitännäinen actix\char`_identity, kirjoittaa oletuksena evästeeseen parametrin \mintinline{JavaScript}{"HttpOnly": true}, mikä tarkoittaa sitä, että selain ei anna sen sisällä suoritettaville ohjelmille pääsyä tähän evästeeseen. Tämä on hyvä turvallisuusominaisuus, joka estää haitallisia ohjelmia varastamasta käyttäjän kirjautumistietoja. Suunnittelmana oli käyttää evästeen tietoja asiakaspuolella reititykseen. Esimerkiksi jos evästettä ei ole olemassa, käyttäjä tulee reitittää sisäänkirjautumiskomponenttiin, jossa eväste voidaan noutaa palvelimelta. Koska evästeessä on tämä HttpOnly-parametri, tähän ei ole mahdollisuutta. Tämän parametrin voisi tietysti laittaa pois päältä, mutta actix\char`_identity ei tarjoa tähän mitään mahdollisuutta. diff --git a/tex/chapters/5-soveltuvuus.tex b/tex/chapters/5-soveltuvuus.tex index f0eff94..f487af4 100644 --- a/tex/chapters/5-soveltuvuus.tex +++ b/tex/chapters/5-soveltuvuus.tex @@ -8,7 +8,7 @@ Näen myös, että Rustin vahvasta tyypityksestä voi olla paljon hyötyä web-s Lisäksi Rust on suosittu kieli muuallakin kuin web-maailmassa. Tämän ansiosta kirjastojen saatavuus erityisesti palvelinpuolella ei ole ongelma. Rust-koodiin voi myös sisällyttää C- ja C++ -kirjastoja, jos jotakin projektissa tarvittavaa kirjastoa ei löydy Rust-pakettina. \section{Asiakaspuoli} -Asiakaspuolella Rustin hyötyjä on vaikeampi oikeuttaa. Asiakaspuolen ohjelmat ovat useimmiten yksinkertaisia, koska niiden tehtävänä on usein vain piirtää käyttöliittymä ja välittää dataa palvelimen ja käyttöliittymän välillä. Näin ollen myös suorituskykyvaatimukset ovat todella pienet verrattuna palvelinpuoleen. Lisäksi asiakaspuoli palvelee aina vain yhtä asiakasta kerrallaan, verrattuna palvelinpuoleen, jossa usein yksi palvelin palvelee kaikkia asiakkaita. Tästä syystä myös Rustin rinnakkaisajo-ominaisuudet jäävät hyödyntämättä. +Asiakaspuolella Rustin hyötyjä on vaikeampi oikeuttaa. Asiakaspuolen ohjelmat ovat useimmiten yksinkertaisia, koska niiden tehtävänä on usein vain piirtää käyttöliittymä ja välittää dataa palvelimen ja käyttöliittymän välillä. Näin ollen myös suorituskykyvaatimukset ovat todella pienet verrattuna palvelinpuoleen. Lisäksi asiakaspuoli palvelee aina vain yhtä asiakasta kerrallaan verrattuna palvelinpuoleen, jossa usein yksi palvelin palvelee kaikkia asiakkaita. Tästä syystä myös Rustin rinnakkaisajo-ominaisuudet jäävät hyödyntämättä. Vahvan tyypityksen hyötyjä ei voi kieltää, mutta kuten luvussa \ref{sect:soveltuvuus:palvelinpuoli} mainittiin, myös JavaScriptin saa tyypitettyä käyttämällä TypeScriptiä. diff --git a/tex/chapters/6-yhteenveto.tex b/tex/chapters/6-yhteenveto.tex index 423fed8..cd919bc 100644 --- a/tex/chapters/6-yhteenveto.tex +++ b/tex/chapters/6-yhteenveto.tex @@ -3,7 +3,7 @@ Rust soveltuu todella hyvin palvelinpuolen web-ohjelmointikieleksi. Sen luotetta Rust on asiakaspuolen web-ohjelmointikielenä riippuvainen WebAssembly-ekosysteemistä, minkä takia kirjastojen saatavuus on vielä melko huono. Lisäksi asiakaspuolen alhaisten suorituskykyvaatimuksien takia Rustin paremmasta suorituskyvystä verrattuna JavaScriptiin on vähän hyötyä useimmissa tapauksissa. -Koko projektia ei ole pakko kirjoittaa alusta loppuun Rustilla, niinkuin tässä insinöörityössä tehtiin. Jos ohjelmassa on korkeampaa suorituskykyä vaativia osia, ne voi kirjoittaa Rustilla ja sisälllyttää WebAssembly-binääreinä osaksi muuten JavaScriptillä toteutettua ohjelmaa. +Koko projektia ei ole pakko kirjoittaa alusta loppuun Rustilla, niin kuin tässä insinöörityössä tehtiin. Jos ohjelmassa on korkeampaa suorituskykyä vaativia osia, ne voi kirjoittaa Rustilla ja sisälllyttää WebAssembly-binääreinä osaksi muuten JavaScriptillä toteutettua ohjelmaa. Rustia voi sisällyttää edellä mainituilla tekniikoilla myös olemassa oleviin projekteihin. Jos kehittäjä haluaa siirtää olemassa olevan JavaScript-projektin kokonaan Rust-pohjaiseksi, sen voi tehdä asteittain. @@ -13,6 +13,6 @@ Itse tulen todennäköisesti käyttämään Rustia seuraavassa palvelinpuolen pr Jään mielenkiinnolla seuraamaan web-kehityksen tulevaisuutta ja sitä, mihin WebAssemblyn ekosysteemi kehittyy lähivuosina. Kirjastojen saatavuuden parantumisen myötä uskon, että se tulee tulevaisuudessa olemaan entistäkin varteenotettavampi vaihtoehto JavaScriptille. En kuitenkaan usko, että JavaScriptistä pois siirtyminen tapahtuu yhdessä yössä, vaan WebAssembly kehittyy JavaScriptin rinnalla vielä pitkään. -Tämän insinöörityön lähdekoodi on avoin ja se löytyy kokonaisuudessaan GitLabista \cite{sourcecode}. Tähän sisältyy projektin lähdekoodi, sekä raportin \LaTeX{}-lähdekoodi. +Tämän insinöörityön lähdekoodi on avoin ja se löytyy kokonaisuudessaan GitLabista \cite{sourcecode}. Tähän sisältyy projektin lähdekoodi sekä raportin \LaTeX{}-lähdekoodi. \clearpage diff --git a/tex/style/abbr.tex b/tex/style/abbr.tex index 92ac216..4cd7e97 100644 --- a/tex/style/abbr.tex +++ b/tex/style/abbr.tex @@ -2,7 +2,7 @@ % Normally, you don't have to modify this file. Your abbreviations, etc. goes in % ../chapters/0abbr.tex file. -\begin{singlespacing} +\begin{onehalfspace} % \gsladdall would add all terms even if not used in your text. %\glsaddall @@ -24,6 +24,6 @@ \setlist[description]{style=standard} % reset settings back to default } \addtocontents{toc}{\cftpagenumberson{chapter}} -\end{singlespacing} +\end{onehalfspace} \clearpage diff --git a/tex/style/style.tex b/tex/style/style.tex index 286dce2..4d4e87c 100644 --- a/tex/style/style.tex +++ b/tex/style/style.tex @@ -60,7 +60,7 @@ \usepackage{tabularx} \usepackage{booktabs} %why not booktabs? :3 % Abbreviations, acronym and glossary -\usepackage[acronym,toc,nonumberlist,section=chapter]{glossaries}%xindy,%toc, ,nomain +\usepackage[acronym,toc,nogroupskip,nonumberlist,section=chapter]{glossaries}%xindy,%toc, ,nomain \renewcommand*{\glsclearpage}{} \usepackage{float} % For forced figure location with modifier H (\begin{figure}[H])