<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule">

<channel>
	<title>Room 303</title>
	<atom:link href="http://room-303.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://room-303.com/blog</link>
	<description>Geek in the Shell: Redesigning the Web</description>
	<lastBuildDate>Tue, 26 May 2009 08:38:20 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<creativeCommons:license>http://creativecommons.org/licenses/by-nc-sa/2.5/pl/</creativeCommons:license>		<item>
		<title>Zaadoptuj hakera i zostań Przyjacielem GNOME</title>
		<link>http://room-303.com/blog/2009/05/26/zaadoptuj-hakera-i-zostan-przyjacielem-gnome/</link>
		<comments>http://room-303.com/blog/2009/05/26/zaadoptuj-hakera-i-zostan-przyjacielem-gnome/#comments</comments>
		<pubDate>Tue, 26 May 2009 08:38:20 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[gnome]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=621</guid>
		<description><![CDATA[Jesteś fanem wolnego oprogramowania (nie mylić z powolnym) i używasz GNOME? Na co dzień pracujesz z innym systemem, ale wykorzystujesz aplikacje oparte na GTK+? A może obudził się w tobie instynkt filantropa? Jeśli tak, to Fundacja GNOME z radością przyjmie twoje pieniądze (i jeśli kiedyś postanowiłeś mi kupić piwo, to nie czekaj i podaruj te [...]]]></description>
			<content:encoded><![CDATA[<p>Jesteś fanem wolnego oprogramowania (nie mylić z powolnym) i używasz <a href="http://www.gnome.org/">GNOME</a>? Na co dzień pracujesz z innym systemem, ale wykorzystujesz aplikacje oparte na GTK+? A może obudził się w tobie instynkt filantropa? Jeśli tak, to Fundacja GNOME z radością przyjmie twoje pieniądze (i jeśli kiedyś postanowiłeś mi kupić piwo, to nie czekaj i <a href="http://www.gnome.org/friends/">podaruj te pieniądze fundacji</a>)!</p>
<p class="strip simple"><a href="http://www.gnome.org/friends/"><img src="http://room-303.com/blog/wp-content/uploads/2009/05/fog-badges.png" alt="Support GNOME" title="Friends of GNOME" width="450" height="235" class="size-full wp-image-622" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2009/05/26/zaadoptuj-hakera-i-zostan-przyjacielem-gnome/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Python, Whoosh i błędy</title>
		<link>http://room-303.com/blog/2009/05/08/python-whoosh-i-bledy/</link>
		<comments>http://room-303.com/blog/2009/05/08/python-whoosh-i-bledy/#comments</comments>
		<pubDate>Fri, 08 May 2009 13:46:15 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[haystack]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[whoosh]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=609</guid>
		<description><![CDATA[Tytułem wstępu: Whoosh to bardzo przyjemny silnik indeksujący i wyszukujący dokumenty w trybie full-text. Niestety, masa ludzi ma problemy z jego wdrożeniem.
Objawy
Główne objawy to:
OSError: [Errno 17] File exists: '/path/to/index/cache/_MAIN_LOCK'

oraz:
IOError: [Errno 2] No such file or directory: '/path/to/index/cache/_MAIN_123.tiz'

Oba z nich są na ogół różnymi objawami faktu, że więcej niż jeden proces próbuje używać tego samego indeksu [...]]]></description>
			<content:encoded><![CDATA[<p>Tytułem wstępu: <a href="http://whoosh.ca/">Whoosh</a> to bardzo przyjemny silnik indeksujący i wyszukujący dokumenty w trybie <q>full-text</q>. Niestety, masa ludzi ma problemy z jego wdrożeniem.</p>
<h4>Objawy</h4>
<p>Główne objawy to:</p>
<blockquote><pre>OSError: [Errno 17] File exists: '/path/to/index/cache/_MAIN_LOCK'</pre>
</blockquote>
<p>oraz:</p>
<blockquote><pre>IOError: [Errno 2] No such file or directory: '/path/to/index/cache/_MAIN_123.tiz'</pre>
</blockquote>
<p>Oba z nich są na ogół różnymi objawami faktu, że więcej niż jeden proces próbuje używać tego samego indeksu w tym samym czasie. Główną przyczyną jest nieświadomość tego, że <strong>Whoosh nie gwarantuje bezpieczeństwa wątków</strong>.</p>
<h4>Rozwiązanie pierwsze: gdy używasz Haystack</h4>
<p>Ponieważ zdecydowana większość użytkowników nie odwołuje się bezpośrednio do <abbr>API</abbr> Whoosh, a korzysta z niego za pośrednictwem modułu <a href="http://haystacksearch.org/">Haystack</a>, najpierw przedstawię rozwiązanie dla nich.</p>
<p>Jak <a href="http://groups.google.com/group/django-haystack/browse_thread/thread/97109b65a6d725f3">ustaliliśmy</a> przedwczoraj z autorem Haystack, przykładowy backend dostarczany dla Whoosh błędnie zakłada bezpieczeństwo wątków tego ostatniego.</p>
<p>Dodatkowo, Haystack używa <q>leniwych obiektów</q> (obiektów z odwleczoną inicjalizacją) do ładowania indeksów, co powoduje, że nawet przy zachowaniu bezpieczeństwa wątków, <em>aplikacja nie będzie działać prawidłowo w środowisku typu <q>prefork</q></em>.</p>
<p>Najprawdopodobniej w przyszłości Haystack będzie uruchamiał Whoosh w postaci usługi systemowej, co pozwoli na wielodostęp do tego samego indeksu i tym samym zlikwiduje problemy związane z rozgałęzianiem procesów i wątkowaniem. Póki co, rozwiązanie składa się z dwóch kroków:</p>
<ol>
<li>
<p>Nałożenie na Haystack mojej łatki, która zapewnia bezpieczeństwo wątków: <a href='http://room-303.com/blog/wp-content/uploads/2009/05/haystack-whoosh.patch'>haystack-whoosh.patch</a></p>
</li>
<li>Upewnienie się, że Django działa w serwerze wątkowanym, a nie forkowanym. Dla używających <code>manage.py</code> sprowadza się to do dopisania parametru <code>method</code>:<br />
<blockquote><pre>python manage.py runfcgi method=threaded ...</pre>
</blockquote>
</li>
</ol>
<h4>Rozwiązanie drugie: gdy bezpośrednio wołasz Whoosh</h4>
<p>Tutaj gotowej łatki podać nie mogę, bo każdy pisze kod po swojemu. Wystarczy jednak &mdash; podobnie jak zrobiłem to w łatce dla Haystack &mdash; użyć mechanizmu <code>threading.Lock</code> i ogrodzić wszystkie wywołania API wspólną blokadą (czy może ryglem, jak czasem jest to słowo tłumaczone). Na przykład:</p>
<blockquote><pre><code>import threading

whoosh_api = threading.Lock()

def do_something():
	whoosh_api.acquire()
	try:
		# use API
	finally:
		whoosh_api.release()</code></pre>
</blockquote>
<p>Należy się również upewnić, że w danej chwili istnieje tylko jeden obiekt typu <code>whoosh.index.Index</code> na każdy katalog indeksu, a także maksymalnie jedna instancja <code>whoosh.writing.IndexWriter</code>. Pierwszy przypadek rozwiązuje implementacja singletona, drugi &mdash; ogrodzenie całego cyklu życia obiektu (aż do pomyślnego wywołania metody <code>commit</code> lub <code>cancel</code>) wewnątrz tej samej blokady, co reszta wywołań <abbr>API</abbr>.</p>
]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2009/05/08/python-whoosh-i-bledy/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Wstęp do kultury: spam</title>
		<link>http://room-303.com/blog/2009/05/07/wstep-do-kultury-spam/</link>
		<comments>http://room-303.com/blog/2009/05/07/wstep-do-kultury-spam/#comments</comments>
		<pubDate>Thu, 07 May 2009 12:29:43 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[varia]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=604</guid>
		<description><![CDATA[Pewni spamerzy wysłali mi właśnie taki potworek:
Szanowni Panstwo !
Biuro Tlumaczen MEGALING oferujace niedrogie tlumaczenia zwraca sie do
Panstwa firmy z prosba o wyrazenie zgody na otrzymanie droga elektroniczna
informacji handlowych na temat produktów naszej firmy.
Jezeli wyrazaja Panstwo zgode na przeslanie przez nas droga elektroniczna
informacji handlowych w rozumieniu ustawy z dnia 18 lipca 2002 roku o
swiadczeniu uslug droga [...]]]></description>
			<content:encoded><![CDATA[<p>Pewni <a href="http://megaling.com.pl/">spamerzy</a> wysłali mi właśnie taki potworek:</p>
<blockquote><p>Szanowni Panstwo !</p>
<p>Biuro Tlumaczen MEGALING oferujace niedrogie tlumaczenia zwraca sie do<br />
Panstwa firmy z prosba o wyrazenie zgody na otrzymanie droga elektroniczna<br />
informacji handlowych na temat produktów naszej firmy.</p>
<p>Jezeli wyrazaja Panstwo zgode na przeslanie przez nas droga elektroniczna<br />
informacji handlowych w rozumieniu ustawy z dnia 18 lipca 2002 roku o<br />
swiadczeniu uslug droga elektroniczna (Dz. U. Nr 144, poz. 1204 ze zm.)<br />
prosimy o wyslanie pustego maila zwrotnego z tematem TAK.</p>
</blockquote>
<p>Oczywiście wierzę, że nie mówimy tu o masowym zbieraniu i przetwarzaniu adresów poczty elektronicznej. Wszak <del>spamerzy</del> <ins>tłumacze</ins> z całą pewnością moje dane znaleźli w katalogu przedsiębiorców, gdyż jak powszechnie wiadomo, mój blog jest zarejestrowaną czternastoosobową spółką akcyjną, na giełdzie jest notowany pod symbolem <code>CYCKI</code> i od lat w nagłówku wyświetla własny identyfikator <abbr>KRS</abbr>. Oczywiście.</p>
<p>Drogie dzieciaczki (ton celowo protekcjonalny, dzięki temu powstrzymuję się od użycia co dobitniejszych epitetów), jeśli wysyłacie zapytanie o zgodę na spamowanie, to macie obowiązek podać pełne dane podmiotu, który o taką zgodę prosi. Co ważniejsze jednak, wasze <em>zapytanie nie może zawierać samej oferty</em>. Dla zobrazowania, scenka rodzajowa.</p>
<blockquote><p>— Czy pozwoli Pan, że napiszę mu, że oferujemy tanie tłumaczenia?</p>
<p>— Nie pozwolę.</p>
<p>— Nie jest Pan zatem zainteresowany informacją, że tłumaczymy za jedyne 25 PLN od strony?</p>
<p>— Obawiam się, że nie.</p>
<p>— Jeśli nie interesują pana tłumaczenia pod przysięgą, być może zezwoli pan na wysłanie oferty dotyczącej <q>sprzedam Opla, rocznik 1998, stan dobry, lekko tłuczony</q>?</p>
</blockquote>
<p>cieszmy się jednak, że taka <q>logika</q> merketoidiotów ogranicza się do reklam.</p>
<blockquote><p>— Czy pozwoli Pan, żebym kopnął, o tak?</p>
<p>— Aaaa, moje jaja!</p>
<p>— Rozumiem. Nie jest pan zainteresowany, zatem powstrzymam się od przemocy.</p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2009/05/07/wstep-do-kultury-spam/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Django hack: wiele domen i jedna instancja</title>
		<link>http://room-303.com/blog/2009/05/05/django-hack-wiele-domen-i-jedna-instancja/</link>
		<comments>http://room-303.com/blog/2009/05/05/django-hack-wiele-domen-i-jedna-instancja/#comments</comments>
		<pubDate>Tue, 05 May 2009 22:40:16 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=599</guid>
		<description><![CDATA[Pozwala serwować wiele domen za pomocą jednego tylko projektu Django. Inspiracją był ten snippet.
Zmiany w settings.py:
from threading import local

SITE_THREAD_INFO = local()
SITE_THREAD_INFO.SITE_ID = 1

class SiteIDHook(object):
	def __int__(self):
		return SITE_THREAD_INFO.SITE_ID
	def __hash__(self):
		return SITE_THREAD_INFO.SITE_ID

SITE_ID = SiteIDHook()

Nowa klasa middleware:
from django.conf import settings
from django.contrib.sites.models import Site
from django.http import HttpResponseRedirect

class MultiSiteMiddleware(object):
	def process_request(self, request):
		settings.SITE_THREAD_INFO.SITE_ID = 0 # Fail by default

		host = request.META.get('HTTP_HOST').split(':')[0]
		if host:
			try:
				site = Site.objects.get(domain [...]]]></description>
			<content:encoded><![CDATA[<p>Pozwala serwować wiele domen za pomocą jednego tylko projektu Django. Inspiracją był <a href="http://www.djangosnippets.org/snippets/1099/">ten snippet</a>.</p>
<p>Zmiany w <code>settings.py</code>:</p>
<blockquote><pre><code>from threading import local

SITE_THREAD_INFO = local()
SITE_THREAD_INFO.SITE_ID = 1

class SiteIDHook(object):
	def __int__(self):
		return SITE_THREAD_INFO.SITE_ID
	def __hash__(self):
		return SITE_THREAD_INFO.SITE_ID

SITE_ID = SiteIDHook()</code></pre>
</blockquote>
<p>Nowa klasa middleware:</p>
<blockquote><pre><code>from django.conf import settings
from django.contrib.sites.models import Site
from django.http import HttpResponseRedirect

class MultiSiteMiddleware(object):
	def process_request(self, request):
		settings.SITE_THREAD_INFO.SITE_ID = 0 # Fail by default

		host = request.META.get('HTTP_HOST').split(':')[0]
		if host:
			try:
				site = Site.objects.get(domain = host)
				settings.SITE_THREAD_INFO.SITE_ID = site.id
			except Site.DoesNotExist:
				return HttpResponseRedirect('http://example.com/')</code></pre>
</blockquote>
<p>Bonus &mdash; automatyczne fitrowanie obiektów:</p>
<p><bloclquote>
<pre><code>from django.contrib.sites.models import Site
from django.db import models

def create_custom_manager(*args, **kwargs):
	class CustomManager(models.Manager):
		def get_query_set(self):
			return super(CustomManager, self).get_query_set().filter(*args, **kwargs)
	return CustomManager()

class Product(models.Model):
	site = models.ForeignKey(Site, default = Site.objects.get_current)
	# ...
	objects = create_custom_manager(site = Site.objects.get_current)</code></pre>
<p></bloclquote></p>
]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2009/05/05/django-hack-wiele-domen-i-jedna-instancja/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django hack: Grouped Model Choice Field</title>
		<link>http://room-303.com/blog/2009/04/22/django-hack-grouped-model-choice-field/</link>
		<comments>http://room-303.com/blog/2009/04/22/django-hack-grouped-model-choice-field/#comments</comments>
		<pubDate>Wed, 22 Apr 2009 10:05:40 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=592</guid>
		<description><![CDATA[Przypuśćmy, że nasza aplikacja pozwala kupić koszulkę. Dostępne kolory zależą od rozmiaru i trzeba pozwolić je jakoś wybrać. Dwa selecty? Będzie się dało wybrać nieistniejącą kombinację. Łączony select? Nieczytelnie, chyba że&#8230;
To tylko przykład:
białyniebieskiczerwonyniebieskiżółty

Poniższe rozwiązanie używa klasy GroupedSelect pochodzącej z Django Snippets.
from django.forms import ModelChoiceField, ValidationError
from django.forms.util import smart_unicode
from django.utils.itercompat import groupby
from operator import attrgetter

class GroupedModelChoiceIterator(object):
 [...]]]></description>
			<content:encoded><![CDATA[<p>Przypuśćmy, że nasza aplikacja pozwala kupić koszulkę. Dostępne kolory zależą od rozmiaru i trzeba pozwolić je jakoś wybrać. Dwa <code>select</code>y? Będzie się dało wybrać nieistniejącą kombinację. Łączony select? Nieczytelnie, chyba że&hellip;</p>
<blockquote><p><label>To tylko przykład:<br />
<select><optgroup label="M"><option>biały</option><option>niebieski</option></optgroup><optgroup label="XL"><option>czerwony</option><option>niebieski</option><option>żółty</option></optgroup></select>
<p></label></p></blockquote>
<p>Poniższe rozwiązanie używa klasy <code>GroupedSelect</code> <a href="http://www.djangosnippets.org/snippets/200/">pochodzącej z Django Snippets</a>.</p>
<blockquote><pre><code>from django.forms import ModelChoiceField, ValidationError
from django.forms.util import smart_unicode
from django.utils.itercompat import groupby
from operator import attrgetter

class GroupedModelChoiceIterator(object):
    def __init__(self, field, grouper):
		self.field = field
		self.queryset = field.queryset
		self.grouper = grouper

    def __iter__(self):
		if self.field.empty_label is not None:
			yield (None, ((u'', self.field.empty_label), ))
		for grouper, items in groupby(self.queryset.all(), attrgetter(self.grouper)):
			yield (unicode(grouper), tuple((o.pk, unicode(o)) for o in items))

class GroupedModelChoiceField(ModelChoiceField):
	def __init__(self, field, widget = GroupedSelect, *args, **kwargs):
		self._field = field
		super(GroupedModelChoiceField, self).__init__(widget = widget, *args, **kwargs)

	def _get_choices(self):
		return GroupedModelChoiceIterator(self, self._field)

	choices = property(_get_choices, None)

	def clean(self, value):
		"""
		Validates that the input is in self.choices.
		"""
		if value in (None, ''):
			value = u''
		value = smart_unicode(value)
		valid_values = []
		for group_label, group in self.choices:
			valid_values += [str(k) for k, v in group]
		if value not in valid_values:
			raise ValidationError(u'Select a valid choice. That choice is not one of the available choices.')
		return super(GroupedModelChoiceField, self).clean(value)</code></pre>
</blockquote>
<p>Przykład (zakładając, że <code>color</code> jest referencją na obiekt, który posiada pole <code>size</code>):</p>
<blockquote><p><code>
<pre>class SizeAndColorForm(forms.ModelForm):
	class Meta:
		model = Product
		fields = ['color']
	color = GroupedModelChoiceField(field = 'size', label = u'size/color')</pre>
<p></code></p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2009/04/22/django-hack-grouped-model-choice-field/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Wstęp do kultury: ten komentarz ewentualnie wyjebać</title>
		<link>http://room-303.com/blog/2009/04/02/wstep-do-kultury-ten-komentarz-ewentualnie-wyjebac/</link>
		<comments>http://room-303.com/blog/2009/04/02/wstep-do-kultury-ten-komentarz-ewentualnie-wyjebac/#comments</comments>
		<pubDate>Thu, 02 Apr 2009 15:13:37 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=580</guid>
		<description><![CDATA[Jeśli ktoś przegapił dyskusję na Blipie, to z pewnością powinien nadrobić i zapoznać się ze startupem Wieczne Odpoczywanie.
Uwaga: notka zawiera słownictwo nieodpowiednie dla osób, które nie sięgają brodą do klawiatury.

Tu właściwie mógłbym zakończyć.
Jaka mądrość płynie z powyższego fragmentu kodu? Nie ma żadnego powodu, żeby komentarze przeznaczone dla ekipy opuszczały serwer. Ładnie, ale to się nie [...]]]></description>
			<content:encoded><![CDATA[<p>Jeśli ktoś przegapił <a href="http://blip.pl/s/8524980">dyskusję na Blipie</a>, to z pewnością powinien nadrobić i zapoznać się ze startupem <a href="http://wieczne-odpoczywanie.pl/">Wieczne Odpoczywanie</a>.</p>
<p><strong>Uwaga:</strong> notka zawiera słownictwo nieodpowiednie dla osób, które nie sięgają brodą do klawiatury.</p>
<p class="strip"><a href="http://www.flickr.com/photos/patrys/3407151684/" title="Wieczne odpoczywanie by patrys, on Flickr"><img src="http://farm4.static.flickr.com/3610/3407151684_82224c2079_o.png" width="560" height="323" alt="Wieczne odpoczywanie" /></a></p>
<p>Tu właściwie mógłbym zakończyć.</p>
<p>Jaka mądrość płynie z powyższego fragmentu kodu? <em>Nie ma żadnego powodu, żeby komentarze przeznaczone dla ekipy opuszczały serwer</em>. Ładnie, ale to się nie sprawdza. Podejrzewam wręcz, że autorowi w ogóle do głowy nie przyszło, że da się to odczytać poza serwerem.</p>
<p>Bardziej ogólna wersja tego przypadku wygląda tak:</p>
<blockquote><pre><code>if impossible:
    print 'FUCK!!1!' # ← this will never happen</code></pre>
</blockquote>
<p>Prawdziwy wniosek jest taki: <em><strong>nigdy</strong> nie umieszczaj w programie kodu, którego efekt działania jest kompromitujący</em>. I nie dotyczy to tylko komentarzy w <abbr>HTML</abbr>. Główni podejrzani to przede wszystkim różnego rodzaju <q>niemożliwe</q> asercje (błędy typu <q>wyjebało się połączenie do bazy</q> okazują się całkiem możliwe po telefonie od klienta).</p>
<p>Nie zapominajmy też o jednej z najpopularniejszych metod debugowania, żartobliwie ochrzczonej przeze mnie <em>chujotestem</em> (polega na wstawianiu w różne miejsca w kodzie właściwej dla danego języka instrukcji drukującej nazwę pewnej popularnej wśród pań części ciała; choć brzmi całkiem niepoważnie, jest bardzo skuteczną metodą śledzenia przepływu sterowania w programowaniu wielowątkowym).</p>
<p>Chcę też zastrzec, że o ile to właśnie religijna tematyka serwisu sprawia, że komentarze w kodzie stają się tak zabawne, to nie chcę, żeby ktoś odebrał to jako nagonkę na jakikolwiek kościół (w sensie organizacji, nie budynku). Miejsce na dyskusje religijne jest pod <a href="http://room-303.com/blog/2009/04/01/wstep-do-kultury-ateizm/">wczorajszą notką</a>.</p>
<p>PS: Kod <abbr>HTML</abbr> wspomnianego wyżej serwisu nadaje się na całą serię notek, celowo na zrzucie ekranu pozostawiłem niezwykle interesującą metodę ukrywania elementów. Ktoś umie zrobić to bardziej absurdalnie? Aż prosi się o <abbr>XML</abbr>.</p>
]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2009/04/02/wstep-do-kultury-ten-komentarz-ewentualnie-wyjebac/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Wstęp do kultury: ateizm</title>
		<link>http://room-303.com/blog/2009/04/01/wstep-do-kultury-ateizm/</link>
		<comments>http://room-303.com/blog/2009/04/01/wstep-do-kultury-ateizm/#comments</comments>
		<pubDate>Wed, 01 Apr 2009 08:21:15 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[varia]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=572</guid>
		<description><![CDATA[— W jakiego koloru butach lubisz jeździć na nartach?
— Nie jeżdżę na nartach.
— Głupi jesteś? Pytałam o kolor!

Bez sensu, prawda? A może tak:
— Jakiego jesteś wyznania?
— Jestem ateistą.
— To niemożliwe, każdy w coś wierzy!

Brzmi znajomo? Jeśli oczekujesz od innych, że będą tolerować przeróżne historie o kosmicznym zombie, który jednocześnie jest własnym ojcem i swoim niewidzialnym [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>— W jakiego koloru butach lubisz jeździć na nartach?</p>
<p>— Nie jeżdżę na nartach.</p>
<p>— Głupi jesteś? Pytałam o kolor!</p>
</blockquote>
<p>Bez sensu, prawda? A może tak:</p>
<blockquote><p>— Jakiego jesteś wyznania?</p>
<p>— Jestem ateistą.</p>
<p>— To niemożliwe, każdy w coś wierzy!</p>
</blockquote>
<p>Brzmi znajomo? Jeśli oczekujesz od innych, że będą tolerować przeróżne historie o <em>kosmicznym zombie, który jednocześnie jest własnym ojcem i swoim niewidzialnym przyjacielem, dał się sfragować, zrespawnował i codziennie z tobą czatuje</em>, zrozum, że kogoś innego może to interesować tyle, co wampiry i wilkołaki.</p>
<p>Religie, nawracania, dżihady — wielki konwent, na którym spotkali się najbardziej oddani fani Batmana i Supermana. Kto jest potężniejszy? Który by wygrał? Tak właśnie to wygląda z boku. A mimo to nie każę ci palić kościołów, synagog i meczetów. Niektórzy potrzebują w coś wierzyć, więc jeśli lubisz narty, to baw się dobrze.</p>
<p>Pamiętaj tylko o jednym: tak jak niejeżdżenie na nartach nie jest kolorem, tak <em>ateizm nie jest religią</em>. A skoro nie jest, to <em>przestań mnie namawiać na jej zmianę</em>.</p>
]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2009/04/01/wstep-do-kultury-ateizm/feed/</wfw:commentRss>
		<slash:comments>73</slash:comments>
		</item>
		<item>
		<title>Wstęp do kultury: listy dyskusyjne</title>
		<link>http://room-303.com/blog/2009/03/29/wstep-do-kultury-listy-dyskusyjne/</link>
		<comments>http://room-303.com/blog/2009/03/29/wstep-do-kultury-listy-dyskusyjne/#comments</comments>
		<pubDate>Sun, 29 Mar 2009 15:09:03 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=568</guid>
		<description><![CDATA[Hello Patryk Zawadzki,
I use Boxbe to protect my email address. While I did receive your email about Re: [Bluez-devel] [Bluez-users] Motorola S9 and BlueZ 4.14, you are not currently on my email Guest List. I&#8217;ll be more likely to see your email and future messages if you are on my priority Guest List.
Click here to [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>Hello Patryk Zawadzki,</p>
<p>I use Boxbe to protect my email address. While I did receive your email about <q>Re: [Bluez-devel] [Bluez-users] Motorola S9 and BlueZ 4.14</q>, you are not currently on my email Guest List. I&#8217;ll be more likely to see your email and future messages if you are on my priority Guest List.</p>
<p>Click here to be put directly on my Guest List</p>
</blockquote>
<p>Taki właśnie komunikat widzę ostatnimi czasy codziennie. Co ciekawe, śmieci od <a href="http://www.boxbe.com/" rel="nofollow">Boxbe</a> dotyczą wiadomości wysłanych na listy dyskusyjne. Pół roku temu.</p>
<p>Ludzie, takie wynalazki możecie sobie podłączać do <q>ochrony</q> korespondencji z ciocią, na listach kwalifikują was na wypisanie i dożywotni zakaz wstępu. Podobnie jak każdy inny rodzaj autorespondera. Tych czterysta osób jakoś obejdzie się bez informacji, że od zeszłego wtorku do przyszłej środy opalacie dupę na Majorce i zastępuje was pani Kazia z pokoju 102.</p>
]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2009/03/29/wstep-do-kultury-listy-dyskusyjne/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GNOME 2.26</title>
		<link>http://room-303.com/blog/2009/03/19/gnome-226/</link>
		<comments>http://room-303.com/blog/2009/03/19/gnome-226/#comments</comments>
		<pubDate>Thu, 19 Mar 2009 09:19:23 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[gnome]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=564</guid>
		<description><![CDATA[
Co nowego? Dostępne we wszystkich dobrych dytrybucjach (z PLD włącznie).
]]></description>
			<content:encoded><![CDATA[<p class="strip simple"><a href="http://www.flickr.com/photos/patrys/3367741580/" title="GNOME 2.26 by patrys, on Flickr"><img src="http://farm4.static.flickr.com/3633/3367741580_8fc6a571eb_o.png" width="600" height="200" alt="GNOME 2.26" /></a></p>
<p><a href="http://library.gnome.org/misc/release-notes/2.26/index.html.pl">Co nowego?</a> Dostępne we wszystkich dobrych dytrybucjach (z <a href="http://pld-linux.org/">PLD</a> włącznie).</p>
]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2009/03/19/gnome-226/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Django hack: OrderedForm</title>
		<link>http://room-303.com/blog/2009/03/12/django-hack-orderedform/</link>
		<comments>http://room-303.com/blog/2009/03/12/django-hack-orderedform/#comments</comments>
		<pubDate>Thu, 12 Mar 2009 12:35:41 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=559</guid>
		<description><![CDATA[Ile razy ręcznie składałem formularz w szablonie tylko po to, żeby pola były we właściwej kolejności? Nie mam pojęcia, ale od jakiegoś czasu stosuję poniższe rozwiązanie. Tak, to jest jedna z tych notek ku własnej pamięci.
class OrderedForm(object):
	def __init__(self, *args, **kwargs):
		super(OrderedForm, self).__init__(*args, **kwargs)
		if hasattr(self.Meta, 'fields_order'):
			self.fields.keyOrder = self.Meta.fields_order

Po umieszczeniu powyższego w jakimś ustronnym wspólnym module można zabrać [...]]]></description>
			<content:encoded><![CDATA[<p>Ile razy ręcznie składałem formularz w szablonie tylko po to, żeby pola były we właściwej kolejności? Nie mam pojęcia, ale od jakiegoś czasu stosuję poniższe rozwiązanie. Tak, to jest jedna z tych notek <em>ku własnej pamięci</em>.</p>
<blockquote><pre><code>class OrderedForm(object):
	def __init__(self, *args, **kwargs):
		super(OrderedForm, self).__init__(*args, **kwargs)
		if hasattr(self.Meta, 'fields_order'):
			self.fields.keyOrder = self.Meta.fields_order</code></pre>
</blockquote>
<p>Po umieszczeniu powyższego w jakimś ustronnym wspólnym module można zabrać się za używanie:</p>
<blockquote><pre><code>from django.contrib.auth.models import User
from django import forms
from core.forms import OrderedForm

class UserNewForm(OrderedForm, forms.ModelForm):
	class Meta:
		model = User
		fields = ('first_name', 'last_name', 'username', 'password', 'email')
		fields_order = ('username', 'password', 'first_name', 'last_name', 'email')
	password = forms.CharField(label = u'Password', required = True, widget = forms.PasswordInput(attrs = {'autocomplete': 'off'}))</code></pre>
</blockquote>
<p>Trzeba zauważyć, że <code>OrderedForm</code> celowo nie dziedziczy po żadnej innej klasie Django, dzięki czemu równie dobrze działa z <code>forms.ModelForm</code>, co z <code>forms.Form</code>, jak i z dowolną własną (zgodną) implementacją.</p>
]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2009/03/12/django-hack-orderedform/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 2.157 seconds -->
<!-- Cached page served by WP-Cache -->
