JRuby 1.1 final

Posted by Jarosław Zabiełło Sun, 30 Mar 2008 22:51:00 GMT

Jest w końcu oczekiwana wersja stabilna JRuby 1.1. W chwili pisania tej notki, nie ma o tym jeszcze informacji na stronie głównej, ale jest już do pobrania. To bardzo dobra wiadomość, bo JRuby 1.1 już w wersjach rozwojowych był znacznie szybszy od poprzedniej 1.0, a w wielu testach bił wydajnością nie tylko Ruby 1.8.6 i 1.9, ale także Pythona 2.5.1. Dzięki JRuby webowe frameworki takie jak Rails czy Merb uzyskują pełny dostęp do bibliotek Javy i tym samym jakiekolwiek uwagi co do (nie)dojrzałości bibliotek Rubiego stają się nieistotne.

Tags , ,  | 1 comment

Django on Jython

Posted by Jarosław Zabiełło Tue, 08 Jan 2008 03:34:00 GMT

Pythonistas chyba pozazdrościli możliwości odpalenia Railsów w JRuby. Trwają prace nad uruchomieniem frameworka Django w Jythonie, implementacji Pythona w czystej Javie.

Read more...

Tags , , ,  | 6 comments

Jest w końcu Java 6 dla Leoparda

Posted by Jarosław Zabiełło Fri, 30 Nov 2007 11:34:00 GMT

Zarówno Apple jak i Sun się ociągają z wypuszczeniem Javy 6 dla Leoparda (tj. Mac OS-X 10.5). W internecie można znaleźć trochę wkurzonych osób. Niektórzy chcą nawet słać petycje do Apple aby coś z tym zrobili.

Read more...

Tags , , ,  | 3 comments

JRuby 1.0

Posted by Jarosław Zabiełło Wed, 13 Jun 2007 09:24:00 GMT

Ukazała się finalna wersja JRuby 1.0 – implementacji języka Ruby w czystej Javie. Zespół JRuby zachęca do testowania Railsów na JRuby. Z tego co pamiętam, to wersji finalnej JRuby zapowiadano włączenie optymalizacji kodu. Zanim zatem wyjdzie w końcu Ruby 2.0, JRuby on Rails ma już teraz szansę przełamać stereotyp o słabszej wydajności Rubiego. Szczególnie może to mieć duże znaczenie dla platformy Windows, bo Ruby na systemach POSIX (Linux, BSD, Mac OS X) pracuje znacznie wydajniej.

Dla miłośników Mac OS X jest dostępny artykuł pokazujący jak skonfigurować całe środowisko Javy dla Rail. Ale do pracy z Rails, najprościej użyć Netbeans 6 który od jakiegoś czasu pozwala1 na uruchamianie Railsów na JRuby (można wybrać sobie opcję JRuby lub klasyczny CRuby).

Zobacz też artykuł “JRuby and the Java Platform” jaki ukazał się wczoraj na stronie firmy Sun.


1 Szkoda, że NB6 nie ma jeszcze zaimplementowanego kolorowania i podpowiadania helperów dla Haml. Jak ktoś ma siły i czas to tu jest opis jak dodać kolorowanie dla jakiegoś innego języka.

Tags , , ,  | 14 comments

Jython 2.2beta1

Posted by Jarosław Zabiełło Fri, 09 Feb 2007 21:51:00 GMT

Pogłoski o przedwczesnej śmierci Jythona (javowej implementacji Pythona), są chyba przesadzone. Właśnie wyszła kolejna wersja 2.2 beta1. Instalacja:

java -jar jython_installer-2.2b1.jar

Tags , ,  | 4 comments

Serwery kontynuacyjne - przyszłość webu

Posted by Jarosław Zabiełło Wed, 22 Nov 2006 01:26:00 GMT

Każdy, kto zajmował się kiedykolwiek programowaniem stron internetowych wie, że protokół HTTP jest bezstanowy. Innymi słowy, każde przeładowanie strony internetowej generuje kompletnie niezależne wywołanie serwera i nie ma on możliwości aby powiązać wywołanie z konkretnym klientem. Aby temu zaradzić, wprowadzono prostą sztuczkę. Wprowadzono tzw. mechanizm cookies (dosł. ciasteczek), malutkich plików które serwer może wysyłać do klienta (tam i z powrotem). Ten unikalny identyfikator (sesji) można też trzymać w adresie URL, ale ta metoda jest zdecydowanie odradzana z powodu łatwości nadużyć (łatwo wykraść taki numer i podszyć się pod autora) Od strony klienta jedynym wymaganiem jest aby nie blokował sobie w przeglądarce mechanizmu cookies.

Serwer znając identyfikator sesji, może związać z nim szereg danych, np. aktualną listę zamawianych produktów włożonych do koszyka w sklepie internetowym. Dane te zwykle trzyma się w bazie lub w pamięci operacyjnej. Oczywiście czas życia danych jest ustalany przez serwet i w normalnym wypadku są one cyklicznie kasowane. (Z tym “normalnym” wypadkiem to różnie bywa, bo wiele frameworków tego nie zapewnia i trzeba samemu o to zadbać)

Mechanizm sesji jest dosyć wydajny, bo zapytania wysyłane do serwera mogą być obsługiwane asynchronicznie, czyli bez żadnych blokad w oczekiwaniu na ukończenie obsługi zapytania. Przez lata “stanowość” protokołu HTTP zapewniało się właśnie za pomocą sesji.

Ten mechanizm ma jednak swoje wady. Największą jest złożoność jaką wprowadza po stronie skryptów na serwerze. (I to nie jest wcale taka trywialna sprawa jak to można było zaobserwować u developerów frameworka CherryPy którzy miesiącami nie potrafili napisać stabilnie działającego mechanizmu obsługi sesji.)

Kontynuacje

Kompletnie innym podejściem jest skorzystanie z tzw. mechanizmu kontynuacji. Mechanizm ten podobny jest do gracza, który w dowolnym momencie może zapisać i wczytać stan gry. Kontynuacje zapewniają taki mechanizm zapisu stanu programu aby w dowolnym momencie później go przywrócić. Niektórzy porównują to do maszyny czasu, bo można w dowolnym momencie przywrócić wcześniejszy stan aplikacji.

Zalet z takiego podejścia jest wiele. Weźmy np. sprawę obsługi “nieśmiertelnego” przycisku Back w przeglądarce. Obsługa tego przycisku sprawia spore problemy. Programiści najchętniej w ogóle by go zablokowali. W wypadku kontynuacji ten problem w ogóle nie istnieje. Kliknął ktoś Back aby cofnąć się do poprzedniej strony? Żaden problem. Serwer przywróci cały wcześniejszy stan aplikacji bez żadnego problemu. I to w sposób przezroczysty dla programisty! Programowanie stron webowych staje się znacznie prostsze, niczym tworzenie aplikacji desktopowej włącznie z debugowaniem krok po kroku. Wg różnych prognoz, tak będziemy programować serwisy internetowe za kilka lat!

Sama idea nie jest najwyraźniej jeszcze powszechnie znana, skoro w rozmowie z developerami irlandzkiego Google, zaskoczyłem ich informacją na ten temat. Kto wie, czy Google mocniej nie zainwestuje w Rubiego (aktualnie mocno korzysta z Pythona, który jest podstawą większości ich skryptów).

Smalltalk i Seaside

Smalltalk jest bardzo starym językiem bo jego początki sięgają lat 70-tych. Od zawsze był językiem w pełni obiektowym i dynamicznym. To m.in. Smalltalk pierwszy wymyślił wirtualną maszynę której to ideę później skopiowali twórcy Javy. W Smalltalku napisano najlepszy (w chwili obecnej) na świecie serwer kontynuacyjny – Seaside.

Czy to spowoduje renesans na Smalltalka? Osobiście w to wątpię. Główną wadą Smalltalka jest jego monolityczność i hermentyczność. Uruchamiając środowisko Smalltalak uruchamia się cały, zamknięty świat Smalltalka. Np. nie ma tam dostępu do plików z zewnątrz tak, jak w innych językach. To spowodowało, że język ten, mimo pewnych sukcesów, nigdy nie zdobył sobie powszechnego uznania. I moim zdanie, nigdy już nie zdobędzie, bo w międzyczasie wyrosła mu młoda, obiecująca konkurencja.

Ruby

Ruby jest językiem relatywnie młodym. Tzn. został po raz pierwszy udostępniony publicznie w tym samym roku co Java – 1995. Ale przez większość lat był mało znany poza Japonią (skąd pochodzi jego twórca). Ruby jest także jednym z tych nielicznych języków, które (podobnie jak Smalltalk) posiadają wbudowaną obsługę kontynuacji. Istnieją co prawda różne próby implementacji tego mechanizmu w Javie1, ale tylko Ruby posiada ten mechanizm jako coś naturalnego i nie trzeba stosować żadnych specjalnych sztuczek aby go emulować. Z tego co pamiętam, Seaside pierwotnie był tworzony w Ruby, ale w tamtym okresie, kontynuacje w Ruby były jeszcze niedojrzałe, więc twórcy przerzucili się na Smalltalka. Ruby jednak szybko się rozwija, nabiera dojrzałości i moim zdaniem, ma bardzo duże szanse, aby wyprzedzić pozostałe języki przy tworzeniu serwisów internetowych nowej generacji, serwisów opartych na kontynuacjach.

Przykład kontynuacji.

Poniższy przykład2 pokazuje implementację generatora za pomocą kontynuacji.

class Generator

  def initialize
    do_generation
  end

  def next
    callcc do |here|
      @main_context = here;
       @generator_context.call
    end
  end

   private

   def do_generation
     callcc do |context|
       @generator_context = context;
       return
     end
     generating_loop
   end

   def generate(value)
     callcc do |context|
       @generator_context = context;
       @main_context.call(value)
     end
   end
end

A oto oparty na nim generator ciągu liczb Fibbonacciego:

class FibGenerator < Generator
  def generating_loop
    generate(1)
    a, b = 1, 1
    loop do
      generate(b)
      a, b = b, a+b
    end
  end
end

fib = FibGenerator.new
15.times { print "#{fib.next} " }
#wynik: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 

1 Np. Riff lub Jetty 6.

2 Przykład z książki “The Ruby Way”, 1ed.

Posted in , ,  | Tags , , ,  | 23 comments

Sun zatrudnił programistów JRuby

Posted by Jarosław Zabiełło Fri, 08 Sep 2006 08:33:00 GMT

W środowisku javowców Ruby jest językiem który robi trochę zamieszania (jakoś tak się składa, że Ruby bardziej przemawia do programistów Javy niż Python). Cieszy zatem, że firma Sun podjęła decyzję o wsparciu projektu JRuby zatrudniając jego czołowych developerów. Powinno to znacznie przyśpieszyć prace nad tym projektem podobnie jak stało się z IronPythonem, gdy Microsoft zatrudnił jego twórcę.

Jestem ciekaw jak długo programiści zachowają entuzjazm dla swego języka, gdy będzie można generować taki sam bytecode Javy lecz w niezrównanie prostszy sposób. :)

Przykładowy kod Javy:

public class Filter {
  public static void main(String[] args) {
    List list = new java.util.ArrayList();
    list.add("Tim"); list.add("Ike"); list.add("Tina");
    Filter filter = new Filter();
    for (String item : filter.filterLongerThan(list, 3)) { 
      System.out.println( item ); 
    }
  }
  public List filterLongerThan(List list, int length) {
    List result = new ArrayList();
    for (String item : list) {
      if (item.length() <= length) { result.add( item ); }
    }
    return result;
  }
}

A oto odpowiadający mu kod w Ruby:

list = ['Tim', 'Ike', 'Tina']
list.select {|n| n.length > 3}.each {|n| puts n}

Oczywiście to nie wszystko. Dzięki JRuby można uzyskać efekty kompletnie nieosiągalne w standardowej Javie – np. można pracować z biblioteką Swing w sposób interaktywny, z poziomu interpretera zmieniając na żywo jej obiekty.

Wkrótce ma także być gotowa wersja Railsów działająca z JRuby (Zobacz prezentację w PowerPoint). Tym samym odeszłyby wszelkie uwagi co do wydajności Railsów, bo współczesna wirtualna maszyna Javy jest tak silnie zoptymalizowna że dorównuje językowi C++. Oczywiście model wątkowy JRuby jest zgodny z wydajnym i dojrzałym modelem wątkowym Javy – po prostu z niego korzysta.

Posted in ,  | Tags , ,  | 32 comments

Akcesory w Javie, Pythonie, Ruby i PHP5

Posted by Jarosław Zabiełło Tue, 07 Feb 2006 22:25:00 GMT

Java, język który dobył sobie silną pozycję na rynku korporacyjnym, jest od jakiegoś czasu pod obstrzałem krytyki z różnych stron. Wyszła cała seria książek krytykujących model obiektowy oraz metodologie promowane przez Javę (od najsłynniejszej Beyond Java po Bitter Java, Bitter EJB, czy inne)

Nie umniejszając zalet Javy, jej krytycy wytykają jej niepotrzebną nadmiarowość kodu, ociężałośći i małą zwrotność, która powoduje że język ten słabo nadaje się do modnej ostatnio metodologii _agile programming. _ Aby lepiej ten problem zobaczyć, przyjrzyjmy się typowej praktyce programistów Javy. Tekst ten zainspirowany jest niedawną dyskusją jaka miała miejsce w jednym z blogów mojego kolegi.

Czym są akcesory?

Akcesory (lub inaczej: gettery i settery) to slangowe okreslenie metod jakie używa obiekt do odczytu i modyfikacji swoich atrybutów. Są one tak nagminnie używane przez programistów Javy, że niektóre edytory (np. Eclipse) zostały nawet wyposażone w makra do automatycznego ich generowania.

Dorzucanie nieużywanego kodu “na wszelki wypadek”...

Nagminna (wśród programistów Javy) praktyka dodawania akcesorów do atrybutów klasy pachnie jakimś antipatternem i są głosy, które używanie akcesorów nazwyają wprost złą praktyką . Jest w tym trochę racji. Ale, jak to poniżej wykażę, jest to pewnego rodzaju kompromis w związku ze słabym modelem obiektowy Javy.

Dlaczego programiści Javy generują akcesory dla każdego atrybutu nawet, jak nie przewidują potrzeby ich używania? Otóż czynią to “na wszelki wypadek” bo nie wiedzą, czy w przyszłości nie będzie im to potrzebne…

Weźmy np. taki kod (z powodu kolejnego ograniczenia Javy nie można w jednym pliku trzymać więcej, niż jednej klasy; tu dla krótkości umieszczam kod z obu plików razem)

public class First {
  public String msg = "hello";
}

public class Second extends First {
  public static void main(String[] args) {
    First obj = new First();        
    System.out.println(obj.msg);
  }
}

Jak widać, w Javie można sięgać do atrybutu w sposób bezpośredni.

Wyobraźmy sobie jednak, że projekt nam się rozrósł i mamy już całkiem sporo kodu oraz sporo plików (pomnażanych wydatnie przez javowe ograniczenia co do ilości klas mogących wystąpić w pliku). Teraz przychodzi polecenie aby w momencie odczytu i zapisu atrybutu coś dodatkowego wykonać. (Np. niech to będzie logowanie informacji o takim zdarzeniu, albo zablokowanie możliwości modyfikacji treści atrybutu. Wszystko jedno co)

Mamy zatem pierwszy problem. Trzeba w tych wszystkich milionach miejsc, gdzie odwoływaliśmy się bezpośrednio do atrybutów, wymienić kod…

Właśnie dlatego, aby takich niespodzianek uniknąć w przyszłości, programiści Javy dorzucają dodatkowe metody opakowujące odczyt i zapis atrybutów. Eclipse upraszcza ten proces zwalniając programistę od ręcznego wpisywania tego kodu.

Klasa First po zmianie:

public class First {
  public String msg = "hello";
  public String getMsg() {
    return msg;
  }
  public void setMsg(String msg) {
    this.msg = msg;
  }
}

Wydaje się, że problem jest rozwiązany. Ale to dopiero początek innych problemów.

Jeśli bowiem, automatycznie dorzucamy te metody do wszystkich tysięcy atrybutów nowo tworzonych klas, to pierwszą rzeczą którą zauważamy, jest nagłe “spuchnięcie kodu”. Mamy kupę nieużywanych linii kodu, z których prawdopodobnie większość nie będzie nigdy używana!

Ale to nie wszystko. Załóżmy, że stajemy przed potrzebą zmiany nazwy atrybutu. Oczywiście, możemy to zmienić w jednym miejscu (w ciele akcesora), ale wtedy wprowadzamy chaos w pozostałej części odnośnie… nazw (atrybut “msg” zmieniony na “title” trochę głupio wygląda z akcesorami o nazwie “getMsg” i “setMsg”) Musimy przekopać się przez miliony miejsc w kodzie i pozmieniać nazwy starym wywołaniom.

Zobaczmy jak ta sytuacja wygląda w językach dynamicznych.

Python

Python (podobnie jak Java) pozwala na bezpośredni dostęp do atrybutów klasy. A co w sytuacji kiedy chcemy opakować atrybut akcesorami? Nic prostszego. Python pozwala na dodanie akcesorów wtedy, i tylko wtedy, kiedy są potrzebne. Na dodatek czyni to w sposób całkowicie przezroczysty dla pozostałej części kodu. Programista Javy może sobie o tym tylko pomarzyć.

class X(object):
  def get_msg(self):
    return self.__msg
  def set_msg(self, val):
    self.__msg = val
  msg = property(get_msg, set_msg)

obj = X()
obj.msg = "hello"
print obj.msg

Python pozwala także związać z dowolną metodą, atrybutem, klasą czy modułem docstring, czyli tekst z dokumentacją, objaśnieniem itp. To jedna z genialnych cech Pythona specjalnie pomyślana dla leniwych programistów, którym nie chce się pisać dokumentacji. ;)

Ruby

W języku Ruby z definicji nie ma żadnej możliwości dostępu do atrubutów klasy inaczej jak przez akcesory. Odpada więc problem zapominania aby je dodać. Ruby jednak narzuca nazwy dla akcesorów (mają nazwę taką jak atrybut!) i całość wygląda tak, jakby operowano bezpośrednio na atrybucie.

class X
  def msg
    @msg
  end
  def msg=(val)
    @msg = val
  end
end

obj = X.new
obj.msg = "hello"
puts obj.msg
Dla osób, które nie lubią za dużo pisać, Ruby ma wygodne skróty. Powyższą definicję klasy można zapisać także w ten sposób:
class X
  attr_accessor :msg
end

Istnieją także oddzielne skróty dla getterów i setterów. No i można po przecinku dodać akcesory dla całej grupy atrybutów.

PHP5

PHP w wersji 5 ma przebudowany model obiektowy od podstaw. Twórcy języka PHP nie starali się poprawiać modelu obiektowego PHP4 (był on tak zły, że prościej było im napisać go od nowa). W nowym PHP5 mamy już możliwość przezroczystego dodania akcesora.

<?php
class X {
  private $attributes = array('msg'=>null);
  private function __get($attrname) {
    return $this->attributes[$attrname];
  }
  private function __set($attrname, $val) {
    $this->attributes[$attrname] = $val;
  }
}

$obj = new X();
$obj->msg = "hello";
print $obj->msg;
?>

Składnia może nie jest tak prosta jak w Ruby, ale (przynajmniej w tym miejscu), PHP5 zachowuje się tu sensownie i unika dylematów Javy.

Posted in , ,  | Tags , , ,  | 8 comments

Ruby, Python vs Java,C++

Posted by Jarosław Zabiełło Mon, 14 Nov 2005 20:41:00 GMT

Od czasu do czasu na grupach dyskusyjnych mają miejsce przepychanki na temat wyższości jednych języków nad drugimi. Czasami takie dyskusje wywołują sporo emocji. Zmęczony ciągłymi ogólnikowymi sloganami o tym jak to w C++ lub Javie pisze się lepiej i szybciej (dobry dowcip) programy niż w językach dynamicznych takich jak Python czy Ruby, postanowiłem rzucić małe wyzwanie miłośnikom tych języków.

Zadanie polegało na napisaniu wyszukiwarki do polskiego tekstu Koranu (w sumie nie widziałem nic takiego, stąd pomysł). Aby nie było za banalnie, trzeba było napisać taki kod który by automatycznie wyssał przekład Koranu ze strony internetowej http://www.planetaislam.com/koran.html następnie przetworzył go i wstawił do bazy mysql oraz przełączył się w tryb wyszukiwania fraz.

Wyzwanie zostało przyjęte i… mijają tygodnie za tygodniami a moi dyskutanci nie są w stanie przedstawić żadnego działającego kodu w Javie ani w C++. Tak to teoria zderza się z praktyką. Oto naprędce stworzona wersja w Ruby:

require 'mysql'
require 'open-uri'

@przeklad = 'bielawski'
@conn = Mysql.new('localhost', 'login', 'hasło', 'koran', 3308)

def download(max_chapter=15)
  def clean_html(src) # nie znam biblioteki czyszczacej html wiec robie to regexem
    src.gsub(/&nbsp;/, ' ').gsub(/(<[^>]+>)/, '').gsub(/[ ]{2,}/, ' ').gsub(/&#/, '').gsub(/&quot;/, '"')
  end   
  @lines = []
  1.upto(max_chapter) do |sura|
    url = sprintf('http://www.planetaislam.com/koran/%s/%03d.htm', @przeklad, sura)
    src = clean_html(open(url).read)
    rows = src.scan(/(\d+?)[. ]([^\d]+)/) # wyluskuj wersety i numery rozdzialow
    rows.each do |row|
      @lines << sprintf('%s:%s %s', sura, row[0], row[1].gsub(/[\s]{2,}/, ' ').strip)
    end
    f = File.new("#{@przeklad}.txt", "w") # zapisuje sobie do pliku
    @lines.each { |line| f.write("#{line}\n") }                               
  end
end
def loaded_db
  @conn.list_tables.include?(@przeklad) # Sprawdzam czy juz istnieje tabela z danymi.
end
def load_db
  @conn.query("DROP TABLE IF EXISTS #{@przeklad}")
  @conn.query("CREATE TABLE #{@przeklad} (
    id int(10) unsigned NOT NULL auto_increment,
    sura varchar(255) NOT NULL,
    nr int(10) unsigned NOT NULL,
    verse text NOT NULL,
    PRIMARY KEY (id)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci")
  @conn.query("SET CHARACTER SET latin2")
  @lines.each do |line|
    sura, nr, verse = line.scan(/(\d+?):(\d+?) (.+)/)[0]
    verse.gsub!(/'/,"''") # uzywam topornego sql bo cos mi metoda prepare nie dziala, moze zla biblioteka?
    @conn.query("INSERT INTO #{@przeklad} (sura, nr, verse) VALUES(#{sura}, #{nr}, '#{verse}')")
  end
end  
def search_result(phrase)
  @conn.query("SET CHARACTER SET cp1250")
  sql = "SELECT * FROM " + @przeklad + " WHERE verse LIKE '%" + phrase + "%' "
  c = @conn.query(sql)
  while row = c.fetch_row
    puts sprintf("Sura %s,%s %s\n", row[1], row[2], row[3])
  end
end

if $0 == __FILE__
  if !loaded_db          
    download
    load_db
  end
  search_result('kobieta')
end

A oto ostateczna wersja w Pythonie:

#!python

"""
Wyszukiwarka do Koranu

Przykłady uzycia:
    python koran.py --txt zabijajcie
    python koran.py --sura=ix
    python koran.py --sura=9 --nr=4
    python koran.py --sura=IX --nr=4    

Wymagania:
    interpreter Pythona 2.4 + standardowe biblioteki
    pythonowska biblioteka MySQLdb
    serwer MySQL 4.1

Autor:
    Jarosław Zabiełło http://zabiello.com
"""

import HTMLParser, os, re, urllib2, sys
from optparse import OptionParser
import MySQLdb

class Koran(object):
    dbname ="koran"
    przeklad = 'bielawski'
    lines = []

    def __init__(self):
        self.conn = MySQLdb.connect(
            host='localhost',
            port=3308,
            user='login',
            passwd='hasło',
            db=self.dbname)

    def download(self, max_chapter=15):
        """Sciagam dane z internetu i je troszke oczyszczam z nadmiarowego kodu html"""
        def html_clean(src):
            x = HtmlStripper()
            x.feed(src)
            return src = x.get_fed_data()            
        self.lines = []
        for i in xrange(1,max_chapter):
            url = 'http://www.planetaislam.com/koran/%s/%03d.htm' % (self.przeklad, i)
            src =  html_clean(urllib2.urlopen(url).read()) # pobrany caly plik
            rows = re.findall(r'(\d+?)[. ]([^\d]+)', src) # wyluskuj wersety i numery rozdzialow            
            for row in rows:
                self.lines.append('%s:%s %s' % (i, row[0], re.sub(r'[\s]{2,}', ' ', row[1]).strip()))
        self.lines = [unicode(line, 'iso-8859-2').encode('utf-8') for line in self.lines]
        f = open(self.przeklad+'.txt', 'w')  # zapisuje sobie kopie na dysk
        for line in self.lines:
            f.write('%s\n' % line)
        f.close()

    def loaded_db(self):
        """Sprawdzam czy juz istnieje tabela z danymi."""
        c = self.conn.cursor()
        c.execute("SHOW TABLES")
        rows = c.fetchall()
        if self.przeklad in [row[0] for row in rows]:
            c.execute("SELECT COUNT(*) FROM %s" % self. przeklad)
            return c.fetchone()[0]
        return False

    def load_db(self):
        "Zakladam ze stworzono baze o nazwie self.dbname i laduje dane do tabeli self.przeklad"
        sqls = (
            "DROP TABLE IF EXISTS %s" % self.przeklad,
            '''CREATE TABLE %s (
                id int(10) unsigned NOT NULL auto_increment,
                sura varchar(255) NOT NULL,
                nr int(10) unsigned NOT NULL,
                verse text NOT NULL,
                PRIMARY KEY (id)
            ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci''' % self.przeklad)
        c = self.conn.cursor()
        for sql in sqls: # odtwarzam baze
            c.execute(sql)
        c.execute("SET CHARACTER SET UTF8")
        sql = "INSERT INTO "+self.przeklad+" (sura, nr, verse) VALUES (%s,%s,%s)"
        for line in self.lines:
            found = re.match(r'(\d+?):(\d+?) (.+)', line)
            if found:
                c.execute(sql, found.groups())
        c.close()

    def set_i18n(self, cursor, phrase=None):
        if sys.platform == 'win32': # zakladam ze bede wyswietlal wyniki w okienku konsoli, czyli kodowanie dosowe
            if phrase:
                phrase = unicode(phrase, 'cp1250').encode('cp852')
            cursor.execute("SET CHARACTER SET cp852")
        elif sys.platform == 'linux':
            cursor.execute("SET CHARACTER SET latin2")
        return phrase

    def search_result(self, phrase):
        """Wyszukuje wersetow wg prostej zasady: znadz wszystkie wersety w ktorych gdziekolwiek
        wystepuje fraza phrase. Dla wygody dodalem obsluge polskich znakow spod konsoli dosowej
        i linuksa."""
        c = self.conn.cursor()
        phrase = self.set_i18n(c, phrase)
        sql = "SELECT * FROM " + self.przeklad + " WHERE verse LIKE '%" + phrase + "%' "
        print sql
        c.execute(sql)
        rows = c.fetchall()
        c.close()
        print "Znaleziono %d wersetow:\n" % len(rows)
        for row in rows:
            print "Sura %s,%s %s\n" % (int_to_roman(int(row[1])), row[2], row[3])

    def show_verse(self, sura, nr=0, context=0):
        c = self.conn.cursor()
        self.set_i18n(c)
        if sura.isalpha():
            sura = roman_to_int(sura)
        if sura and nr: # wyswietl pojedyncza sure
            sql = "SELECT verse FROM " + self.przeklad + " WHERE sura=%s AND nr=%s"
            c.execute(sql, (sura, nr))
            verse = c.fetchone()[0]
            print "Sura %s,%s %s\n" % (int_to_roman(int(sura)), nr, verse)
        else: # wyswietl cala sure
            sql = "SELECT nr, verse FROM " + self.przeklad + " WHERE sura=%s ORDER BY nr"
            c.execute(sql, [sura])
            rows = c.fetchall()
            for row in rows:
                print "Sura %s,%s %s\n" % (int_to_roman(int(sura)), row[0], row[1])
        c.close()

class HtmlStripper(HTMLParser.HTMLParser):
    """src: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440481"""
    def __init__(self):
        self.reset()
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def get_fed_data(self):
        return ''.join(self.fed)

def int_to_roman(input):
    """src: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81611"""
    if type(input) != type(1):
        raise TypeError, "expected integer, got %s" % type(input)
    if not 0 < input < 4000:
        raise ValueError, "Argument must be between 1 and 3999"   
    ints = (1000, 900,  500, 400, 100,  90, 50,  40, 10,  9,   5,  4,   1)
    nums = ('M',  'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I')
    result = ""
    for i in range(len(ints)):
        count = int(input / ints[i])
        result += nums[i] * count
        input -= ints[i] * count
    return result

def roman_to_int(input):
    """src: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81611"""
    if type(input) != type(""):
        raise TypeError, "expected string, got %s" % type(input)
    input = input.upper()
    nums = ['M', 'D', 'C', 'L', 'X', 'V', 'I']
    ints = [1000, 500, 100, 50,  10,  5,   1]
    places = []
    for c in input:
        if not c in nums:
            raise ValueError, "input is not a valid roman numeral: %s" % input
    for i in range(len(input)):
        c = input[i]
        value = ints[nums.index(c)]
        try:
            nextvalue = ints[nums.index(input[i +1])]
            if nextvalue > value:
                value *= -1
        except IndexError:
            pass
        places.append(value)
    sum = 0
    for n in places:
        sum += n
    return sum

if __name__ == '__main__':
    appl = Koran()
    if not appl.loaded_db():
        print 'Pobieram dane z internetu....'
        appl.download()
        print 'Wkladam dane do bazy...'
        appl.load_db()
    parser = OptionParser()
    parser.add_option('--txt', dest='txt', help='wyszukiwana fraza')
    parser.add_option('--sura', dest='sura', help='numer Sury (liczba dziesietna lub rzymska)')
    parser.add_option('--nr', dest='nr', help='numer wersetu dla Sury')
    (options, args) = parser.parse_args()
    if options.txt:
        appl.search_result(options.txt)
    elif options.sura:
        appl.show_verse(options.sura, options.nr)
    else:
        print "Wywolaj z parametrem --help"

W międzyczasie zdążyłem nawet wykorzystać swój kod Pythona aby dodać go jako moduł do Plone. :)

Posted in ,  | Tags , ,  | 54 comments