"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

2013-01-26

Smalltalk and Seaside part 4.

(c) Robert Tinney
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.

CSS code is kept within object called DPCSSLibrary. Adding library is simple, just follow this: http://book.seaside.st/book/in-action/serving-files/filelibraries/creating .

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.

Whole application as One-click archive: https://dl.dropbox.com/u/71388328/DP-OneClick.zip

2012-12-20

Clojure z Emacs i Leiningen 2


"Leiningen is for automating Clojure projects without setting your hair on fire."
- czyli jak krok po kroku przygotować Emacsa do współpracy z Clojure i Leiningen. Podam też sposób jad dołączać własne biblioteki do projektu przy użyciu Maven.


Instalacja będzie prowadzona na systemie Ubuntu 12.10 x64. Jeżeli ktoś zauważy jakieś niezgodności, to proszę o komentarz.

Leinngen jest menedżerem pakietów dla projektów Clojure.  Ma on ułatwić pracę z zależnościami. Jednak nie ma tak pięknie jak można by się spodziewać na pierwszy rzut oka. Jeżeli pakietów nie ma w repozytorium Clojure, to trzeba stworzyć sobie własne. O tym na końcu.

Krok 1: Instalacja Emacs 24.x  i Leiningen 2

Najprościej jest to zrobić z terminala tymi poleceniami:

sudo apt-get install emacs24
wget https://raw.github.com/technomancy/leiningen/preview/bin/lein
chmod 755 lein
sudo mv lein /usr/bin
lein self-install


Krok 2: Konfiguracja Emacs 

Poleceniem z konsoli uruchmić:

emacs ~/.emacs.d/init.el

Wkleić to i zapisać:

(require 'package)
(add-to-list 'package-archives
             '("marmalade" . "http://marmalade-repo.org/packages/"))
(package-initialize)

Potem (M-x to klawisz Alt+x, po wciśnięciu wpisać resztę polecenia):

M-x eval-buffer

Krok 3: Instalacja wtyczki clojure-mode

Teraz trzeba załadować listę pakietów. Nie wiem dlaczego tak, ale sposób podany na Github u mnie nie zadziałał, więc metodą prób i błędów tak mi się udało.

Trzeba wyświetlić listę pakietów poleceniem:

M-x package-list-packages 

Znaleźć pakiet clojure-mode. Kliknąć [Install] i zamknąć Emacs.

Krok 4: Przykładowy projekt

Utworzyć projekt poleceniem w terminalu:

lein new clojure-projekt1

Następnie wejść do katalogu projektu i edytować plik konfiguracji project.clj. Emacs by widzieć projekt musi być uruchamiany z katalogu głównego projektu:

cd clojure-projekt1
emacs project.clj

Wkleić tę linijkę:

:plugins [[lein-swank "1.4.4"]]

nad linijkę z :dependencies

W Emacs uruchomić serwer Swank poleceniem:

M-x clojure-jack-in

I voila ;). Powinien się pokazać REPL.

Przykładowe polecenia przydatne przy współpracy z Emacs i SLIME (C - Ctrl, M - Alt):



  • M-. : Skok do definicji var-a
  • M-TAB lub C-c TAB: Autouzupełnianie symbolu w miejscu kursora
  • C-x C-e: Wykonaj wyrażenie pod kursorem
  • C-c C-k: Skompiluj bieżący burfor
  • C-c C-l: Załaduj bieżący bufor i wymuś przeładowanie wymaganych przestrzeni nazw
  • C-M-x: Skompiluj najbardziej zewnętrzne wyrażenie pod kursorem
  • C-c S-i: Podejrzyj wartość
  • C-c C-m: Wykonaj macroexpand dla wyrażenia pod kursorem
  • C-c C-d C-d: Przeszukaj dokumentacje dla var-a
  • C-c C-z: Przełącz się pomiędzy buforem Clojure a REPL
  • C-c M-p: Zmien przestrzeń nazw w REPL dla odpowiedniej z bieżącego bufora
  • C-c C-w c: Wyświetl funkcje wywołujące bieżącą funkcję



  • Dodawanie własnych bibliotek do projektu

    Najpierw trzeba zainstalować Maven2:

    sudo apt-get install maven2

    Następnie trzeba mieć jakąś bibliotekę w bieżącym katalogu, np.  ormlite-core-4.42.jar.
    Poleceniem poniżej dodaje się taką bibliotekę do lokalnego repozytorium mavena:



    mvn install:install-file \
     -Dfile=ormlite-core-4.42.jar \
     -DgroupId=self \
     -DartifactId=ormlite-core \
     -Dversion=4.42 \
     -Dpackaging=jar \
     -DgeneratePom=true



    Po pozytywnym przetworzeniu powinien pojawić się napis BUILD SUCCESSFUL. Następnie do pliku projektu w sekcji :dependencies trzeba dodać linijkę i zapisać:

    [self/ormlite-core "4.42"]

    Następnie w katalogu projektu w terminalu dać polecenie:

    lein deps

    I to wszystko. ;)




    2012-12-17

    Light Table : Factorial fun

    Niedawno na kickstarter.com wystartował projekt IDE dla języka Clojure przez człowieka o nazwisku Chris Granger. Dzisiaj zaprezentuję działanie części edytora: Instarepl. Jest to moduł, w którym można testować kod. Ma tę przewagę nad innymi REPL-ami, że widać wyniki działania poszczególnych poleceń. Nie tylko po wykonaniu polecenia, ale też aktualizację innych w historii jeżeli wcześniejszy kod coś pozmienia. Jak dla mnie bajka. Dawno czegoś takiego nie widziałem. Ekran Instarepl jest podzielony na dwa obszary. Z lewej strony mamy edytor kodu, z prawej kod, i wyniki działanaia. W wynikach przez ciała funkcji zamiast symboli przewijają się dane i widać jak przechodzą przez kolejne polecenia. W przypadku pętli zauważyłem, że widać tylko ostatnią iterację.

    Light table można pobrać z tej strony. Wymagane jest JDK Javy. Przy pierwszym uruchomieniu program pobiera niezbędne biblioteki. Proces ten trwa dość długo, więc nie należy się przerażać, że aplikacja "wisi". Najlepiej za pierwszym razem uruchomić w konsoli poleceniem "java -jar launcher.jar",  bo będzie widać przebieg instalacji. Program także doinstalowuje biblioteki przy przejściu do części projektowej : Table.

    Widok główny Light Table


    Widok Instarepl

    Jak widać na zrzucie ekranu Instarepl wyjście funkcji, które piszą do standardowego wyjścia (terminal/konsola) umieszczane jest na końcu bloku wyników pod linijką "Output:".

    Część Table wygląda tak:


    Z lewej pokazują się bloki funkcji, które można edytować. Polecam zapoznać się ze skrótami klawiszowymi, bo w widoku nie ma żadnych przycisków, czy menu z komendami.

    Z prawej strony ekranu na górze w lewym okienku jest widok przestrzeni nazw. Każda przestrzeń nazw zawiera funkcje, które wyświetlają się w prawym okienku. Niżej na czerwono jest brudnopis do testowania kodu, a jeszcze niżej w zielonym polu pojawiają się wyniki działania funkcji z brudnopisu lub programu. Widać, że autor wzorował się na browserze kodu Smalltalka. Według mnie to dobre rozwiązanie, a przydałaby się jeszcze możliwość podziału funkcji na własne kategorie.

    Dodam, że to IDE jest w wersji alpha, więc zawiera bugi i nie jest tak rozbudowane jak można by sobie życzyć (np. brak refaktoringu).  Jednak w porównaniu do wtyczek Clojure do Eclipse czy Intellij IDEA program ten to ogromny krok naprzód i z przyjemnością się z nim pracuje.

    Na koniec załączam gwóźdź programu, czyli film z testowania Instarepl, na przykładzie realizacji funkcji liczącej silnię z testami ze strony 4Clojure.


    Pod koniec filmu mam małą przywieszkę z tego względu, że nie zauważyłem błędu w funkcji, a środowisko czasem lubi się przywiesić. Np. w wyniku edycji funkcji, program  wpadnie w nieskończoną pętlę. Ten fakt można poznać po zwiększonym zużyciu procesora, lagowaniu interfejsu i braku reakcji na zmiany kodu (factorial3 zawsze pokazywał 1). To mnie wprowadziło w błąd.

    Projekt Light Table jest bardzo ciekawy, więc mam nadzieję, że nie tylko mnie przypadnie do gustu. :)

    2012-12-11

    Smalltalk : Seaside z użyciem Pharo, cz.3

    (c) Robert Tinney

    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:

    WAAdmin register: DPRoot asApplicationAt: 'dp'.

    2012-12-08

    Clojure Easy 2


    "The computing scientist’s main challenge is not to get confused by the complexities of his own making."
     --- E. W. Dijkstra

    Czas na kolejną część zagadek ze strony 4clojure.com.


    Problem 9: Fibonacci Sequence


    Zadanie polega na napisaniu funkcji, która zwróci n pierwszych elementów ciągu Fibonacci.
    Funkcja w E1 bierze za parametr szukaną liczbę elementów ciągu. Dla pierwszego i drugiego elementu zwracane są wartości bezpośrednio. Dla dalszych obliczane są kolejne elementy na podstawie dwóch pierwszych elementów listy acc. W pętli zwiększany jest licznik c i dodawana do początku listy acc suma dwóch poprzednich elementów. Trzeba pamiętać, że funkcja conj dodaje elementy do list od lewej strony, dlatego aby uzyskać poprawną odpowiedź trzeba acc odwrócić przed zwrotem. Odpowiedź E2 jest z wykorzystaniem wektora.

    T1: (= (__ 3) '(1 1 2))
    T2: (= (__ 6) '(1 1 2 3 5 8))
    T3: (= (__ 8) '(1 1 2 3 5 8 13 21))

    E1: (fn fiblist [n]
          (let [a '(1) b '(1 1)]
              (cond (= n 1) a
                   (= n 2) b
                   :else (loop [c 2 acc b]
                        (if (>= c n)
                            (reverse acc)
                            (recur (inc c)
                                   (conj acc (+ (first acc) (second acc)))))))))

    E2 : (fn fiblist [n]
            (let [a [1] b [1 1]]
                (cond (= n 1) a
                      (= n 2) b
                      :else (loop [c 2 acc b]
                                 (if (>= c n)
                                  acc
                                 (recur (inc c) 
                                        (conj acc (+ (acc (- c 1)) (acc (- c 2))))))))))


    Problem 10: Maximum value


    Zadanie polega na napisaniu funkcji, która zwróci parametr o największej wartości.
    Haczyk: nie można użyć funkcji max i max-key.

    Zanim podam wynik wyjaśnię na przykładzie jak to jest z przekazywaniem argumentów do funkcji:

    => ((fn f [a b & more] more) 1 2 3 4)
    (3 4)
    => ((fn f [a & more] more) 1 2 3 4)
    (2 3 4)
    => ((fn f [& more] more) 1 2 3 4)
    (1 2 3 4)

    Przed znakiem & argumenty przypisywane są jak leci 1 do a, 2 do b, itd. Po znaku & reszta argumentów traktowana jest jako lista. W przypadku braku identyfikatorów przed & wszystkie argumenty traktowane są jako lista. Oczywiście a, b, more i args mogą mieć dowolne nazwy.

    T1: (= (__ 1 8 3 4) 8)
    T2: (= (__ 30 20) 30)
    T3: (= (__ 45 67 11) 67)

    E1:(fn maxx [& args]
                (loop [e (first args)
                       re (rest args)
                       mx e]
                      (if (empty? re)
                           mx
                          (recur (first re) (rest re) (if (> e mx) e mx)))))

    Problem 11: Get the Caps


    Zadanie: Znaleźć funkcję, która z podanego łańcucha znaków zwróci łańcuch zawierający tylko wielkie litery. Łańcuchy tekstowe w Clojure to zwykłe String z Javy, więc będą na nich działać funkcje z Javy. Aby dostać listę wielkich liter trzeba przefiltrować tekst za pomocą funkcji filter, którą następnie trzeba złączyć w napis przy użyciu funkcji str i apply.


    T1: (= (__ "HeLlO, WoRlD!") "HLOWRD")
    T2: (empty? (__ "nothing"))
    T3: (= (__ "$#A(*&987Zf") "AZ")

    E1: #(apply str (filter (fn [c] (Character/isUpperCase c)) %))

    Problem 12: Intro to some


    Należy podać wynik działania funkcji some. Funkcja ta bierze za pierwszy parametr funkcję, która zwraca logiczną prawdę lub fałsz , oraz kolekcję, której elementy będą przekazywane do tej funkcji. Jeżeli wynikiem działania funkcji będzie true to zostanie zwrócony pierwszy element dla którego warunek został spełniony.


    T1: (= __ (some #{2 7 6} [5 6 7 8]))
    T2: (= __ (some #(when (even? %) %) [5 6 7 8]))

    E1: 6

    Problem 13: Duplicate a sequence


    Zadanie polega na napisaniu funkcji, która powtórzy elementy sekwencji dwukrotnie.

    T1: (= (__ [1 2 3]) '(1 1 2 2 3 3))
    T2: (= (__ [:a :a :b :b]) '(:a :a :a :a :b :b :b :b))
    T3: (= (__ [[1 2] [3 4]]) '([1 2] [1 2] [3 4] [3 4]))

    E1: #(if (empty? %1) []
             (loop [a (first %1) r (rest %1) ac [a a]]
                  (if (empty? r) ac
                      (let [s (first r)]
                           (recur s (rest r) (conj ac s s))))))

    E2: #(interleave %1 %1)


    Problem 14: Implement range


    Zadanie polega na napisaniu odpowiednika funkcji range .

    T1: (= (__ 1 4) '(1 2 3))
    T2: (= (__ -2 2) '(-2 -1 0 1))
    T3: (= (__ 5 8) '(5 6 7))

    E1: (fn [s e]
            (loop [c s acc []]
                 (if (>= c e) 
                  acc
                 (recur (inc c) (conj acc c)))))