"The Grid. A digital frontier. I tried to picture clusters of information as they move through the computer. What did they look like? Ships? Motorcycles? Were the circuits like freeways? I kept dreaming of a world I thought I’d never see. And then, one day, I got in."
— Tron: Legacy
This time this blog post is in English. I decided to change language when I saw link to my blog at Seaside.st. The other reason is that Google's translator just sucks. I hope to suck less.
Post 4 is the last post from the series about HTML parsing and presenting results in form of a web page using Smalltalk and Seaside. All these posts about Smalltalk were my attempt to find out for myself what this language is really about. A year ago I didn't even know about it's existence. My programming world till that time was wrapped mainly in C/C++ and Java. So I thought I'll try some of it in action and that the best way to grab a bit of it is to try it in practice and to solidify that experience on a blog. Well, actually Smalltalk was the main reason for this blog to show up. At the same time I also found Clojure. Both of these as languages just blown my C/Java mind away. Smalltalk is impossibly simple language and has a very helpful IDE (a REAL IDE not just some editor with syntax highlighter and fancy keyboard shortcuts that spit out code or strictly said: vomit code at you). Seaside is also one of the most brilliant ideas I've seen. To efficiently build apps with Seaside actually all you need is a basic knowledge of HTML and CSS and surprisingly no JavaScript - I didn't need it through the whole creation process. Less is better. ;) I find the idea about Smalltalk application image far superior than Java libraries and whats more, easier way to share applications than using jars. For example, I don't have to do anything with my application if I want run it on Android - just copy and run ( thanks to this project: http://code.google.com/p/squeakvm-tablet/ ). Can you do it in Java? You can't!
So lets get back to play. Last time I left, my blog comments reader was in the state where i had to manually add entries to collection, comments were looking ugly, there was no way to remove entry form collection, etc...
This time I wont show any code here (as a text). I decided to share whole one-click Pharo image where you can experiment with. Ill just describe what my objects do.
First, I'll show you the final look:
Well, at least it is not as scary looking as last one...
The minus sign removes entry , + button displays DPDialog, and U button updates comments.
DPDialog looks like this:
Some code:
A you can see its a jQuery dialog, simple, nothing fancy. Code under submitButton checks if user entered a valid URL (its valid if it can be read from). If its OK then DPRoot class adds blog entry to its internal list and then tries to parse comments from given URL.
Here is the graphical description of how tabs bar is created:
Updated DPComment rendering method:
This method checks if its instance variable (DPComments object sets it when creating new DPComment) is false or true naming div element accordingly, so its later possible to apply a proper CSS code.
Mam
już schemat jak wyciągnąć komentarze z dowolnego bloga, to teraz
czas na część wizualną. Tak jak poprzednio, najpierw z grubsza
opiszę poszczególne kroki. Film z podsumowaniem dam na końcu.
Jako, że film byłby dość długi gdyby pokazywać tworzenie kodu
to pokażę w nim tylko jak wygląda struktura pakietu i kod. Z
poprzedniej części mam już w zasadzie napisaną podstawę
pojedynczej funkcji, która odczyta komentarze z podanego adresu URL
i zwróci tablicę komentarzy. Najpierw jednak opiszę jak utworzyć
komponent w Seaside.
Otwieram
System Browser i zaczynam od utworzenia kategorii (paczki obiektów).
Prawy myszki na liście kategorii i klikam "Add category...".
Jako, że tworzę pod portal dobreprogramy.pl, utworzę kategorię
DobreprogramyAPI.
Tworzenie
komponentu głównego
Ten
komponent będzie odpowiadać za zawartość strony głównej. To
wszystko o czym będę pisać jest także dostępne dostępne w
dokumentacji
Seaside.. Kasą główną komponentów jest klasa WAComponent. Po
niej odziedziczę funkcjonalność na potrzeby mojej nowej klasy
"DPRoot":
WAComponent
subclass: #DPRoot
instanceVariableNames:
''
classVariableNames:
''
poolDictionaries:
''
category:
'DobreprogramyAPI'
Pierwsze
co trzeba zrobić, to utworzyć metodę "initialize":
initialize
super
initialize.
Metoda
"initialize" jest metodą wywoływaną zaraz po utworzeniu
obiektu, m.in. po wywołaniu metody "new". Dobrą praktyką
jest wywołać metodę "initialize" obiektu nadrzędnego za
pomocą metody "super". Zielona strzałka do góry, która
się pokaże obok nazwy metody mówi, że moja metoda nadpisuje
metodę z klasy nadrzędnej.
Kolejnym
krokiem będzie utworzenie metody, która wyświetli coś na stronie.
Na chwilę obecną dam jakiś prosty tekst byle zobaczyć, czy
działa. Metoda ta posiada określona nazwę, bo Seaside przeszukując
obiekty i wyświetlając zawartość szuka właśnie jej:
renderContentOn:html
html
paragraph:
'Alibaba'.
Jako,
że obiekt DPRoot jest obiektem głównym, trzeba zaznaczyć to
tworząc metodę, która poinformuje framework Seaside. W części
klasowej trzeba utworzyć metodę:
canBeRoot
^true.
Dzięki
niej Seaside będzie widzieć obiekt w przeglądarce aplikacji.
Rejestracja
komponentu jako aplikacji
W
przeglądarce przechodzę pod adres http://localhost:8080/config .
Klikam w Add na pasku menu. W pole wpisuję 'dp' i wybieram typ jako
Application. OK. W sekcji General na kolejnej stronie jako 'Root
class' wybieram DPRoot. Klik w Apply. Teraz przechodząc pod adres
localhost:8080/dp powinien się wyświetlić Alibaba... Jest. ;)
To
samo można zrobić wywołując w Workspace polecenie:
Dzisiaj postanowiłem pokazać jak w prosty sposób odczytać komentarze z bloga na portalu dobreprogramy.pl, który nie posiada usługi odczytu wiadomości za pomocą XML/JSON przez co trzeba parsować HTML. Dla niecierpliwych na końcu tekstu jest krótki film podsumowujący temat tego wpisu.
Odczyt zawartości strony internetowej robi się dość banalnie. Wystarczy w Workspace Pharo wpisać :
Potem umieścić kursor klawiatury za kropką (bądź zaznaczyć myszką całą linijkę kodu) i nacisnąć [Ctrl+p] ("Print it"). Powinna pojawić się zawartość z podanego adresu. Tekst, który się pojawia jest już zaznaczony, więc można nacisnąć klawisz Del by go usunąć.
Jako, że tekst strony już mamy, to potrzebny będzie parser HTML. Będzie potrzebny, bo wbudowany parser XML tonie w zupie tagów luźno zapodanego HTMLa, gdzie zdarzają się niedomknięte tagi, ew. tagi zapisane niezgodnie z dokumentacją i wtrącenia w postaci JavaScriptu czy CSS i inne śmieci.
Dodatkowe biblioteki Smalltalka są przechowywane w repozytoriach. Z takiego dziś skorzystam. Jest to repozytorium Squeaka (inna odmiana Smalltalka zgodna z Pharo).
Aby zainstalować bibliotekę trzeba wejść do przeglądarki Monticello (dostępne z menu myszki). Następnie trzeba kliknąć w "+Package" nadać dowolną nazwę, np. "Soup". Potem klik w "+Repository" i wybieram opcję HTTP. Modyfikuję adres, który się pojawi na szablonie na:
location: 'http://www.squeaksource.com/Soup'
Klikam OK, potem w "Open". Załaduje się lista wersji pakietów. Trzeba teraz wybrać ConfigurationOfSoup - najnowszą wersję , u mnie jest 37 i kliknąć Load. ConfigurationOfSoup to pakiet obiektów, które pobiorą najnowszą wersję zupki. Aby się dowiedzieć jak go uruchomić trzeba otworzyć System Browser i znaleźć ConfigurationOfSoup. Można to zrobić poprzez menu z Find, górną belkę, lub przewinąć listę na sam koniec. Najnowsze pakiety są widoczne zawsze na końcu. W drugim okienku, które jest zwane klasowym lub instancji trzeba kliknąć jeszcze raz nazwę ConfigurationOfSoup, a potem pytajnik. Pod pytajnikiem zwykle jest dokumentacja obiektu.
Widać tam dwie linijki. Jako, że kod Smalltaka można wykonać z każdego okienka, zaznaczam linijkę z wersją #stable i wykonuję "Do it". skrypt pobierze i skompiluje resztę biblioteki.
Teraz można pobrać zupę do obiektu już za pomocą samej biblioteki (oczywiście wszystko to wpisuję w Workspace):
zupa := Soup fromUrl: 'http://*dobreprog***.pl/aeroflyluby/Domowy-sposob-na-Diode,36474.html'. Tym razem użyłem prawdziwego adresu z bloga. Mam więc zupę z tagów i w tym momencie zaczyna się zabawa. Trzeba dowiedzieć się pomiędzy jakimi tagami trzymany jest blok komentarzy. Potem znaleźć pojedyncze wpisy, a następnie rozbić na:
adres logo: #img
nazwę komentującego: #nick
treść komentarza: #text
Zrobiłem małe rozeznanie i znalazłem id tagu, w którym te komentarze siedzą:
ctl00_phContentLeft_panUpdateComment
Niestety Soup nie ma możliwości wyciągania tagów po identyfikatorze. Trzeba sobie napisać swoją funkcję. Najlepiej to zrobić zrzynając już z istniejącej:
Oczywiście tworzymy ją w instancji klasy SoupTag w pakiecie Soup-Core. Widać w kodzie, że funkcja ta wywołuje jeszcze inną, z parametrem ifAbsent, która to jest główną (rozszerzoną funkcją) do wyciągania tagu. Jej nazwę też trzeba podmienić. W sumie tworzymy dwie funkcje:
Można użyć "Print it", które wyświetli treść tagu, lub dać "Do it" i podejrzeć obiekt root przez zaznaczenie go i użycie "Inspect it".
Teraz czas wyciągnąć pojedyncze pola komentarzy. Tutaj będzie trochę trudniej, bo id komentarzy zmienia się. To jest, id komentarzy jest w postaci: komentarz_124234. Niestety, Soup nie umożliwia wyciągania tagów z użyciem wyrażeń regularnych. Trzeba będzie sobie poradzić. I znów trzeba zmodyfikować nasze nowe funkcje:
Mając te funkcje teraz mogę wyciągnąć komentarze do tablicy: komentIDregX := 'komentarz_[0-9]+'. komentSoups := root findAllTagsByIDregX: komentIDregX.
Super. ;) Teraz utworzę obiekty na tagi komentarzy, nicków i logo komentujących:
Na filmie można zauważyć, że nie tworzę funkcji przed wykonaniem kodu. Środowisko Smalltalka informuje mnie, że takowej nie ma i pozwala utworzyć brakującą. Po utworzeniu funkcji można kliknąć "Proceed" i środowisko zachowa się jakby nic nie było. W ten sposób także bardzo łatwo i szybko pisze się testy w konwencji TDD - test first. To nie żadna nowość. Smalltalk był pierwszym szeroko dostępnym środowiskiem, które było wyposażone w bibliotekę do testów jednostkowych xUnit (jUnit - wersja na Javę). To, czym dziś ludzie się zachwycają i biorą za nowość, było używane grubo ponad 30 lat temu.
W kolejnej części o tym jak to wszystko wyświetlić na stronie WWW.
i webowy framework zbudowany na jego podstawie: Seaside. W oparciu o nowoczesną, darmową implementację Pharo
Aby lepiej zrozumieć wpis najlepiej zacząć od ściągnięcia Seaside 3.0.7 w wersji One-Click. W katalogu Seaside.app, po rozpakowaniu będą skróty do uruchomienia na trzech systemach: Linux, Mac i Windows.
Krótko o Smalltaku
dla tych, co im się nie chce czytać Wiki + moje własne obserwacje:
Smalltalk to dynamiczny
język zorientowany obiektowo, zaprojektowany z myślą o zintegrowanym
środowisku programowania. Smalltalk nie posiada struktury plików z
jakich korzystają inne języki programowania. Wszystko żyje w obrazie,
który jest obsługiwany przez maszynę wirtualną. Rozwiązanie bardzo
podobne do systemu operacyjnego uruchomionego w Virtual Box. Daje to m.
in. możliwość zapisania stanu w jakim jest w danej chwili - tzw.
snapshot. Język jest tak prosty, że wdraża się go w OLPC jako pomoc
edukacyjną dla dziesięciolatków. Na dzień dzisiejszy popularne i darmowe odmiany Smalltalka : Pharo, Squeak (OLPC), GNU Smalltalk,
działają tylko na jednym rdzeniu. To jest spuścizna po latach '80 i
'90, gdzie nikt nie przejmował się skalowalnością horyzontalną na jednej
maszynie, bo procesory były wtedy jednordzeniowe. Mogę powiedzieć, że i
dziś nie jest to żadną wadą. System webowy w oparciu o Seaside tworzy
się najczęściej w oparciu o Apache, który zajmuje się rozsyłaniem żądań
do maszyn wirtualnych, które działają na różnych portach i różnych
procesorach. W systemie Linux łatwo wskazać na jakim rdzeniu ma się
uruchomić dany program. W przypadku awarii maszyny po prostu podnosi się
kolejną na tym samym porcie. Wszystko to w zwykłym skrypcie powłoki systemowej. Smalltalk
to język, w którym nie wywołuje się funkcji tak jak w językach
imperatywnych, a jedynie przekazuje wiadomości do obiektów (wysyła).
Dość ciekawa sprawa, bo kompilator wysyła wiadomość do obiektu, a w
momencie, gdy obiekt i klasy macierzyste nie obsługują komunikatu
pokazuje się wyjątek 'doesNotUnderstand' ('nie rozumiem'). Nie jest to
wyjątek taki jak w Javie, czy C, że program podnosi ręce do góry i
często kończy działanie. Tutaj pokazuje się okienko z ostrzeżeniem i
możliwością dodania obsługi komunikatu do obiektu. Po edycji dajemy
restart i program działa dalej jakby nic. Ta wbrew pozorom błaha funkcja
pozwala na pisanie testów w konwencji TDD - 'test first'. Smalltalk
posiada jedną z najczystszych składni językowych. Nie najkrótszą, a
najczystszą. Jest bardzo zbliżony w wyglądzie do zwykłego angielskiego.
myArray at: 1 put: 'tekst'.
Sentencję
kończy się kropką (w C średnikiem). Komentarz umieszcza się w
"podwójnym cudzysłowie". Łańcuchy znaków łączy się przecinkiem: napi := 'aaaa','bddd'. W
języku występują tzw. bloki. Są to kawałki kodu ujęte w nawiasy
kwadratowe. Służą do przekazywania wykonania kodu w argumencie
wiadomości lub mogą być zwracane. Posiadają także argumenty, np.:
elements := 5.
tab := Array new: elements.
1 to: elements do: [ :a | tab at: a put: (a * a)]
Jak to działa: - utwórz obiekt SmallInteger (32 bit) z wartością 5 - utwórz tabelę tab jako wektor 5 obiektów (wszystkie mają wartość nil) - dla każdego elementu w tablicy przypisz kwadrat wartości jego indeksu
Jak
widać na powyższym obrazku obiekt można podejrzeć zaznaczając go i
klikając w menu Inspect it. Okienko inspektora posiada na dole wygodne
miejsce, w którym można wpisywać kod i np. w ten sposób modyfikować
obiekt.
Przydatne skróty klawiszowe:
Ctrl + s, - (Accept) zapis zmian
Ctrl + d, - (Do it) wykonanie kodu
Ctrl + p, - (Print it) wykonanie kodu i pokazanie wyniku na końcu zaznaczonego bloku
Ctrl + i, - (Inspect it) - podgląd obiektu
Niektórzy zastanawiają się w tym momencie co to są te pola z dwukropkiem np. 'put:'. Są
to tzw. settery - funkcje, które odpowiadają za odebranie i przeróbkę obiektu
podanego jako parametr. Setter obiektu może przyjąć tylko jeden
parametr. Bez dwukropka to getter, czyli funkcja, która zwraca wartość i/lub wykonuje działanie na obiekcie.
Zmienne instancji obiektu (są także zmienne klasowe - odpowiednik static
w Javie) nie są widoczne z zewnątrz, stąd użycie getterów i setterów. Obiekty są dzielone w IDE na dwie strony: część instancji (instance) i część klasową (class). Strona instancji jest miejscem, gdzie przechowuje się zmienne i funkcje, które mogą się zmieniać z każdą kopią obiektu. Część klasowa jest wspólna dla wszystkich kopii, a także jest dostępna bez potrzeby tworzenia nowego obiektu. Klasa w Smalltalku ma postać: Object subclass: #MojaKlasa instanceVariableNames: 'zmienna_prywatna1 zmienna_prywatna_2' classVariableNames: '' poolDictionaries: '' category: 'Moja-Kategoria' Zwykle setter ma postać:
zmienna_prywatna1: param
zmienna_prywatna1 := param.
a getter:
zmienna_prywatna1
^ zmienna_prywatna1 Pierwsza linijka to jednocześnie nazwa funkcji i parametry. Zamiast pisać znane z C: typ getA(void) , pisze się tylko a . Zamiast pisać void setA(int a)pisze się a: aInt. Znaczek ^ oznacza zwrócenie obiektu - słowo return z Javy/C. Średnika używa się do powtórzeń operacji na tym samym obiekcie. Zamiast pisać tak:
tablica_do_liczenia_sensu_zycia = Array new: 3.
tablica_do_liczenia_sensu_zycia at: 1 put: 'a'.
tablica_do_liczenia_sensu_zycia at: 2 put: 'y'.
tablica_do_liczenia_sensu_zycia at: 3 put: 'z'.
można pisać tak:
tablica_do_liczenia_sensu_zycia at: 1 put: 'a';
at: 2 put: 'y';
at: 3 put: 'z'.
Każdy
obiekt można edytować. I to w sposób jaki jest to nieosiągalny dla
innych języków. Mam tu na myśli przede wszystkim popularną rodzinę
języków C. Przykładowo mogę dodać brakującą funkcjonalność do klasy
String - nie do pomyślenia w Javie. Jako, że mamy do czynienia tylko z obiektami i przesyłaniem komunikatów w tym języku nie ma operatorów. Przykład:
wynik_działania := 1 + 2 * 3.
wynik_działania -> 9
Działa
to w ten sposób: do obiektu '1' prześlij komunikat '+' i obiekt '2'. W
tym momencie obiekt '1' zwraca obiekt : '3' do którego przesyłany jest
komunikat '*' i obiekt '3'. Aby dostać prawidłowy wynik trzeba iloczyn wziąć w nawias. Kod w nawiasach jest wykonywany jako pierwszy.
Podstawowe obiekty liczbowe
Podstawowym obiektem, z którego wywodzą się inne (poprzez dziedziczenie) jest obiekt Number. Zawiera on podstawową obsługę wiadomości typu '+' '-' '/' 'negated' 'abs', 'even', 'odd',konwersję typów, testowanie, itp. Kolejnym obiektem jest typ całkowity Integer. Nie jest jednak bezpośrednio używany, a posłużył do utworzenia dwóch klas pochodnych: SmallInteger i LargeInteger. SmallInteger posiada rozmiar 32-bit z wartościami od -2^30 do 2^30-1. LargeInteger przejmuje na siebie odpowiedzialność w przypadku sytuacji gdy jego wcześniejszy kolega próbuje 'przekręcić licznik'. LargeInteger jest ograniczony ilością dostępnej pamięci. Przykład: a := 400000000 "-> SmallInteger" a := a * 400000000 "-> zamiana typów" a -> 80000000000000000 "-> LargeInteger" Kolejny typ to typ ułamkowy Fraction. Powstaje gdy w czasie dzielenia biorą udział wartości całkowite. Użycie typu ułamkowego nie powoduje utraty precyzji związanej z zaokrągleniem. a := 1 / 2 a -> (1/2) a asFloat a -> 0.5 Float to typ zmiennoprzecinkowy zgodny ze standardem IEEE-754 . Zakres do +/- 10^307
Więcej można zobaczyć przeglądając paczkę Kernel-Numbers.
Instrukcje warunkowe i pętle
W Smalltalku nie ma instrukcji warunkowych i pętli. Są tylko komunikaty i operacje na blokach. Np. znana z C instrukcja: if ( a > 0 ) a = a * 5; w Smalltalku przyjmuje postać: ( a > 0 ) ifTrue: [ a := a * 5 ]. - do zmiennej a przesyłana jest wiadomość '>' z parametrem 0. Obiekt a następnie wysyła nowy obiekt Boolean z ustawioną wartością true lub false. Do tego obiektu wysyłana jest wiadomość ifTrue: z blokiem [ a := a * 5 ] jako parametr. Jeżeli warunek jest spełniony obiekt Boolean wykona blok.
Pętle są realizowane w podobny sposób, np.:
[ a > 0 ] whileTrue: [ a := a - 1 ].
Tym razem to blok [ a > 0 ] otrzymuje komunikat whileTrue: z blokiem [ a := a - 1 ] jako parametr. Trochę więcej o blokach:
Bloki
Blokiem jest kod zapakowany w nawiasy kwadratowe. Np.:
[ 1 + 2 ]
Blok zawsze zwraca sam siebie, chyba, że poprosimy o wartość (value):
blok := [ 1 + 2 ].
blok -> [ clojure ] in UndefinedObject
blok value. -> 3
Blok może przyjmować parametry:
sqrt := [ :a | ( a >= 0) ifTrue: [a sqrt]
ifFalse: [Complex new real: 0 imaginary: (a abs sqrt)]].
sqrt value: 4. -> 2
sqrt value: -4. -> 0 + 2i
suma2arg := [:a :b | a + b ].
suma2arg value: 1 value: 2. -> 3
[:a :b | a + b ] value: 1 value: 2. -> 3
Kolejny
szczegół: obiekt może mieć tylko jedną klasę bazową. Nie ma
wielokrotnego dziedziczenia. Interpreter i kompilator Smalltaka są
napisane w Smalltalku. Pozwala to uruchomić maszynę wirtualną wewnątrz
maszyny wirtualnej. To tak jakby się dało uruchomić Javę w Javie.
Praca w Smalltalku jest pracą z całym systemem. Nie ma tu podziałów na programy. Programem, jest wynik modyfikacji systemu. Smalltalk
nie potrzebuje systemu operacyjnego by działać. Są wersje edukacyjne
dla dzieci, które bootują do Smalltalka. Jako ciekawostkę podam, że w
innych, bardziej rozwiniętych krajach naukę programowania dla dzieci zaczyna się od Smalltalka. U nas zaczyna się od Logo. Nie wiem czyj to wymysł, ale znając ten kraj można się było tego spodziewać. Tworzenie
nowej klasy w Smalltalku to wysłanie komunikatu do obiektu, po którym
chcemy dziedziczyć lub do podstawowego obiektu 'Object'. Nie tworzy się
żadnego pliku tak jak w Javie. Oczywiście utworzony obiekt można zapisać
do pliku, np w celu archiwizacji. I ostatnia dość ciekawa cecha
środowiska, której nie spotkałem w żadnym języku, czy IDE, czyli:
wykonywanie kodu gdziekolwiek i kiedykolwiek. To cecha, która pozwala
bardzo szybko testować kod. Słabą namiastką tego są tak zwane REPL w
innych językach, czyli interaktywne konsole, gdzie kawałek kodu się
kopiuje bądź wysyła i on tam jest wykonywany. Przykład: (Transcript to odpowiednik stdout).
Gdy chcę zobaczyć wynik operacji w okienku, którym piszę, używam opcji 'Print it'. Np.:
Teraz trochę o Seaside
Małe intro na początek:
Widać
trochę machania myszką, ale to efekt usunięcia ścieżki dźwiękowej,
która nie nadawała się do publikacji. Seaside jest oparty o model
komponentowy. Obiektowość Smalltalka bardzo ułatwia ich tworzenie i
edycję. Seaside pozbywa się także lub stara się pozbyć instrukcji GOTO.
Znanej i pogardzanej instrukcji, która została wypędzona w programowaniu
klasycznym dzięki licznym egzorcyzmom, a która w programowaniu webowym
manifestuje się w tagu HREF. W Seaside kliknięcie w odnośnik na stronie
powoduje wysłanie wiadomości do obiektu, który decyduje jak
odpowiedzieć. Seaside nie wykorzystuje szablonów. Tutaj wszystko tworzy
się w jednym języku. Chodzi o to, by jak najbardziej zbliżyć się w stylu
tworzenia do aplikacji desktopowych. Seaside posiada wbudowane i przezroczyste wsparcie dla jQuery i jQuery UI. Ajax jest bardziej bezbolesny niż w Ruby on Rails. Tutaj w ogóle można nie dotykać JavaScriptu. Ogólne
podejście z tego, co zauważyłem zamyka się w tworzeniu rdzenia
aplikacji w Seaside, a reszta treści: multimedia, css i inny statyczny
kontent serwuje się oddzielnie. Oczywiście można wciągnąć CSS i obrazki
do obrazu aplikacji, ale o ile nie jest to pojedyncza aplikacja, to
będzie tylko zajmować niepotrzebnie RAM. W Seaside dzięki użyciu
kontynuacji użycie przycisku przeglądarki 'Wstecz' i 'Następny' nie
powoduje zaburzenia w działaniu aplikacji. Jako ciekawostkę dodam, że jest już maszyna wirtualna, która działa na Androidzie. Działa, bo sprawdziłem. ;) Polecam tablet 10 cali, bo będzie ciężko z rozdzielczością. Najlepiej ściągnąć i zainstalować samą maszynę wirtualną,
a później skopiować plik .image i .changes na kartę SD. Aplikacja sama
znajdzie pliki i wyświetli listę, z której można uruchomić obraz
Seaside. To rozwiązanie z Androidem jest o tyle dobre, że ten sam
obraz można uruchomić na wielu systemach bez kompilacji. Nawet Java i
inne języki tak nie potrafią. Przede wszystkim to zaleta Open Source,
które to dzięki dostępności kodu źródłowego pozwoliło skompilować
maszynę na Androida. BTW. Ktoś czeka aż Java od Oracle pojawi się na
Androidzie? ;)