<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"><channel><title>http://stronger.epsi.pl/</title><link>http://stronger.epsi.pl/</link><description>Wpisy z dziennika internetowego Jogger, wspomaganego przez Jabbera</description><lastBuildDate>Mon, 08 Mar 2010 21:27:36 +0100</lastBuildDate><generator>JoggerPL</generator><item><title>Agile database modelling</title><link>http://stronger.epsi.pl/2010/01/10/agile-database-modelling/</link><description>&lt;p&gt;Things change. New laws are being passed, new business rules are kicking in, new requirements emerge and old ones get phased out. To keep the planets moving software must play catch up and good developer can be easily spotted: their code is ready for changes pretty much all the time and the change won't be a painful one.&lt;/p&gt;
&lt;h3&gt;NoSQL&lt;/h3&gt;
&lt;p&gt;The überpopular term &lt;cite&gt;NoSQL&lt;/cite&gt; is not the thing I'm about to propose here. Despite its enthusiastic uptake the established position in the industry is yet to come. Secondly, because there are &lt;em&gt;absolutely outstanding&lt;/em&gt; tools, books, common practices, and other knowledge sources on relational databases, SQL language, and Object-Relational Mapping. There are RDBMSes that range from zero to megabucks, there is workforce on the market that range from interns to six-digits-salary wizards. Everywhere you look the web is living and breathing in relational databases.&lt;/p&gt;
&lt;p&gt;But I'm getting off the point. To outline this picture quickly, let's put two items into the equation:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;design and implementation details &lt;em&gt;do&lt;/em&gt; change&lt;/li&gt;
&lt;li&gt;database schemas are strict and do not automatically adopt to changes by themselves&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Granted the two forces opposing, cracks on the surface are inevitable. Sure, you can bend your neck and keep updating schema with every change. Or you could employ one of this big, complicated ORM engines and force it on all memebers of your team. Or, you can oppose all change and get replaced with someone competent. Finally, you can be smart and use in-schema serialised LOBs to provide required dynamic features. To illustrate what I mean, may I present real life example. I use PDO for database access, DAO for persistence handling, and JSON LOBs for dynamic containers. First, example without dynamic features:&lt;/p&gt;
&lt;pre&gt;
CREATE TABLE orders (
        id              SERIAL PRIMARY KEY,
        ref_num         VARCHAR(60),
        created_on      TIMESTAMP NOT NULL DEFAULT current_timestamp,
        customer_id     INT
);
&lt;/pre&gt;
&lt;pre&gt;
class Order {

        protected $columns = array('id', 'ref_num', 'created_on', 'customer_id');
        protected $data = array();
        protected $dao;

        public function __construct() {
                $this-&amp;gt;dao = new PDODataAccessObject('orders', 'id', $this-&amp;gt;columns);
        }

        public static function getById($id) {
                $o = new Order();
                $o-&amp;gt;data = $o-&amp;gt;dao-&amp;gt;getByPrimaryKey($id);
                return $o;
        }

        public function __get($k) {
                return array_key_exists($k, $this-&amp;gt;data) ? $this-&amp;gt;data[$k] : null;
        }

        public function __set($k, $v) {
                $this-&amp;gt;data[$k] = $v;
        }

        public function save() {
                $this-&amp;gt;dao-&amp;gt;save($this-&amp;gt;data);
        }

}
&lt;/pre&gt;
&lt;p&gt;This is minimal implementation of working persistent object. As you can possibly imagine class &lt;cite&gt;PDODataAccessObject&lt;/cite&gt; is a relational storage, which was skipped as not being relevant for this example. You can retrieve Order from database by primary key simply typing &lt;code&gt;$order = Order::getById(10)&lt;/code&gt;. You can read &lt;code&gt;echo $order-&amp;gt;created_on&lt;/code&gt; and write &lt;code&gt;$order-&amp;gt;ref_num = 12345&lt;/code&gt; object properties, that will be stored in database upon &lt;code&gt;$order-&amp;gt;save()&lt;/code&gt;. DAO will figure out whether to insert new row into db or update existing – but that's not relevant either. Let's enable class Order for dynamic schema.&lt;/p&gt;
&lt;pre&gt;
CREATE TABLE orders (
        id              SERIAL PRIMARY KEY,
        ref_num         VARCHAR(60),
        created_on      TIMESTAMP NOT NULL DEFAULT current_timestamp,
        customer_id     INT,
        &lt;b&gt;json         TEXT&lt;/b&gt;
);
&lt;span&gt;Column &quot;json&quot; is a JSON container for dynamic properties.&lt;/span&gt;
&lt;/pre&gt;
&lt;pre&gt;
class Order {

        protected $columns = array('id', 'ref_num', 'created_on', 'customer_id', &lt;b&gt;'json'&lt;/b&gt;);
        protected $data = array();
        protected $json;
        protected $dao;

        public function __construct() {
                $this-&amp;gt;dao = new PDODataAccessObject('orders', 'id', $this-&amp;gt;columns);
        }

        public static function getById($id) {
                $o = new Order();
                $o-&amp;gt;data = $o-&amp;gt;dao-&amp;gt;getByPrimaryKey($id);
                return $o;
        }

        public function __get($k) {
                if (in_array($k, $this-&amp;gt;data)) {
                        return array_key_exists($k, $this-&amp;gt;data) ? $this-&amp;gt;data[$k] : null;
                }
                &lt;b&gt;if (is_null($this-&amp;gt;json)) {
                        $this-&amp;gt;json = (array)json_decode($this-&amp;gt;data['json'], true);
                }
                return array_key_exists($k, $this-&amp;gt;json) ? $this-&amp;gt;json[$k] : null;&lt;/b&gt;
        }

        public function __set($k, $v) {
                if (in_array($k, $this-&amp;gt;data)) {
                        $this-&amp;gt;data[$k] = $v;
                        return;
                }
                &lt;b&gt;if (is_null($this-&amp;gt;json)) {
                        $this-&amp;gt;json = (array)json_decode($this-&amp;gt;data['json'], true);
                }
                $this-&amp;gt;json[$k] = $v;&lt;/b&gt;
        }

        public function save() {
                &lt;b&gt;$this-&amp;gt;data['json'] = json_encode($this-&amp;gt;json);&lt;/b&gt;
                $this-&amp;gt;dao-&amp;gt;save($this-&amp;gt;data);
        }

}
&lt;span&gt;Parts in bold were added to previous implementation.&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Note the additional column &lt;cite&gt;json&lt;/cite&gt; in table schema. This is where your dynamic properties go. Now, you are free to store any property you want without making any change to schema! Like, &lt;code&gt;$order-&amp;gt;if_not_delivered_please_call = &quot;085123456&quot;&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;No DBA would ever design something like this!&lt;/h3&gt;
&lt;p&gt;Yep, true. It is precisely because what most DBAs tend to care about is their golden temple of normalised tables, non-reduntant schemas, and optimal column sizes. It's the mental wank, the uplifting feeling of doing &quot;Proper Engineering&quot; that hardly ever contributes to anything but self esteem. Such DBAs would fear of a craftsman who treats their golden temple as a mean to an end and not an end itself, a developer who rather than sticking to &quot;middleware&quot; operates across and between layers where needed.&lt;/p&gt;
&lt;p&gt;Dynamic schema extension as presented above has severe limitation of not being easy to report upon. You cannot retrieve records by columns stored in serialised LOB. It's just not suitable for that purpose. But serialised LOBs have this nice feature that once necessity arises, their content can be easily migrated to dedicated column. I.e. let's say that putting &lt;code&gt;employee_id&lt;/code&gt; in Order object is becoming a common thing. Now, manager wants to know how do employees perform. Reporting on dynamic column is not possible, but at least, since information was stored in JSON, writing a five-liner that would extract property employee_id and store it into newly created column is a no-brainer. Now, DBA's been saved from heart attack.&lt;/p&gt;
&lt;p&gt;However not an one-size-fits-all type of tool, dynamic schema can be a life saver in situations when it is unknown what sort of fields are worth storing into db. It's also perfect for rapid prototyping, where you don't really want to remodel your table columns endlessly and just get the shit running asap. Agile schema modelling can buy you significant amount of time before stabilising your code. Then, once you have pretty good idea on how is this all expected to work, you can start pulling dynamic properties into strict db columns.&lt;/p&gt;
</description><pubDate>Sun, 10 Jan 2010 18:04:11 +0100</pubDate><guid>http://stronger.epsi.pl/2010/01/10/agile-database-modelling/</guid><category>en</category><category>php</category><category>Techblog</category><category>orm</category><category>nosql</category><category>json</category><category>agile</category><category>db</category></item><item><title>Landscape od Canonical</title><link>http://stronger.epsi.pl/2009/08/18/landscape-od-canonical/</link><description>&lt;p&gt;&lt;em&gt;Ten wpis NIE JEST o platformie Launchpad. Landscape != Launchpad.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Ponad rok temu pojawiło się ogłoszenie, że &lt;a href=&quot;http://www.canonical.com&quot;&gt;Canonical&lt;/a&gt; ma zamiar stworzyć oprogramowanie do grupowego zarządzania systemami. Sprawa przeszła jednak poniżej radarów prasy branżowej i blagsfery. Ponieważ ostatnio pojedynkowałem się bezcelowo na argumenty ze zwolennikiem Windows, przypomniałem sobie o Landscape. Rzeczony windziarz zarzucał, że w Linuksach brakuje odpowiednika windowsowej konsoli administracyjnej (MMC) do zarządzania większą liczbą maszyn. Moja odpowiedź poszła w trzech kierunkach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MMC służy tylko rozwiązywamiu problemów stworzonych przez same Windowsy&lt;/li&gt;
&lt;li&gt;odpowiednikiem MMC w Linuksach jest konsola tekstowa&lt;/li&gt;
&lt;li&gt;jest &lt;a href=&quot;http://www.canonical.com/projects/landscape&quot;&gt;Landscape&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;W tym ostatnim przypadku strzelałem w ciemno, bo o Landscape dawno nikt nie napisał. Po chwili, namysłu... krótkiej chwili, by nie angażować się zbytnio w ten proces, zapisałem się na jazdę próbną z tym narzędziem. I w zasadzie o całej sprawie zapomniałem na dwa tygodnie. Zaproszenie przyszło wczoraj, i ponieważ tak się akurat składa, że mam jeden hostowany RPS działający pod Ubuntu to z łatwością skonfigurowałem go do pracy z Landscape. Sprowadziło się to do zainstalowania jednego pakietu i tony zależności z bieżącego repo i odpowiedzi na kilka banalnych pytań.&lt;/p&gt;
&lt;h2&gt;I.&lt;/h2&gt;
&lt;p&gt;System składa się z dwóch części. Pierwsza to demon instalowany na własnej maszynie. Druga to webowy frontend dostępny pod landscape.canonical.com. Po kilku prostych i dobrze opisanych czynnościach konfiguracyjnych otrzymujemy dostęp do pełnej konsoli administracyjnej. Wygląda ona tak:&lt;/p&gt;
&lt;div style=&quot;overflow: auto&quot;&gt;&lt;img src=&quot;http://gruz.epsi.pl/g/landscape/land-users.png&quot; alt=&quot;Landscape - użytkownicy&quot;&gt;&lt;/div&gt;
&lt;p&gt;Zachęcony przejrzystym interfejsem postanowiłem zdziałać coś, co miałoby pozory rzeczywistej pracy. Postanowiłem więc udać się tam, gdzie wysyła mnie lista alertów, czyli do aktualizacji pakietów.&lt;/p&gt;
&lt;div style=&quot;overflow: auto&quot;&gt;&lt;img src=&quot;http://gruz.epsi.pl/g/landscape/land-alerts.png&quot; alt=&quot;Landscape - monity&quot;&gt;&lt;/div&gt;
&lt;p&gt;Instalacja jest banalna i sprowadza się do kliknięcia pakietów z listy i zatwierdzeniu zadania. Przypomina to nieco starego dobrego, najpierw kochanego, potem znienawidzonego &lt;em&gt;dselecta&lt;/em&gt;.&lt;/p&gt;
&lt;div style=&quot;overflow: auto&quot;&gt;&lt;img src=&quot;http://gruz.epsi.pl/g/landscape/land-updates.png&quot; alt=&quot;Landscape - wybór pakietów&quot;&gt; &lt;img src=&quot;http://gruz.epsi.pl/g/landscape/land-installation.png&quot; alt=&quot;Landscape - instalacja pakietów&quot;&gt;&lt;/div&gt;
&lt;p&gt;Na screenshocie widać pasek postępu, ale nie jest on jednak ajaksowym gadżetem, który przyrasta w miarę postępu prac. Po odświeżeniu strony jego wartość podniosła się do 33%, co oznaczało, że paczki zostały pobrane i zaczyna się właściwa instalacja. Po niedługim czasie mogłem już oglądać raport poinstalacyjny.&lt;/p&gt;
&lt;div style=&quot;overflow: auto&quot;&gt;&lt;img src=&quot;http://gruz.epsi.pl/g/landscape/land-postinst.png&quot; alt=&quot;Landscape - raport z instalacji oprogramowania&quot;&gt;&lt;/div&gt;
&lt;h2&gt;II.&lt;/h2&gt;
&lt;p style=&quot;clear: both&quot;&gt;&lt;img class=&quot;right&quot; src=&quot;http://gruz.epsi.pl/g/landscape/land-admins.png&quot; alt=&quot;Landscape - administratorzy&quot;&gt; Pierwsze wrażenie z Landscape jest znakomite. Potrafię sobie wyobrazić, że w przypadku większej ilości serwerów potrafi ono ułatwić uzyskanie lepszego obrazu całościowego infrastruktury. Ponadto ciekawe jest odejście od klasycznej roli root-a jako administratora systemu. W Landscape można dodawać wielu administratorów bez konieczności dopisywania ich do sudoers. Ponadto, administratorzy mogą subskrybować powiadomienia e-mailem o konretnych zdarzeniach.&lt;/p&gt;
&lt;p style=&quot;clear: both&quot;&gt;&lt;img class=&quot;left&quot; src=&quot;http://gruz.epsi.pl/g/landscape/land-load.png&quot; alt=&quot;Landscape - wykresy&quot;&gt; &lt;cite&gt;Przysłowiową wisienką na tak zwanym torcie&lt;/cite&gt; (© Szyman) jest możliwość prezentacji wykorzystania zasobów systemu w postaci wykresów. Oprócz zajętości pamięci operacyjnej i masowych czy temperatury można dostarczyć własnych źródeł danych, które wygenerują wykres dowolnej funkcji, jaką tylko uda się nam wytrzasnąć. Źródła danych - to brzmi dumnie. W rzeczywistości są to zwykłe skrypty z podanym she-bangiem, czyli dowolnym interpreterem, który tylko jest dostępny na maszynie, więc jeśli tylko mamy ochotę na odrobinę dziwacznej perwersji możemy podać np. #!/usr/bin/php.&lt;/p&gt;
&lt;h2 style=&quot;clear: both&quot;&gt;I c4n haz one?&lt;/h2&gt;
&lt;p&gt;Na lot testowy z Landscape można zapisać się &lt;a href=&quot;https://landscape.canonical.com/&quot;&gt;na stronie produktu&lt;/a&gt;. Aplikacja jest prostsza niż niejeden panel systemów hostingowych i być może celuje nawet w ten sam segment rynku - kto wie? W końcu po co komu cPanel czy Plesk (jeden gorszy od drugiego) kiedy ma do dyspozycji tak przyjemną w użyciu konsolę webową.&lt;/p&gt;
&lt;p&gt;Landscape z początku był aplikacją hostowaną u Canonical (model Application Service Provider), jednak producent &lt;a href=&quot;http://www.internetnews.com/dev-news/article.php/3833111&quot;&gt;ogłosił niedawno&lt;/a&gt; jej dostępność w &lt;a href=&quot;https://forms.canonical.com/landscape_dedicated_server&quot;&gt;instalacji on-site. Roczna licencja wyniesie $150.&lt;/p&gt;
&lt;p&gt;Moar on &lt;a href=&quot;http://blog.landscape.canonical.com/&quot;&gt;Landscape blog&lt;/a&gt;&lt;/p&gt;
</description><pubDate>Tue, 18 Aug 2009 17:22:51 +0200</pubDate><guid>http://stronger.epsi.pl/2009/08/18/landscape-od-canonical/</guid><category>pl</category><category>Techblog</category><category>landscape canonical ubuntu konsola mmc</category></item><item><title>Tak zdechł XHTML i chuj mu na grób</title><link>http://stronger.epsi.pl/2009/07/03/tak-zdechl-xhtml-i-chuj-mu-na-grob/</link><description>&lt;p&gt;(ten grób pochodzi z piosenki &lt;a href=&quot;http://slashermckagan.wrzuta.pl/audio/5JpKjH0xzBZ/homo_twist_-_arkadiusz&quot;&gt;Arkadiusz&lt;/a&gt; by Homo Twist)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.w3.org/News/2009#item119&quot;&gt;I badzo dobrze&lt;/a&gt;, bo miejsce pięknych standardów jest w muzeach utrzymywanych z pieniędzy podatników zarabiających dzięki standardom, które wprawdzie piękne nie są, ale są za to (by sparafrazować profesora Bartoszewskiego) sympatyczne.&lt;/p&gt;
&lt;h3&gt;Gęba pełna frazesów&lt;/h3&gt;
&lt;p&gt;Kiedy byłem pięknym młodzieńcem zwykłem głosić z przekonaniem, że każdy problem da się rozwiązać w standardowy sposób. Jeśli rozwiązanie nie posiada szablonu rozwiązania to należy go stworzyć i ustandaryzować, bo przecież na wszystko da się znaleźć wystarczająco obszerny algorytm czy format. Ponadto dobre rozwiązanie powinno cechować się uniwersalnością, zatem wszystkie pośledniejsze rozwiązania powinny zostać uogólnione za pomocą dodatkowej warstwy abstrakcji. I te pe, i te de, kosmiczna harmonia i zen.&lt;/p&gt;
&lt;h3&gt;Szybki reality check&lt;/h3&gt;
&lt;p&gt;Teraz pobawimy się w grę fabularną i ja będę twoim mistrzem gry. Możesz sobie stworzyć avatara, ale nie musisz, bo wynik rozgrywki mogę ci zdradzić już teraz – będąc wielbicielem XHTMLa wielkiego sukcesu nie odniesiesz. I nie myśl, że poniższa gra nie ma wiele wspólnego z developersko-biznesową rzeczywistością. Ma całkiem sporo – straciłem już dawno nadzieję, że życie zacznie mnie nagle rozpieszczać bardziej sensownymi wyborami.&lt;/p&gt;
&lt;div style=&quot;border: lightgray 1px solid; padding: .1em 1em; -moz-border-radius: .3em&quot;&gt;
&lt;p id=&quot;q0&quot;&gt;Dołączasz do zespołu tworzącego aplikację WWW. Jest już napisane trochę kodu, który raczej słabo się waliduje. Poziom wiedzy pozostałych członków zespołu jest zróżnicowany: kilku gości świeżo po studiach, jeden nadęty mastahaka, dwójka całkiem sensownych develi. Co robisz?&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;q0&quot; onclick=&quot;reveal(1)&quot;&gt; Zaczynasz tworzyć brakującą funkcjonalność pozostawiając kod niezgodnym z żadnych ze standardów byle tylko działał w FF i IE&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;q0&quot; onclick=&quot;reveal(2)&quot;&gt; Przepisujesz aplikacją na zgodną z XHTML&lt;/p&gt;
&lt;p id=&quot;q1&quot; style=&quot;display: none&quot;&gt;I bardzo dobrze. Aplikacja zostaje ukończona na czas, wszyscy stają się obrzydliwie bogaci i żyją długo i szczęśliwie.&lt;/p&gt;
&lt;p id=&quot;q2&quot; style=&quot;display: none&quot;&gt;Wprowadziłeś zmiany do kodu, by walidował się jako XHTML, ale widzisz, że nowy kod, który tworzą twoi współpracownicy jest nadal niepoprawny. Co robisz?&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;q2&quot; onclick=&quot;reveal(3)&quot;&gt; Olewasz swój pierwotny plan i zaczynasz dodawać zaplanowaną funkcjonalność&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;q2&quot; onclick=&quot;reveal(4)&quot;&gt; Uczysz ich poprawnego XHTMLa i pomagasz poprawiać nowopowstały kod&lt;/p&gt;
&lt;p id=&quot;q3&quot; style=&quot;display: none&quot;&gt;I słusznie. Aplikacja zostaje ukończona na czas, a klienci są raczej zadowoleni.&lt;/p&gt;
&lt;p id=&quot;q4&quot; style=&quot;display: none&quot;&gt;Po dwóch tygodniach twój kierownik pyta dlaczego żadna z zaplanowanych funkcji nie została zaimplementowana. Co robisz?&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;q2&quot; onclick=&quot;reveal(5)&quot;&gt; Ściemniasz, że musiałeś przeczyścić kod by nie zakopać się w prowizorycznych rozwiązaniach i nie wracasz już do XHTMLa&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;q2&quot; onclick=&quot;reveal(6)&quot;&gt; Próbujesz przekonać go, że powinniście serio potraktować XHTML i wymusić zmianę priorytetów.&lt;/p&gt;
&lt;p id=&quot;q5&quot; style=&quot;display: none&quot;&gt;Lepiej późno niż wcale. Przekraczacie deadline i dostajecie połowę spodziewanej premii. Koledzy mają do ciebie żal o ten głupi XHTML.&lt;/p&gt;
&lt;p id=&quot;q6&quot; style=&quot;display: none&quot;&gt;Poprawiacie wszystko jak leci aby było zgodne z XHTMLem. Nagle kilka komponentów opartych o zewnętrzne biblioteki przestaje działać. Co robisz?&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;q2&quot; onclick=&quot;reveal(7)&quot;&gt; Wycofujesz zmiany, które spowodowały błędy w komponentach.&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;q2&quot; onclick=&quot;reveal(8)&quot;&gt; Oddajesz się analizie komponentów i własnoręcznie przywracasz zgodność z nowym kodem.&lt;/p&gt;
&lt;p id=&quot;q7&quot; style=&quot;display: none&quot;&gt;Aplikacja zostaje ukończona grubo po terminie. Nikt nie chce ze sobą już dalej pracować. Najlepsi ludzie odchodzą.&lt;/p&gt;
&lt;p id=&quot;q8&quot; style=&quot;display: none&quot;&gt;Ponieważ jesteści poważnie opóźnieni kierownictwo przydziela wam nowego developera: studenta IV roku elektryki, który całą swoją wiedzę o webie nabył z podręcznika Pawła Wimmera.&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;q2&quot; onclick=&quot;reveal(9)&quot;&gt; Dajesz mu robotę po stronie serwera, by nie rozwalał walidacji.&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;q2&quot; onclick=&quot;reveal(10)&quot;&gt; Uczysz go poprawnej składni XHTML oraz odkrywasz przed nim tajniki DOM inne niż innerHtml.&lt;/p&gt;
&lt;p id=&quot;q9&quot; style=&quot;display: none&quot;&gt;Student wprowadza sporo błędów do logiki biznesowej i niszczy separację warstw. Klienci są rozczarowani. Wszyscy nienawidzicie studenta, bo tak jest łatwiej.&lt;/p&gt;
&lt;p id=&quot;q10&quot; style=&quot;display: none&quot;&gt;Produkt jest gotowy grubo po terminie i większa część klientów poszła sobie do konkurencji. Jeden wierny klient zgłasza problem z parsowaniem XHTMLa z uwagi na brak wsparcia dla przestrzeni nazw w jego narzędziu (komercyjne, nie wspierane, bez źródeł).&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;q2&quot; onclick=&quot;reveal(11)&quot;&gt; Dostarczasz osobny dokument XML, który zawiera wyłącznie potrzebną treść.&lt;br&gt;
&lt;input type=&quot;radio&quot; name=&quot;q2&quot; onclick=&quot;reveal(12)&quot;&gt; Budujesz parser, który bierze poprawny XHTML i zwraca niepoprawny, ale za to parsowalny HTML lub XML.&lt;/p&gt;
&lt;p id=&quot;q11&quot; style=&quot;display: none&quot;&gt;Wszyscy cię nienawidzą, że dla głupiego XHTMLa prawie doprowadziłeś do upadku projektu a zastosowana technologia i tak nie spełnia pokładanych w niej nadziei. I po co ci to było?&lt;/p&gt;
&lt;p id=&quot;q12&quot; style=&quot;display: none&quot;&gt;Nienawidzisz sam siebie za to, że dałeś się omotać pięknej wizji uniwersalnego formatu, który rozwiązuje wszystkie problemy tego świata oprócz (tak się akurat składa) twoich. Wersja alternatywna: nienawidzisz złego świata, który nic a nic nie robi sobie z solidnych standardów. Reszta bez zmian.&lt;/p&gt;
&lt;/div&gt;
&lt;h3&gt;42&lt;/h3&gt;
&lt;p&gt;Odpowiedzią na większość problemów, z którymi miałem szansę się zmierzyć była dziwna mieszanka solidnej inżynierii, doświadczenia, dystansu do technologii, szczęścia, paniki, braku czasu oraz jasna konieczność indywidualnego podejścia do zagadnienia. Wszelkie automaty, uniwersalne formaty i metasilniki okazywały się niewłaściwe, w najgorszym przypadku uniemożliwiały wykonanie zadania. &lt;a href=&quot;http://www.joelonsoftware.com/articles/fog0000000018.html&quot;&gt;Architektoniczni astronauci&lt;/a&gt; budują piękne cacka, których nikt nie potrafi poprawnie używać. Dokładnie takie samo zdanie mam o XHTMLu. Jest to przeteoretyzowany gniot, produkt myśli obsesyjnego purysty, który w zamierzeniu ma łączyć cechy XMLa i HTMLa. Byłoby fantastycznie gdyby łączył ich zalety, ale niestety łączy również ich wady. I jest to bardzo zła wiadomość, bo o ile zalety obu formatów &lt;strong&gt;czasami&lt;/strong&gt; bywają przydatne, to ich połączone wady &lt;strong&gt;zawsze&lt;/strong&gt; stanowią przeszkodę. Gdyby było inaczej to wszyscy z uśmiechem na twarzy używalibyśmy takich pereł architektury jak XHTML, &lt;acronym title=&quot;obyś łajzo zdechła!&quot;&gt;SOAP&lt;/acronym&gt; czy CORBA, tym czasem wszystkie wymienione chętnie widziałbym w tym samym miejscu, w którym obecnie znajduje się Betamax.&lt;/p&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
function reveal(q) {
        var h = Math.floor((q-1) / 2 ) * 2;
        for (var i = h + 1; i &lt;= 12; ++i) {
                document.getElementById('q' + i).style.display = 'none';
        }
        document.getElementById('q' + q).style.display = 'block';
}
&lt;/script&gt;</description><pubDate>Fri, 03 Jul 2009 23:19:33 +0200</pubDate><guid>http://stronger.epsi.pl/2009/07/03/tak-zdechl-xhtml-i-chuj-mu-na-grob/</guid><category>php</category><category>pl</category><category>xhtml</category><category>html5</category></item><item><title>PHP+SOAP+WSSE</title><link>http://stronger.epsi.pl/2009/06/24/php-soap-wsse/</link><description>&lt;p&gt;Pick any two and your life will remain nice and easy. Pick all and you will want to hurt yourself with random object in reach. PHP native class for handling SOAP calls can't do WS-Security. Here's how one can work around this problem.&lt;/p&gt;
&lt;pre&gt;
class WSSESoapClient extends SoapClient {                                                                                           
        protected $wsseUser;
        protected $wssePassword;

    public function setWSSECredentials($user, $password) {
        $this-&amp;gt;wsseUser = $user;
        $this-&amp;gt;wssePassword = $password;
    }

    public function __doRequest($request, $location, $action, $version) {
        if (!$this-&amp;gt;wsseUser or !$this-&amp;gt;wssePassword) {
            return parent::__doRequest($request, $location, $action, $version);
        }

        // get SOAP message into DOM
        $dom = new DOMDocument();
        $dom-&amp;gt;loadXML($request);
        $xp = new DOMXPath($dom);
        $xp-&amp;gt;registerNamespace('SOAP-ENV', 'http://schemas.xmlsoap.org/soap/envelope/');

        // search for SOAP header, create one if not found
        $header = $xp-&amp;gt;query('/SOAP-ENV:Envelope/SOAP-ENV:Header')-&amp;gt;item(0);
        if (!$header) {
            $header = $dom-&amp;gt;createElementNS('http://schemas.xmlsoap.org/soap/envelope/', 'SOAP-ENV:Header');
            $envelope = $xp-&amp;gt;query('/SOAP-ENV:Envelope')-&amp;gt;item(0);
            $envelope-&amp;gt;insertBefore($header, $xp-&amp;gt;query('/SOAP-ENV:Envelope/SOAP-ENV:Body')-&amp;gt;item(0));
        }

        // add WSSE header
        $usernameToken = $dom-&amp;gt;createElementNS('http://schemas.xmlsoap.org/ws/2002/07/secext', 'wsse:UsernameToken');
        $username = $dom-&amp;gt;createElementNS('http://schemas.xmlsoap.org/ws/2002/07/secext', 'wsse:Username', $this-&amp;gt;wsseUser);
        $password = $dom-&amp;gt;createElementNS('http://schemas.xmlsoap.org/ws/2002/07/secext', 'wsse:Password', $this-&amp;gt;wssePassword);
        $usernameToken-&amp;gt;appendChild($username);
        $usernameToken-&amp;gt;appendChild($password);
        $header-&amp;gt;appendChild($usernameToken);

        // perform SOAP call
        $request = $dom-&amp;gt;saveXML();
        return parent::__doRequest($request, $location, $action, $version);
    }

} // class WSSESoapClient
&lt;span&gt;Use method &lt;strong&gt;setWSSECredentials()&lt;/strong&gt; to enclose WS-Security header in SOAP message.&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Code above is a modification and extension of &lt;a href=&quot;http://stackoverflow.com/questions/953639#955272&quot;&gt;reply to Q#953639&lt;/a&gt; on &lt;a href=&quot;http://stackoverflow.com/&quot;&gt;StackOverflow.com&lt;/a&gt;&lt;/p&gt;
</description><pubDate>Wed, 24 Jun 2009 13:26:54 +0200</pubDate><guid>http://stronger.epsi.pl/2009/06/24/php-soap-wsse/</guid><category>en</category><category>php</category><category>soap</category><category>wsse</category><category>ws-security</category></item><item><title>video/theora - bo mamy XXI wiek</title><link>http://stronger.epsi.pl/2009/06/18/theora-bo-mamy-xxi-wiek/</link><description>&lt;p&gt;W ostatnich dniach przewaliła się przez Internety wiadomość &lt;a href=&quot;http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2009-June/020363.html&quot;&gt;sprzedana ustami Chrisa DiBony&lt;/a&gt;, jakoby format Theora był dla Google niewystarczający technologicznie, w związku z czym YouTube - owszem - rozważa użycie nowego tagu &lt;code&gt;video&lt;/code&gt; z proponowanego standardu HTML 5, ale do serwowania plików w formacie H.264. Powodem takiego postawienia sprawy mabyć rzekoma dbałość o pasmo. Chris ostrożnie zasugerował, że pliki zakodowane Theorą i Vorbisem mają być większe przy zachowaniu tej samej jakości co H.264 i MP3/AAC. Na reakcję świadomej technologicznie części społeczności nie trzeba było długo czekać. &lt;a href=&quot;http://people.xiph.org/~greg/video/ytcompare/comparison.html&quot;&gt;Greg Maxwell&lt;/a&gt; i &lt;a href=&quot;http://people.xiph.org/~maikmerten/youtube/&quot;&gt;Maik Merten&lt;/a&gt; przedstawili nienaukowe, ale w miarę obiektywne porównania obu par formatów.&lt;/p&gt;
&lt;p&gt;Dość przemiału newsowo-blogowego. Teraz ja się zapytuję: kto za to zapłaci gdy &lt;a href=&quot;http://en.wikipedia.org/wiki/H.264#Patent_licensing&quot;&gt;wejdzie w życie plan pobierania opłat licencyjnych&lt;/a&gt; za kodowanie i/lub dekodowanie H.264? Opcji jest kilka. Dostawca treści - co wydaje się być mało prawdopodobne, z uwagi na sporą siłę jaką dysponują więksi providerzy, czy raczej końcowy odbiorca, któremu kodeki zostaną dostarczone w cenie systemu operacyjnego lub sprzętu? A może pojawi się nowy model licencjonowania technologii i strony zaczną pobierać opłaty za każdorazowe odtworzenie multimediów? &quot;Twój kredyt odtworzeń został wyczerpany - aby obejrzeć to video doładuj swoje konto YouTube.&quot; - jak ci się podoba?&lt;/p&gt;
&lt;p&gt;Zwolennicy ponoszenia przez użytkownika opłat za korzystanie z technologii lubią odpowiadać pytaniem: kto zapłaci za jej rozwój? Ja na to zadaję kolejne: kto płaci za rozwój istniejących bezpłatnych technologii? Z całą pewnością Dirac, Theora, Vorbis, Speex i inne nie powstały z powietrza. Mniejsza już o jakiś tam kodek, weźmy grubszą sprawę: kto zapłacił za technologię znaną pod nazwą TCP/IP?&lt;br&gt;
Pozostaje jeszcze argument technologiczny - dlaczego używać słabszego formatu, skoro istnieją lepsze? Otóż te lepsze wymagają całkiem sporej mocy obliczeniowej, co nie jest bez znaczenia w przypadku urządzeń przenośnych (arytmetyka zmiennoprzecinkowa!). Z pewnością lubisz mieć trochę prądu w swojej komórce i jeśli jesteś posiadaczem &lt;a href=&quot;http://images.google.ie/images?q=nokia+6300&quot;&gt;Nokii 3600&lt;/a&gt; przeklinasz konieczność ładowania co drugi dzień. Dlaczego zatem nie pozostawić specyfikacji formatu video bez definicji, by umożliwić odtwarzanie intensywnych obliczeniowo multimediów tym, którzy mogą? Melduję posłusznie obywatelu kapralu, że udzielam odpowiedzi: z tego samego powodu, dla którego znacznik &lt;code&gt;img&lt;/code&gt; ma ściśle sprecyzowaną listę formatów koniec odpowiedzi obywatelu kapralu.&lt;/p&gt;
&lt;p&gt;Tak się akurat szczęśliwie składa, że Google oczekuje sugestii na temat rozwoju serwisu YouTube. Pomóżmy im zatem podjąć właściwą decyzję za pośrednictwem tej oto strony. Krótki formularz zawiera pole odpowiedzi otwartej po zaznaczeniu ostatniego checkboksa. Oto moja:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://gruz.epsi.pl/g/youtube-theora.png&quot; alt=&quot;OGG Theora on YouTube&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://help.youtube.com/support/youtube/bin/request.py?page=&amp;amp;contact_type=suggestion&amp;amp;master=suggestion&amp;amp;Action.Search=Continue&quot;&gt;Teraz twój ruch.&lt;/a&gt;&lt;/p&gt;
</description><pubDate>Thu, 18 Jun 2009 01:24:35 +0200</pubDate><guid>http://stronger.epsi.pl/2009/06/18/theora-bo-mamy-xxi-wiek/</guid><category>pl</category><category>video</category><category>youtube</category><category>theora</category><category>h264</category><category>html5</category></item><item><title>Mała rzecz a cieszy</title><link>http://stronger.epsi.pl/2009/06/14/mala-rzecz-a-cieszy/</link><description>&lt;p&gt;Kolejny dodatek do &lt;a href=&quot;http://folksr.com&quot;&gt;Folksr&lt;/a&gt;-a to filtr tagów. Działa ograniczająco (spójnik AND) – im więcej tagów zaznaczysz tym krótszą listę otrzymasz. Tak jak w Google.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://gruz.epsi.pl/g/folksr/folksr-filter-pl.png&quot; alt=&quot;Filtr tagów&quot;&gt;&lt;/p&gt;
&lt;p&gt;Przy okazji zmiany przerobiłem również sposób sortowania. Teraz działa bez przeładowania strony, wykorzystując Ajax. Na marginesie, czy wspominałem już, że JavaScript to genialny język? ;-)&lt;/p&gt;
</description><pubDate>Sun, 14 Jun 2009 16:54:27 +0200</pubDate><guid>http://stronger.epsi.pl/2009/06/14/mala-rzecz-a-cieszy/</guid><category>pl</category><category>folksr</category><category>filtr</category><category>tagi</category></item><item><title>Folksr - pierwsze dwa tygodnie</title><link>http://stronger.epsi.pl/2009/06/06/folksr-pierwsze-dwa-tygodnie/</link><description>&lt;p&gt;Upływa właśnie drugi tydzień prywatnej bety Folksr-a. Pora na krótkie podsumowanie okresu najgwałtowniejszego (jak na razie) rozwoju.&lt;/p&gt;
&lt;h3&gt;Wasze propozycje, które ziściły się&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Nowy tryb pracy widgetu: &lt;em&gt;Ja, znajomi i wszyscy, którzy mają mnie w swoich znajomych&lt;/em&gt;.&lt;/strong&gt; To ustawienie powoduje, że prezentujesz w widgecie również tych, którzy lubią twój blog i mają go w swojej liście znajomych, pomimo, że nie są na twojej liście.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upload własnego avatara.&lt;/strong&gt; Poprzednio Folksr pobierał mordkę użytkownika z serwisu Gravatar. Teraz możesz załadować własną grafikę.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kto ma mnie w znajomych.&lt;/strong&gt; Na stronie znajomych została dodana lista użytkowników i ich blogów, którzy linkują do ciebie. Dodatkowo, w liście twoich znajomych pojawia się informacja gdy twój znajomy również ma cię na swojej liście.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Powiadomienia pocztą o dodaniu do znajomych.&lt;/strong&gt; Gdy ktoś doda twojego bloga do listy znajomych zostaniesz o tym fakcie powinformowany emailem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;XML-owa sitemapa.&lt;/strong&gt; Dzięki niej Google i inni będą mogli skuteczniej dotrzeć do profili użytkowników i ich blogów. Trochę darmowego page ranku nie zaszkodzi ;-)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Statystyki wejść i wyjść na stronie wpisów.&lt;/strong&gt; Bardzo często zgłaszana funkcja, która wyświetla liczbę przybyć i wybyć z/na inne blogi. Poniżej liczby odsłon pojawiają się mniejszym pismem dwie dodatkowe wartości: wyjścia→wejścia.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sortowanie listy wpisów wg różnych kryteriów.&lt;/strong&gt; Logiczną konsekwencją poprzedniej funkcji wydaje się być szeregowanie stron w oparciu o kolejność zaindeksowania, tytuł (alfabetycznie), ilość wyświetlen oraz wejść i wyjść z/na inne blogi.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To wszystko dzięki waszym zgłoszeniom.&lt;/p&gt;
&lt;h3&gt;Plany na nieodległą przyszłość&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Przepisany ekran edytora skórki&lt;/strong&gt;. Edytor skinu działa poprawnie, ale nie jest szczególnie piękny. Plan obejmuje przeorganizowanie podstrony oraz dodanie biblioteki przykładowych skórek. Podgląd aktualnie tworzonego widgetu będzie dodany w dalszej kolejności. Została zaproponowana nazwa &quot;szablon&quot; w miejsce słowa &quot;skórka&quot;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Wsparcie dla blogów na Blogspocie.&lt;/strong&gt; Mam nadzieję, że wkrótce zmagania z denną dokumentacją od Google zaowocują pojawieniem się pluginu i detektora blogów opartych na silniku Blogger/Blogspot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migracja do innej domeny.&lt;/strong&gt; Trochę wysiłku musi zostać włożone by przeindeksowanie wpisów pod inną domenę było możliwe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Filtrowanie powtórzeń i przeładowań w logach.&lt;/strong&gt; Obecnie każde kliknięcie jest odnotowane, choć nie zawsze powinno. W szczególności przeładowania strony nie powinny liczyć się podwójnie.&lt;/p&gt;
&lt;h3&gt;Garść liczb&lt;/h3&gt;
&lt;p&gt;Od początku prywatnej bety ilość wyświetleń widgetu wzrosła dwukrotnie, do poziomu ok. 2800 odsłon. W sumie tyle razy wasze blogi były oglądane. Dzienna wartość szczytowa to 3813 odsłon. Podobny wzrost dotyczy liczby kliknięć w widget (clickthrough). Obecnie jest ona na poziomie ok. 70 dziennie, co stanowi 2,5% wyświetleń - to lepiej niż w przypadku reklam Google!&lt;/p&gt;
&lt;p&gt;Folksr indeksuje 1731 wpisów z 21 blogów (płodni jesteście!). Liczba zaindeksowanych tagów wynosi 8781, z czego 4004 jest unikalnych. Średnia ilość tagów na post to 5,09 (czyli moje &lt;a href=&quot;http://stronger.epsi.pl/2009/05/29/folksr-algorytm-szukania-podobnych-wpisow/&quot;&gt;wcześniejsze analizy&lt;/a&gt; były trafne).&lt;/p&gt;
&lt;p&gt;Serwer Folksr-a działa pod Ubuntu 8.10 z Apache, PHP i bazą PostgreSQL. Najbardziej skomplikowane złożenie SQL obejmuje 5 tabel + 2 podzapytania z grupowaniem. Obciążenie maszyny oscyluje w granicach 0,15-0,25. Oznacza to, że mamy olbrzymi zapas mocy, którego nie zawahamy się użyć ;-)&lt;/p&gt;
&lt;h3&gt;Beta 2&lt;/h3&gt;
&lt;p&gt;Nadchodzące dni będą dla Folksr-a dalszym ciągiem prywatnej bety. Nadal znajdują się drobnostki, które wymagają naprawy – lepiej gdy zdarzają się kilku osobom niż miałyby dotyczyć setek użytkowników. Z pewnością gdzieś czai się także kilka nieprzetłumaczonych komunikatów (czego zwalczaniem zajmuje się &lt;a href=&quot;http://blog.breffa.pl/&quot;&gt;breffa&lt;/a&gt;, kudosy i creditsy). Ponadto chciałbym pozyskać więcej testerów na platformach WordPress i Blogspot. Po zakończeniu prywatnych testów Folksr będzie miał już wystarczającą ilość zaindeksowanych wpisów, że podpowiedzi widgetu staną się dużo bardziej trafne.&lt;/p&gt;
&lt;p&gt;Dzięki wszystkim dotychczasowym betatesterom, w szczególności tym raportującym błędy i dającym link do Folksr-a na swoich blogach. Możecie podsyłać propozycje ulepszeń, pamiętajcie tylko: Folksr powinien pozostać prosty w obsłudze (w następnych wpisach wyjaśnię dlaczego).&lt;/p&gt;
</description><pubDate>Sat, 06 Jun 2009 21:00:44 +0200</pubDate><guid>http://stronger.epsi.pl/2009/06/06/folksr-pierwsze-dwa-tygodnie/</guid><category>pl</category><category>folksr prywatna beta folksr.com</category></item><item><title>Folksr: algorytm szukania podobnych wpisów</title><link>http://stronger.epsi.pl/2009/05/29/folksr-algorytm-szukania-podobnych-wpisow/</link><description>&lt;p&gt;Program prywatnej bety &lt;a href=&quot;http://folksr.com&quot;&gt;Folksr&lt;/a&gt;-a postępuje znakomicie i odzew społeczności jest ogromnie nagradzający. Szczególne podziękowania należą się &lt;a href=&quot;http://rozie.jogger.pl/&quot;&gt;roziemu&lt;/a&gt; i &lt;a href=&quot;http://blog.breffa.pl/&quot;&gt;breffie&lt;/a&gt; (czy to się odmienia?) za cenne uwagi i raporty. Myślę, że będę Was musiał jakoś wynagrodzić, chłopaki ;-) Ale do rzeczy; oto algorytm używany przez Folksr-a do wyszukiwania podobnych wpisów.&lt;/p&gt;
&lt;h3&gt;Co się stanie jak dodam 1000 tagów?&lt;/h3&gt;
&lt;p&gt;No właśnie. Obliczanie podobieństwa wpisów na podstawie tylko i wyłącznie liczby tagów nie jest lepszym pomysłem. Technologia WWW już to przerabiała w postaci nagłówków &lt;code&gt;meta&lt;/code&gt; z atrybutem &lt;code&gt;keywords&lt;/code&gt;. Pole to szybko stało się na tyle bezużyteczne, że obecnie żadna wyszukiwarka nie bierze go pod uwagę. Folksr ogranicza liczbę indeksowanych tagów do dziesięciu. Jest to przyzwoita liczba, którą można wyrazić esencję treści i zachęca do rzetelnego tagowania swoich wpisów. Jest poza tym zgodna z ogólnymi wzorcami interfejsów użytkownika, gdzie 3-7 to zakres optymalny (jak myślicie, skąd 37signals wzięło swoją nazwę?).&lt;/p&gt;
&lt;h3&gt;Punkty za ilość&lt;/h3&gt;
&lt;p&gt;Każdy zbieżny tag jest punktowany i stanowi równą część wyniku. Załóżmy więc, że popełniłem wpis otagowany jako &lt;code&gt;&lt;u&gt;ubuntu&lt;/u&gt; &lt;u&gt;9.04&lt;/u&gt; &lt;u&gt;jaunty&lt;/u&gt; &lt;u&gt;launch&lt;/u&gt; &lt;u&gt;party&lt;/u&gt; &lt;u&gt;dublin&lt;/u&gt;&lt;/code&gt;. Przyjmijmy też, że dwie inne osoby postanowiły podzielić się z blogsferą podobnymi wpisami. Pierwszy z nich zawiera tagi &lt;code&gt;&lt;u&gt;ubuntu&lt;/u&gt; &lt;u&gt;9.04&lt;/u&gt; release &lt;u&gt;party&lt;/u&gt; katowice&lt;/code&gt;, drugi jest oznaczony &lt;code&gt;&lt;u&gt;ubuntu&lt;/u&gt; &lt;u&gt;9.04&lt;/u&gt; &lt;u&gt;jaunty&lt;/u&gt; jackalope linux unix gnome recenzja&lt;/code&gt;. W obu przypadkach znalezione blogi mają po trzy tagi wspólne z wpisem na blogu gospodarza, ale ten drugi jest dużo szerszy. Jak ocenić który z nich jest trafniejszy?&lt;/p&gt;
&lt;h3&gt;Punkty za jakość&lt;/h3&gt;
&lt;p&gt;Do głosu dochodzi zatem zasada, że nie liczy się ilość lecz jakość. Ponieważ szerzej otagowane wpisy mogą być mniej trafne Folksr pomniejsza wagę wpisów o dużej liczbie tagów. Zapobiec ma to dodawaniu etykiet, które są słabo bądź wcale nie powiązane z tagami na blogu gospodarza. W drugim przypadku przytoczonym powyżej można się zgodzić, że tagi &lt;code&gt;linux unix gnome&lt;/code&gt; są nieco nadmiarowe, a nawet błędne (&lt;code&gt;unix&lt;/code&gt;???)&lt;/p&gt;
&lt;h3&gt;Jakość × ilość&lt;/h3&gt;
&lt;p&gt;Folksr bierze pod uwagę zarówno jakość jak i ilość. Każdy tag ma przyznaną pewną liczbę punktów, ale im więcej tagów tym punktów mniej. Ponieważ liczba tagów ograniczona jest do 10, to dwie skrajne możliwości są następujące:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1 tag za 10 punktów&lt;/li&gt;
&lt;li&gt;10 tagów po jeden punkt każdy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wielkości pośrednie łatwo sobie wyobrazić: 2 tagi po 9 punktów każdy, 3 tagi po 8 punktów każdy, itd. Gdy wartość pojedyńczego tagu jest wyznaczona Folksr przystępuje do zliczania pasujących tagów. Wartość każdej etykiety zostanie pomnożona przez liczbę tagów zgodnych z tagami na blogu gospodarza, wg wzoru:&lt;/p&gt;
&lt;blockquote&gt;wynik = (11 - liczba tagów) × liczba podobnych tagów&lt;/blockquote&gt;
&lt;p&gt;Wracając do wcześniejszych przykładów, wyliczmy punktację dla każdego z nich:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&lt;u&gt;ubuntu&lt;/u&gt; &lt;u&gt;9.04&lt;/u&gt; release &lt;u&gt;party&lt;/u&gt; katowice&lt;/code&gt;&lt;br&gt;
(11 - 5) × 3 = 18&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;u&gt;ubuntu&lt;/u&gt; &lt;u&gt;9.04&lt;/u&gt; &lt;u&gt;jaunty&lt;/u&gt; jackalope linux unix gnome recenzja&lt;/code&gt;&lt;br&gt;
(11 - 8) × 3 = 9&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Zgodnie z przeczuciem, wpis nr 2 został oceniony niżej, natomiast nagrodzona została celność tagów we wpisie nr 1.&lt;/p&gt;
&lt;h3&gt;Punktacja na max&lt;/h3&gt;
&lt;p&gt;Skoro wiemy już, że Folksr nagradza za ilość przekładającą się na jakość, to ile punktów można osiągnąć maksymalnie? Szybki rachunek w arkuszu kalkulacyjnym pokazuje, że najwyższa liczba punktów możliwych do osiągnięcia to 30:&lt;/p&gt;
&lt;table id=&quot;grid&quot;&gt;
&lt;tr&gt;
&lt;th&gt;Liczba trafień&lt;/th&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Wartość taga&lt;/th&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Wynik&lt;/th&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;div style=&quot;text-align: center&quot;&gt;&lt;img src=&quot;http://folksr.com/var/img/graphs/max-score-graph.png&quot; alt=&quot;Wykres&quot;&gt;&lt;/div&gt;
&lt;p&gt;Wartość 30-tu punktów można osiągnąć publikując 5 lub 6 tagów pod warunkiem, że wszystkie z nich są zbieżne z tagami na blogu gospodarza. Całkiem przyzwoite wyniki uzyskuje się również dla 4 lub 7-miu tagów, co może być dość dobrą strategią biorąc pod uwagę trudność utrafienia wszystkich pięciu lub sześciu tagów (twórcy totolotka też potrafią liczyć ;-)&lt;/p&gt;
&lt;h3&gt;Kalkulator punktacji&lt;/h3&gt;
&lt;p&gt;Poniżej zamieszczam szybki kalkulator punktacji napisany w JavaScripcie. Oczywiście nie ma sensu brać go na poważnie – w końcu nie o to chodzi w blogowaniu; niech posłuży tylko jako ilustracja zastosowanego w Folkserze algorytmu i miernik jego jakości.&lt;/p&gt;
&lt;p style=&quot;padding: .5em 1em; background-color: white&quot;&gt;Liczba tagów: &lt;input id=&quot;s&quot; value=&quot;4&quot; maxlength=&quot;2&quot; style=&quot;width: 3em&quot;&gt; × Liczba trafionych: &lt;input id=&quot;h&quot; value=&quot;3&quot; maxlength=&quot;2&quot; style=&quot;width: 3em&quot;&gt; = &lt;span id=&quot;r&quot;&gt;21&lt;/span&gt;&lt;/p&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
//&lt;![CDATA[
var s = document.getElementById('s');
var h = document.getElementById('h');
var r = document.getElementById('r');
var calculate = function() {
        var res = (11 - s.value) * h.value;
        r.innerHTML = (isNaN(res) || h.value &gt; s.value)
                ? '?'
                : res;
}
s.onkeyup = calculate;
h.onkeyup = calculate;
//]]&gt;
&lt;/script&gt;</description><pubDate>Fri, 29 May 2009 10:59:41 +0200</pubDate><guid>http://stronger.epsi.pl/2009/05/29/folksr-algorytm-szukania-podobnych-wpisow/</guid><category>pl</category><category>folksr algorytm podobne wpisy</category></item><item><title>Folksr: empowered by OSNews.pl</title><link>http://stronger.epsi.pl/2009/05/26/folksr-empowered-by-osnews-pl/</link><description>&lt;p&gt;Dzięki uprzejmości &lt;a href=&quot;http://borys.musielak.eu/&quot;&gt;Borysa&lt;/a&gt; z &lt;a href=&quot;http://osnews.pl&quot;&gt;OSNews.pl&lt;/a&gt; Folksr zaczął indeksować treść tego serwisu (&lt;a href=&quot;http://creativecommons.org/licenses/by/2.5/pl/&quot;&gt;Creative Commons Attribution 2.5pl&lt;/a&gt;).&lt;/p&gt;
&lt;div style=&quot;background: white; margin: 0; text-align: center; border: lightgray 1px solid&quot;&gt;&lt;a href=&quot;http://osnews.pl&quot;&gt;&lt;img style=&quot;margin: 1em&quot; src=&quot;http://osnews.pl/stuff/osnews/osnews.png&quot; alt=&quot;OSNews.pl&quot;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Zasoby te z każdym dniem będą rosły, a ponieważ artykuły na OSNews są znakomicie otagowane ich trafność w podpowiedziach będzie wysoka. Oczywiście jeśli nie chcesz newsów z serwisu w swoim widgecie, po prostu nie dodawaj go do znajomych!&lt;/p&gt;
</description><pubDate>Tue, 26 May 2009 03:28:50 +0200</pubDate><guid>http://stronger.epsi.pl/2009/05/26/folksr-empowered-by-osnews-pl/</guid><category>pl</category><category>folksr osnews</category></item><item><title>Folksr - już wkrótce</title><link>http://stronger.epsi.pl/2009/05/13/folksr-juz-wkrotce/</link><description>&lt;div style=&quot;overflow: auto; text-align: center; border: #e0e0e0 1px solid; margin: 1em; padding: 1em; background: white&quot;&gt;&lt;img src=&quot;http://folksr.com/var/img/logo/logo-220x120.png&quot; alt=&quot;Folksr&quot; style=&quot;margin: 0 auto&quot;&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Folksr&lt;/strong&gt; nadchodzi, jest tuż za rogiem. Spodziewaj się zaproszenia – tak tak, Ty!&lt;/p&gt;
</description><pubDate>Wed, 13 May 2009 13:04:56 +0200</pubDate><guid>http://stronger.epsi.pl/2009/05/13/folksr-juz-wkrotce/</guid><category>pl</category><category>folksr beta</category></item></channel></rss>