<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Jaros&#322;aw Zabie&#322;&#322;o - BLOG: Tag pylons</title>
    <link>http://blog.zabiello.com/articles/tag/pylons</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>moje notatki, linki, komentarze</description>
    <item>
      <title>Szybki start z Rails, Merb i Pylons</title>
      <description>&lt;p&gt;Celem tego tekstu jest por&#243;wnanie podstawowych czynno&#347;ci niezb&#281;dnych do tego aby uruchomi&#263; Rails, Merb oraz Pylons. Zak&#322;adam &#380;e u&#380;ywany b&#281;dzie&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://rubyonrails.org"&gt;Rails&lt;/a&gt; (v2.0.1) + &lt;a href="http://ar.rubyonrails.org/"&gt;Active Record&lt;/a&gt; + &lt;a href="http://haml.hamptoncatlin.com/"&gt;Haml&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://merbivore.com/"&gt;Merb&lt;/a&gt; (v0.4.2)+ &lt;a href="http://code.google.com/p/ruby-sequel/"&gt;Sequel&lt;/a&gt; (v0.4.3) +  &lt;a href="http://haml.hamptoncatlin.com/"&gt;Haml&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://pylonshq.com"&gt;Pylons&lt;/a&gt;  (v0.9.6.1) + &lt;a href="http://sqlalchemy.org"&gt;SQLAlchemy&lt;/a&gt;  + &lt;a href="http://makotemplates.org"&gt;Mako&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;Wpierw musimy (je&#347;li jeszcze nie mamy) poinstalowa&#263; stosowne biblioteki. W wypadku Rails i Merba musimy mie&#263; Rubiego i RubyGems oraz doinstalowane gemy:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;gem install -y rails haml merb sequel merb_sequel&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;W wypadku Pylons musimy mie&#263; zainstalowanego Pythona oraz pakiet setuptools daj&#261;cy mo&#380;liwo&#347;&#263; u&#380;ywania skryptu easy_install (taki odpowiednik RubyGems dla Pythona)&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;easy_install Pylons SQLAlchemy Mako&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h2&gt;Tworzenie projektu:&lt;/h2&gt;


	&lt;h3&gt;Rails&lt;/h3&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;rails rails_app&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h3&gt;Merb&lt;/h3&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;merb merb_app&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h3&gt;Pylons&lt;/h3&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;paster create -t pylons pylons_app&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h2&gt;Podpi&#281;cie do bazy&lt;/h2&gt;


	&lt;h3&gt;Rails&lt;/h3&gt;


	&lt;p&gt;Nale&#380;y w pliku config/database.yml wpisa&#263;:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_yaml "&gt;&lt;span class="key"&gt;shared&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="anchor"&gt;&amp;amp;shared&lt;/span&gt;  
  &lt;span class="key"&gt;adapter&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; mysql
  &lt;span class="key"&gt;encoding&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; utf8
  &lt;span class="key"&gt;username&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; login
  &lt;span class="key"&gt;socket&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; /opt/local/var/run/mysql5/mysqld.sock
  &lt;span class="key"&gt;password&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; haslo
&lt;span class="key"&gt;development&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
  &lt;span class="key"&gt;database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; rails_app
  &amp;lt;&amp;lt;: *shared
&lt;span class="key"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
  &lt;span class="key"&gt;database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; test
  &amp;lt;&amp;lt;: *shared
&lt;span class="key"&gt;production&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
  &lt;span class="key"&gt;database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; rails_app
  &amp;lt;&amp;lt;: *shared&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h3&gt;Merb&lt;/h3&gt;


Tu jest tak samo jak w Rails z t&#261; r&#243;&#380;nic&#261;, &#380;e nie ma tego pliku. Nale&#380;y stworzy&#263; Plik config/database.yml:
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_yaml "&gt;&lt;span class="document"&gt;---
  shared: &amp;amp;shared  &lt;/span&gt;
    &lt;span class="symbol"&gt;:adapter&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; mysql
    &lt;span class="symbol"&gt;:encoding&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; utf8
    &lt;span class="symbol"&gt;:username&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; login
    &lt;span class="symbol"&gt;:socket&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; /opt/local/var/run/mysql5/mysqld.sock
    &lt;span class="symbol"&gt;:password&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; haslo
  &lt;span class="key"&gt;development&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
    &lt;span class="symbol"&gt;:database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; merb_app
    &amp;lt;&amp;lt;: *shared
  &lt;span class="key"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
    &lt;span class="symbol"&gt;:database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; test
    &amp;lt;&amp;lt;: *shared
  &lt;span class="key"&gt;production&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
    &lt;span class="symbol"&gt;:database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; merb_app
    &amp;lt;&amp;lt;: *shared&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h3&gt;Pylons&lt;/h3&gt;


	&lt;p&gt;Tu, niestety, jest wi&#281;ksza zabawa, bo zar&#243;wno Pylons jak i SQLAlchemy s&#261; bardziej z&#322;o&#380;one.&lt;/p&gt;


	&lt;p&gt;W pliku development.ini wstawiamy:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;sqlalchemy&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;url&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mysql&lt;/span&gt;&lt;span class="punct"&gt;:/&lt;/span&gt;&lt;span class="regex"&gt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="symbol"&gt;:haslo@localhost&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;&lt;span class="number"&gt;3306&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;pylons_app&lt;/span&gt;
&lt;span class="ident"&gt;sqlalchemy&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;echo&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;True&lt;/span&gt;
&lt;span class="ident"&gt;sqlalchemy&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;pool_recycle&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;3600&lt;/span&gt;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Warto zwr&#243;ci&#263; uwag&#281;, &#380;e parametr &amp;#8216;pool_recycle&amp;#8217; zabezpiecza nas przed pewnym problemem w MySQL. Ot&#243;&#380; po paru godzinach nieaktywno&#347;ci, potrafi zrywa&#263; zestawione wcze&#347;niej po&#322;&#261;czenie. SQLAlchemy zabezpiecza si&#281; przed tym stukaj&#261;c do bazy co godzin&#281;. Active Record nie ma przed tym standardowo wbudowanego zabezpieczenia (sic!). Sequel za&#347; (te&#380; &amp;#8211; jak SQLAlchemy- pracuj&#261;cy wielow&#261;tkowo) wy&#322;apuje zerwane po&#322;&#261;czenia, czy&#347;ci je i zestawia z powrotem.&lt;/p&gt;


	&lt;p&gt;Musimy ustawi&#263; sesj&#281; dla SQLAlchemy. Dzia&#322;a i czy&#347;ci si&#281; po ka&#380;dym prze&#322;adowaniu przegl&#261;darki internetowej. W pliku pylons_app/lib/base.py wpisujemy:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;# reszta pliku...&lt;/span&gt;
&lt;span class="ident"&gt;import&lt;/span&gt; &lt;span class="ident"&gt;pylons_app&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;model&lt;/span&gt; &lt;span class="ident"&gt;as&lt;/span&gt; &lt;span class="ident"&gt;model&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;BaseController(WSGIController):&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;__call__&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;environ&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;start_response&lt;/span&gt;&lt;span class="punct"&gt;):&lt;/span&gt;
        &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Invoke the Controller&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="ident"&gt;conn&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;config&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;pylons.g&lt;/span&gt;&lt;span class="punct"&gt;'].&lt;/span&gt;&lt;span class="ident"&gt;sa_engine&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;connect&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;
        &lt;span class="ident"&gt;model&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Session&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;bind&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="ident"&gt;conn&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="ident"&gt;try&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
            &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;WSGIController&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;__call__&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;environ&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;start_response&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="ident"&gt;finally&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
            &lt;span class="ident"&gt;model&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Session&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;remove&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;
            &lt;span class="ident"&gt;conn&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;close&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;
&lt;span class="comment"&gt;# reszta pliku...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;           

	&lt;h2&gt;Tworzenie modeli&lt;/h2&gt;


	&lt;h3&gt;Rails&lt;/h3&gt;


	&lt;p&gt;W Rails korzystamy z wbudowanych generator&#243;w kodu:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;$ script/generate model User
-&amp;gt; app/models/user.rb
...
-&amp;gt; db/migrate/001_create_users.rb

$ script/generate model Post
-&amp;gt; app/models/post.rb
...
-&amp;gt; db/migrate/002_create_posts.rb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Razem z modelami domy&#347;lnie s&#261; tworzone pliki z migracjami. Wpierw jednak zdefiniujmy relacje mi&#281;dzy modelami:&lt;/p&gt;


	&lt;p&gt;Plik app/models/post.rb:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Post&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="ident"&gt;belongs_to&lt;/span&gt; &lt;span class="symbol"&gt;:user&lt;/span&gt;
  &lt;span class="comment"&gt;# Albo to samo za pomoc&#261; tych metod:&lt;/span&gt;
  &lt;span class="comment"&gt;# def user&lt;/span&gt;
  &lt;span class="comment"&gt;#   User.find :first,:conditions =&amp;gt; [&amp;quot;id=?&amp;quot;, id]&lt;/span&gt;
  &lt;span class="comment"&gt;# end&lt;/span&gt;
  &lt;span class="comment"&gt;# def user=(item)  &lt;/span&gt;
  &lt;span class="comment"&gt;#   self.user_id = item.id&lt;/span&gt;
  &lt;span class="comment"&gt;# end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Plik app/models/user.rb:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;User&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="ident"&gt;has_many&lt;/span&gt; &lt;span class="symbol"&gt;:posts&lt;/span&gt;
  &lt;span class="comment"&gt;# Albo to samo bardziej bezpo&#347;rednio:&lt;/span&gt;
  &lt;span class="comment"&gt;# def posts&lt;/span&gt;
  &lt;span class="comment"&gt;#  Post.find :all, :conditions =&amp;gt; [&amp;quot;user_id=?&amp;quot;, id]&lt;/span&gt;
  &lt;span class="comment"&gt;# end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Zdefiniujemy nie tylko struktury tabel ale te&#380; wype&#322;nimy je przyk&#322;adowymi danymi.&lt;/p&gt;


	&lt;p&gt;Plik db/migrate/001_create_users.rb:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;CreateUsers&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Migration&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.up&lt;/span&gt;
    &lt;span class="ident"&gt;create_table&lt;/span&gt; &lt;span class="symbol"&gt;:users&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:force&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;string&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:null&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="ident"&gt;add_index&lt;/span&gt; &lt;span class="symbol"&gt;:users&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:unique&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;
    &lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;create!&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Jarek&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.down&lt;/span&gt;
    &lt;span class="ident"&gt;drop_table&lt;/span&gt; &lt;span class="symbol"&gt;:users&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Plik db/migrate/001_create_posts.rb:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;CreatePosts&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Migration&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.up&lt;/span&gt;
    &lt;span class="ident"&gt;create_table&lt;/span&gt; &lt;span class="symbol"&gt;:posts&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:force&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;integer&lt;/span&gt; &lt;span class="symbol"&gt;:user_id&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:null&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;
      &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;string&lt;/span&gt; &lt;span class="symbol"&gt;:title&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:null&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;
      &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;text&lt;/span&gt; &lt;span class="symbol"&gt;:body&lt;/span&gt;
      &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;timestamps&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="ident"&gt;add_index&lt;/span&gt; &lt;span class="symbol"&gt;:posts&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:title&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:unique&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;title&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Tytu&#322; artyku&#322;u&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;body&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Tre&#347;&#263; artyku&#322;u&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find_by_name&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;Jarek&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save!&lt;/span&gt;    
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.down&lt;/span&gt;
    &lt;span class="ident"&gt;drop_table&lt;/span&gt; &lt;span class="symbol"&gt;:posts&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Uruchamiamy migracje:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;rake db:migrate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Sprawd&#378;my interaktywnie (w konsoli, czyli po odpaleniu ruby script/console) czy modele dzia&#322;aj&#261; prawid&#322;owo.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:all&lt;/span&gt;
&lt;span class="comment"&gt;# =&amp;gt; [#&amp;lt;User id: 1, name: &amp;quot;Jarek&amp;quot;&amp;gt;]&lt;/span&gt;
&lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:first&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;posts&lt;/span&gt;
&lt;span class="comment"&gt;# =&amp;gt; [#&amp;lt;Post id: 1, user_id: 1, title: &amp;quot;Tytu&#322; artyku&#322;u&amp;quot;, body: &amp;quot;Tre&#347;&#263; artyku&#322;u&amp;quot;, created_at: &amp;quot;2007-12-15 18:16:27&amp;quot;, updated_at: &amp;quot;2007-12-15 18:16:27&amp;quot;&amp;gt;]&lt;/span&gt;
&lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:first&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;posts&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;title&lt;/span&gt;
&lt;span class="comment"&gt;# =&amp;gt; &amp;quot;Tytu&#322; artyku&#322;u&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h2&gt;Merb&lt;/h2&gt;


	&lt;p&gt;Tu wpierw trzeba wybra&#263; jaki &lt;span class="caps"&gt;ORM&lt;/span&gt; chcemy u&#380;ywa&#263; (domy&#347;lnie Merb nie ma w&#322;aczonej obs&#322;ugi &#380;adnego). Operacja jest bardzo prosta. W pliku config/dependencies.rb odkomentujemy linijk&#281; tak aby by&#322;o:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;use_orm&lt;/span&gt; &lt;span class="symbol"&gt;:sequel&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Modele generujemy identycznymi generatorami jak w Rails.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_shell "&gt;$ script/generate model User
-&amp;gt; app/models/user.rb
...
-&amp;gt; schema/migrations/001_add_model_users.rb

$ script/generate model Post
-&amp;gt; app/models/post.rb
...
-&amp;gt; schema/migrations/002_add_model_posts.rb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Definicja modeli.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Post&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;Sequel&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:posts&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;one_to_one&lt;/span&gt; &lt;span class="symbol"&gt;:user&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:Post&lt;/span&gt;
  &lt;span class="comment"&gt;# To samo co:&lt;/span&gt;
  &lt;span class="comment"&gt;# def user&lt;/span&gt;
  &lt;span class="comment"&gt;#   User.filter(:id=&amp;gt; user_id).first&lt;/span&gt;
  &lt;span class="comment"&gt;# end  &lt;/span&gt;
  &lt;span class="comment"&gt;# def user=(obj)&lt;/span&gt;
  &lt;span class="comment"&gt;#   self.user_id = obj.id&lt;/span&gt;
  &lt;span class="comment"&gt;# end  &lt;/span&gt;
  &lt;span class="comment"&gt;#&lt;/span&gt;
  &lt;span class="comment"&gt;# Merb nie ma automagicznego wype&#322;niania tre&#347;ci&#261;&lt;/span&gt;
  &lt;span class="comment"&gt;# p&#243;l o nazwach created_at czy updated_at&lt;/span&gt;
  &lt;span class="comment"&gt;# Ale posiada wywo&#322;ania zwrotne (te&#380; jak Rails)&lt;/span&gt;
  &lt;span class="ident"&gt;after_create&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;set&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:created_at&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;Time&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;set&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:updated_at&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;Time&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;after_update&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;set&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:updated_at&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;Time&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;User&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;Sequel&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:users&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;one_to_many&lt;/span&gt; &lt;span class="symbol"&gt;:posts&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:from&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:posts&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:key&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:user_id&lt;/span&gt;
  &lt;span class="comment"&gt;# To samo co:&lt;/span&gt;
  &lt;span class="comment"&gt;# def posts&lt;/span&gt;
  &lt;span class="comment"&gt;#   Post.filter :user_id =&amp;gt; pk&lt;/span&gt;
  &lt;span class="comment"&gt;# end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;W przeciwie&#324;stwie do Active Record, w Sequelu mo&#380;na zdefiniowa&#263; (&lt;a href="http://code.google.com/p/ruby-sequel/wiki/SequelModels"&gt;metod&#261; set_schema&lt;/a&gt;) struktur&#281; tabel w modelu (wtedy nie trzeba np. zagl&#261;da&#263; do migracji ani bazy, aby sobie przypomnie&#263; struktur&#281; tabeli). Ale aby oszcz&#281;dzi&#263; sobie pisania, zda&#322;em si&#281; tu na automatyczne rozpoznanie struktury tabel przez Rubiego.&lt;/p&gt;


	&lt;p&gt;Teraz pora na migracje. Sequel te&#380; je posiada, cho&#263; r&#243;&#380;ni&#261; si&#281; troszk&#281; sk&#322;adni&#261; do tych w Active Record.&lt;/p&gt;


	&lt;p&gt;Plik schema/migrations/001_add_mode_users.rb:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;AddModelUsers&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;Sequel&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Migration&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;up&lt;/span&gt;
    &lt;span class="ident"&gt;create_table!&lt;/span&gt; &lt;span class="symbol"&gt;:users&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
      &lt;span class="ident"&gt;primary_key&lt;/span&gt; &lt;span class="symbol"&gt;:id&lt;/span&gt;
      &lt;span class="ident"&gt;varchar&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:size&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;255&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:unique&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;      
    &lt;span class="keyword"&gt;end&lt;/span&gt;  
    &lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;create&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Jarek&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;down&lt;/span&gt;
    &lt;span class="ident"&gt;execute&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;DROP TABLE users&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Plik schema/migrations/002_add_mode_posts.rb:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;AddModelPosts&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;Sequel&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Migration&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;up&lt;/span&gt;
    &lt;span class="ident"&gt;create_table!&lt;/span&gt; &lt;span class="symbol"&gt;:posts&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;      
      &lt;span class="ident"&gt;primary_key&lt;/span&gt; &lt;span class="symbol"&gt;:id&lt;/span&gt; 
      &lt;span class="ident"&gt;integer&lt;/span&gt; &lt;span class="symbol"&gt;:user_id&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:null&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;
      &lt;span class="ident"&gt;varchar&lt;/span&gt; &lt;span class="symbol"&gt;:title&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:null&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:unique&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;
      &lt;span class="ident"&gt;text&lt;/span&gt; &lt;span class="symbol"&gt;:body&lt;/span&gt;
      &lt;span class="ident"&gt;datetime&lt;/span&gt; &lt;span class="symbol"&gt;:created_at&lt;/span&gt; 
      &lt;span class="ident"&gt;datetime&lt;/span&gt; &lt;span class="symbol"&gt;:updated_at&lt;/span&gt; 
    &lt;span class="keyword"&gt;end&lt;/span&gt;    
    &lt;span class="ident"&gt;post&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;title&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Tytu&#322; artyku&#322;u&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;body&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Tre&#347;&#263; artyku&#322;u&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;filter&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:name=&lt;/span&gt;&lt;span class="punct"&gt;&amp;gt;'&lt;/span&gt;&lt;span class="string"&gt;Jarek&lt;/span&gt;&lt;span class="punct"&gt;').&lt;/span&gt;&lt;span class="ident"&gt;first&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;down&lt;/span&gt;
    &lt;span class="ident"&gt;execute&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;DROP TABLE posts&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Uruchomienie migracji dzia&#322;a w Sequelu tak samo jak w Active Record (nawet parametr &lt;span class="caps"&gt;VERSION&lt;/span&gt; te&#380; tu dzia&#322;a).&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;rake sequel:db:migrate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;No to pora na test z interaktywnej konsoli. Odpala si&#281; j&#261; przez:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;merb -i&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;all&lt;/span&gt;
&lt;span class="comment"&gt;# =&amp;gt; [#&amp;lt;User:0x19477e8 @changed_columns=[], @values={:name=&amp;gt;&amp;quot;Jarek&amp;quot;, :id=&amp;gt;1}, newfalse]&lt;/span&gt;
&lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;first&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;posts&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="keyword"&gt;class 
&lt;/span&gt;&lt;span class="class"&gt;#&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;Sequel&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="ident"&gt;MySQL&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="ident"&gt;Dataset&lt;/span&gt;
&lt;span class="comment"&gt;# User.first.posts.sql&lt;/span&gt;
&lt;span class="comment"&gt;# =&amp;gt; &amp;quot;SELECT * FROM posts WHERE (`user_id` = 1)&amp;quot;        &lt;/span&gt;
&lt;span class="ident"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;first&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;posts&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;first&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;title&lt;/span&gt;
&lt;span class="comment"&gt;# =&amp;gt; &amp;quot;Tytu\305\202 artyku\305\202u&amp;quot;      &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Sequel nie tylko jest szybszy i u&#380;ywa iterator&#243;w (oszcz&#281;dzaj&#261;&#263; przez to pami&#281;&#263;). Jest te&#380; wielow&#261;tkowy (u&#380;ywa puli w&#261;tk&#243;w) oraz pozwala na krokowy podgl&#261;d kodu &lt;span class="caps"&gt;SQL&lt;/span&gt; &lt;strong&gt;przed&lt;/strong&gt; jakimkolwiek przeslaniem kwerendy do bazy danych. Sequel r&#243;wnie&#380; nie zwraca liste obiektow &lt;span class="caps"&gt;ORM&lt;/span&gt; z bazy (jak Active Record) ale jeden, specjalny obiekt proxy. Pozwala to na interaktywne budowanie kodu w stopniu o wiele lepszym ni&#380; pozwala na to Active Record (czy nawet  DataMapper). Sequel ma r&#243;wnie&#380; &lt;a href="http://code.google.com/p/ruby-sequel/wiki/SequelModels"&gt;wbudowany cache modeli&lt;/a&gt; wsp&#243;&#322;pracuj&#261;cy z szybkim serwerem memcached.&lt;/p&gt;


	&lt;h3&gt;Pylons&lt;/h3&gt;


	&lt;p&gt;Poni&#380;sza konfiguracja jest inspirowana tekstem &lt;a href="http://wiki.pylonshq.com/display/pylonscookbook/SQLAlchemy+0.4+for+people+in+a+hurry"&gt;SQLAlchemy 0.4 for people in a hurry&lt;/a&gt;. Pylons nie wymaga aby ka&#380;dy model by&#322; w oddzielnym pliku, zatem u&#380;yjemy jednego. W pliku pylons_app/model/&lt;i&gt;init&lt;/i&gt;.py wpisujemy:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;# -*- coding: utf8 -*-&lt;/span&gt;
&lt;span class="ident"&gt;from&lt;/span&gt; &lt;span class="ident"&gt;datetime&lt;/span&gt; &lt;span class="ident"&gt;import&lt;/span&gt; &lt;span class="ident"&gt;datetime&lt;/span&gt;

&lt;span class="comment"&gt;# Zgodnie z filozofi&#261; Pythona, importujemy tylko tylko interesuj&#261;ce nas symbole&lt;/span&gt;
&lt;span class="comment"&gt;# aby nie za&#347;mieca&#263; sobie przestrzeni nazw:&lt;/span&gt;

&lt;span class="ident"&gt;from&lt;/span&gt; &lt;span class="ident"&gt;pylons&lt;/span&gt; &lt;span class="ident"&gt;import&lt;/span&gt; &lt;span class="ident"&gt;config&lt;/span&gt;
&lt;span class="ident"&gt;from&lt;/span&gt; &lt;span class="ident"&gt;sqlalchemy&lt;/span&gt; &lt;span class="ident"&gt;import&lt;/span&gt; &lt;span class="constant"&gt;ForeignKey&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;Column&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;MetaData&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;Table&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;types&lt;/span&gt;
&lt;span class="ident"&gt;from&lt;/span&gt; &lt;span class="ident"&gt;sqlalchemy&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;orm&lt;/span&gt; &lt;span class="ident"&gt;import&lt;/span&gt; &lt;span class="ident"&gt;mapper&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;relation&lt;/span&gt;
&lt;span class="ident"&gt;from&lt;/span&gt; &lt;span class="ident"&gt;sqlalchemy&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;orm&lt;/span&gt; &lt;span class="ident"&gt;import&lt;/span&gt; &lt;span class="ident"&gt;scoped_session&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;sessionmaker&lt;/span&gt;

&lt;span class="constant"&gt;Session&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;scoped_session&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;sessionmaker&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;autoflush&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="constant"&gt;True&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;transactional&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="constant"&gt;True&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;bind&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="ident"&gt;config&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;pylons.g&lt;/span&gt;&lt;span class="punct"&gt;'].&lt;/span&gt;&lt;span class="ident"&gt;sa_engine&lt;/span&gt;&lt;span class="punct"&gt;))&lt;/span&gt;
&lt;span class="ident"&gt;metadata&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;MetaData&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;

&lt;span class="comment"&gt;# Tworzymy definicje tabel:&lt;/span&gt;

&lt;span class="ident"&gt;table_users&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Table&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;users&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;metadata&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
  &lt;span class="constant"&gt;Column&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;types&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Integer&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;primary_key&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="constant"&gt;True&lt;/span&gt;&lt;span class="punct"&gt;),&lt;/span&gt;
  &lt;span class="constant"&gt;Column&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;types&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;String&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;255&lt;/span&gt;&lt;span class="punct"&gt;))&lt;/span&gt;
  &lt;span class="punct"&gt;)&lt;/span&gt;
&lt;span class="ident"&gt;table_posts&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Table&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;posts&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;metadata&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
  &lt;span class="constant"&gt;Column&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;id&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;types&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Integer&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;primary_key&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="constant"&gt;True&lt;/span&gt;&lt;span class="punct"&gt;),&lt;/span&gt;
  &lt;span class="constant"&gt;Column&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;user_id&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;types&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Integer&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;ForeignKey&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;users.id&lt;/span&gt;&lt;span class="punct"&gt;')),&lt;/span&gt;
  &lt;span class="ident"&gt;Column&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;title&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;types&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Unicode&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;255&lt;/span&gt;&lt;span class="punct"&gt;)),&lt;/span&gt;
  &lt;span class="ident"&gt;Column&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;body&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;types&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Unicode&lt;/span&gt;&lt;span class="punct"&gt;),&lt;/span&gt;
  &lt;span class="ident"&gt;Column&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;created_at&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;types&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;default&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="ident"&gt;datetime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;),&lt;/span&gt;
  &lt;span class="constant"&gt;Column&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;updated_at&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;types&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;default&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="ident"&gt;datetime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="punct"&gt;)&lt;/span&gt;
&lt;span class="comment"&gt;# Zamiast tego mo&#380;na automatycznie wci&#261;gn&#261;&#263; istniej&#261;c&#261; struktur&#281;:&lt;/span&gt;
&lt;span class="comment"&gt;#&lt;/span&gt;
&lt;span class="comment"&gt;# table_users = Table('users', metadata, autoload=True)&lt;/span&gt;
&lt;span class="comment"&gt;# table_posts = Table('posts', metadata, autoload=True)&lt;/span&gt;

&lt;span class="comment"&gt;# Tworzymy definicje modeli.&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;User(object):&lt;/span&gt;
    &lt;span class="ident"&gt;pass&lt;/span&gt;
&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Post(object):&lt;/span&gt;
    &lt;span class="ident"&gt;pass&lt;/span&gt;

&lt;span class="comment"&gt;# Mapowanie ORM do klas SQLAlchemy i ustawienie&lt;/span&gt;
&lt;span class="comment"&gt;# relacji jeden-do-wielu&lt;/span&gt;
&lt;span class="ident"&gt;mapper&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;table_users&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;properties&lt;/span&gt;&lt;span class="punct"&gt;={'&lt;/span&gt;&lt;span class="string"&gt;posts&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="symbol"&gt;:relation&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Post&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;backref&lt;/span&gt;&lt;span class="punct"&gt;='&lt;/span&gt;&lt;span class="string"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;')})&lt;/span&gt;
&lt;span class="ident"&gt;mapper&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Post&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;table_posts&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Wygl&#261;da to bardziej skomplikowanie, ale SQLAlchemy ma te&#380; wi&#281;ksze mo&#380;liwo&#347;ci. Zasadnicz&#261; r&#243;&#380;nic&#261; w SQLAlchemy jest rozdzielenie obiekt&#243;w &lt;span class="caps"&gt;ORM&lt;/span&gt; od obiekt&#243;w modelu biznesowego. Obiekty &lt;span class="caps"&gt;ORM&lt;/span&gt; zajmuj&#261; si&#281; mapowaniem struktur tabel do obiekt&#243;w Pythona. S&#261; one nast&#281;pnie &#322;&#261;czone z klasami SQLAlchemy. Takie rozdzielenie daje du&#380;o wi&#281;ksze mo&#380;liwo&#347;ci, bo mo&#380;na w modelu biznesowym nawet zamapowa&#263; kilka tabel do jednego modelu. SQLAlchemy ma du&#380;o innych mo&#380;liwo&#347;ci, o kt&#243;rych tu nie b&#281;d&#281; teraz pisa&#263;. W ka&#380;dym razie odbija si&#281; to na z&#322;o&#380;ono&#347;ci (ale i tak to jest pro&#347;ciej ni&#380; np. w javowym Hibernate).&lt;/p&gt;


	&lt;p&gt;Co z migracjami? Nie ma ich. Trzeba by by&#322;o je napisa&#263; (to w sumie nie jest trudna sprawa). Stworzmy jednak strukture tabel wraz z przyk&#322;adowymi danymi za pomoc&#261; SQLAlchemy. W tym celu ko&#324;c&#243;wka pliku pylons_app/websetup.py powinna wygl&#261;da&#263; nast&#281;puj&#261;co:&lt;/p&gt;


# pocz&#261;tek pliku&amp;#8230;
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;setup_config&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;command&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;filename&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;section&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;vars&lt;/span&gt;&lt;span class="punct"&gt;):&lt;/span&gt;
    &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Place any commands to setup pylons_app here&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="ident"&gt;conf&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;appconfig&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;config:&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="ident"&gt;filename&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;load_environment&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;conf&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;global_conf&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;conf&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;local_conf&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

    &lt;span class="ident"&gt;from&lt;/span&gt; &lt;span class="ident"&gt;pylons_app&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;model&lt;/span&gt; &lt;span class="ident"&gt;import&lt;/span&gt; &lt;span class="ident"&gt;metadata&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;Session&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;Post&lt;/span&gt;

    &lt;span class="ident"&gt;log&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;info&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Creating tables&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="ident"&gt;metadata&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;create_all&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;bind&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="ident"&gt;config&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;pylons.g&lt;/span&gt;&lt;span class="punct"&gt;'].&lt;/span&gt;&lt;span class="ident"&gt;sa_engine&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;log&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;info&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Successfully setup&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;

    &lt;span class="ident"&gt;log&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;info&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Adding some data to table 'users'&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="ident"&gt;user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;
    &lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Jarek&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="constant"&gt;Session&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="constant"&gt;Session&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;commit&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;

    &lt;span class="ident"&gt;log&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;info&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Adding some data to table 'posts'&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="ident"&gt;user_query&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Session&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;query&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;user_id&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;user_query&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;filter&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Jarek&lt;/span&gt;&lt;span class="punct"&gt;').&lt;/span&gt;&lt;span class="ident"&gt;one&lt;/span&gt;&lt;span class="punct"&gt;().&lt;/span&gt;&lt;span class="ident"&gt;id&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Post&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;title&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Tytu&#322; artyku&#322;u&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;body&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Tre&#347;&#263; artyku&#322;u&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="ident"&gt;post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;user_id&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;user_id&lt;/span&gt;
    &lt;span class="constant"&gt;Session&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;save&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;post&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="constant"&gt;Session&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;commit&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;    
    &lt;span class="ident"&gt;log&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;info&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Data added.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;W powy&#380;szym przyk&#322;adzie wida&#263; te&#380; pr&#243;bk&#281; sposobu w jaki u&#380;ywa si&#281; SQLAlchemy. Ka&#380;da operacja jest zamykana w ramach specjalnej sesji a sesja jest tworzona niezale&#380;nie dla ka&#380;dego w&#261;tku. W SQLAlchemy praktycznie w og&#243;le nie ma potrzeby si&#281;gania do kodu &lt;span class="caps"&gt;SQL&lt;/span&gt; tak jak to nagminnie wida&#263; w Active Record. Cho&#263; musz&#281; te&#380; przyzna&#263;, &#380;e Sequel tak&#380;e &#322;adnie daje sobie rad&#281; ze skomplikowanymi konstrukcjami bez konieczno&#347;ci uciekania si&#281; do surowego kodu &lt;span class="caps"&gt;SQL&lt;/span&gt;.&lt;/p&gt;


	&lt;p&gt;Uruchamiamy tworzenie tabel za pomoc&#261;:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;paster setup-app development.ini&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Interaktywny shell uruchamia si&#281; przez&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;paster shell&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Trzeba pami&#281;ta&#263;, &#380;e fakt uruchomienia konsoli nie oznacza, &#380;e mamy automatycznie powci&#261;gane modu&#322;y wszystkich modeli. W Pythonie nie ma magii, wszystko nale&#380;y jawnie zaimportowa&#263;. Tak&#380;e i nasz projekt.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;from&lt;/span&gt; &lt;span class="ident"&gt;pylons_app&lt;/span&gt; &lt;span class="ident"&gt;import&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;
&lt;span class="ident"&gt;print&lt;/span&gt; &lt;span class="ident"&gt;model&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;User&lt;/span&gt;
&lt;span class="comment"&gt;# =&amp;gt; &amp;lt;class 'pylons_app.model.User'&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h2&gt;Tworzenie kontrolera i prostego widoku.&lt;/h2&gt;


	&lt;h3&gt;Rails&lt;/h3&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;script/generate controller home
# =&amp;gt; app/controllers/home_controller.rb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Plik kontrolera (app/controller/home_controller.rb):&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;HomeController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;index&lt;/span&gt;
    &lt;span class="attribute"&gt;@user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt; &lt;span class="symbol"&gt;:first&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Aby u&#380;y&#263; Haml zamiast ERb musimy w katalogu projektu odpali&#263; komend&#281; (nie zapomnij o kropce, kt&#243;ra oznacza tu bie&#380;&#261;cy katalog):&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;haml --rails .&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Plik widok (app/views/home/index.html.haml):&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haml "&gt;U&#380;ytkownik
= @user.name
posiada post(y)
%ul
  - @user.posts.each do |post|
    %li= post.title&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Serwer uruchamiamy klasycznie&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;script/server&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h3&gt;Merb&lt;/h3&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;script/generate controller home
# =&amp;gt; app/controllers/home.rb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

Plik kontrolera (app/controllers/home.r):
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Home&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;Application&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;index&lt;/span&gt;
    &lt;span class="attribute"&gt;@user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;first&lt;/span&gt;
    &lt;span class="ident"&gt;render&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Aby uaktywni&#263; Haml nale&#380;y w pliku config/merb_init.rb dopisa&#263; na ko&#324;cu&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="constant"&gt;Merb&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Template&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Haml&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Warto te&#380; doinstalowa&#263; gemy:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;gem install -y merb_helpers merb_has_rails_plugins ruby-debug&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;i doda&#263; je do config/dependencies.rb&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;dependency&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;merb_helpers&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;dependency&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;merb_has_rails_plugins&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;dependency&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;ruby-debug&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Dzi&#281;ki temu b&#281;dziemy mieli dost&#281;pnych troch&#281; dodatkowych helper&#243;w  z Rails. Za&#347; gem ruby-debug umo&#380;liwia nam wpisanie w dowolnym miejscu komendy 
debugger przerywaj&#261;cej prac&#281; przegl&#261;darki i kieruj&#261;cej wykonanie kodu do konsoli. Po jej zako&#324;czeniu przegl&#261;darka wznawia obs&#322;ug&#281; requestu. Dzia&#322;a to tu tak jak w Rails. Mo&#380;na interaktywnie sobie operowa&#263; na &#380;ywym kodzie. Warto te&#380; wiedzie&#263;, &#380;e w Merb wszystkie pluginy s&#261; po prostu gemami. U&#322;atwia to zarz&#261;dzanie ich wersjami.&lt;/p&gt;


	&lt;p&gt;Plik widoku jest identyczny jak w wy&#380;ej dla Railsow wi&#281;c nie b&#281;d&#281; podawa&#322; jeszcze raz jego tre&#347;ci.&lt;/p&gt;


	&lt;p&gt;Odpalamy serwer za pomoc&#261; komendy &lt;em&gt;merb&lt;/em&gt; i na stronie http://localhost:4000/home podziwiamy wynik. :) Komenda merb&amp;#8212;help wy&#347;wietli wszystkie, dost&#281;pne opcje.&lt;/p&gt;


	&lt;h3&gt;Pylons&lt;/h3&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;paster controller home
# =&amp;gt; pylons_app/controllers/home.py&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Plik kontrolera (pylons_app/controllers/home.py):&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;import&lt;/span&gt; &lt;span class="ident"&gt;logging&lt;/span&gt;
&lt;span class="ident"&gt;from&lt;/span&gt; &lt;span class="ident"&gt;pylons_app&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;lib&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;base&lt;/span&gt; &lt;span class="ident"&gt;import&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;
&lt;span class="ident"&gt;log&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;logging&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;getLogger&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;__name__&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
&lt;span class="ident"&gt;from&lt;/span&gt; &lt;span class="ident"&gt;pylons_app&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;model&lt;/span&gt; &lt;span class="ident"&gt;import&lt;/span&gt; &lt;span class="constant"&gt;Session&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;User&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;HomeController(BaseController):&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;index&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;):&lt;/span&gt;
        &lt;span class="ident"&gt;user_query&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Session&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;query&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="ident"&gt;c&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;user_query&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;filter&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Jarek&lt;/span&gt;&lt;span class="punct"&gt;').&lt;/span&gt;&lt;span class="ident"&gt;one&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;
        &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="ident"&gt;render&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;/home/index.mako&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Plik widoku (pylons_app/templates/home/index.mako):&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_mako "&gt;U&#380;ytkownik ${c.user.name} posiada post(y)
&amp;lt;ul&amp;gt;
  % for post in c.user.posts:
    &amp;lt;li&amp;gt;${post.title}&amp;lt;/li&amp;gt;
  % endfor
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Odpalamy serwer za pomoc&#261;&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_bash "&gt;paster serve development.ini --reload&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Pylons nie posiada tryb&#243;w pracy tak jak Rails czy Merb. Wielow&#261;tkowy serwer &lt;span class="caps"&gt;HTTP&lt;/span&gt; (paster) musi dosta&#263; odpowiedni plik konfiguracyjny. Daje to w sumie wi&#281;cej mo&#380;liwo&#347;ci, bo mo&#380;na izolowa&#263; sobie r&#243;&#380;ne konfiguracje du&#380;o bardziej ni&#380; prosty podzia&#322; na tryb produkcyjny i rozwojowy.&lt;/p&gt;


	&lt;h2&gt;Podsumowanie.&lt;/h2&gt;


	&lt;p&gt;Rails jest bardzo bogato wyposa&#380;ony w r&#243;&#380;ne helpery, posiada dobr&#261; literatur&#281;, ksi&#261;&#380;ki itd. Jest te&#380; bardzo &#322;atwy do nauki i dosy&#263; intuicyjny. Je&#347;li chodzi o prototypowanie, to jest prawie nie do pobicia. Niestety, mo&#380;na si&#281; przyczepi&#263; do jako&#347;ci implementacji wewn&#281;trznej jak i do niekt&#243;rych &lt;a href="http://blog.zabiello.com/articles/2007/12/13/slabosci-active-record"&gt;b&#322;&#281;d&#243;w projektowych w Active Record&lt;/a&gt;. My&#347;l&#281;, &#380;e Railsom wyr&#243;s&#322; bardzzo gro&#378;ny konkurent w postaci Merba. Jest lepiej zorganizowany, sp&#243;jniejszy i szybszy. Jest te&#380; tak samo prosty i intuicyjny. Czy warto si&#281; przesiada&#263;? Wg mnie &amp;#8211; warto. Je&#347;li za&#347; chodzi o &lt;span class="caps"&gt;ORM&lt;/span&gt;, to Sequel jest pod wieloma wzgl&#281;dami lepszym projektem od Active Record. Tu mo&#380;e tego nie eksponowa&#322;em, ale Sequel pozwala na obiektowe budowanie znacznie bardziej skomplikowanych zapyta&#324; ni&#380; to potrafi Active Record czy DataMapper. Po przyk&#322;ady kodu Sequela odsy&#322;am do jego &lt;a href="http://code.google.com/p/ruby-sequel/"&gt;strony domowej&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;Co z Pylons? Korzysta z pot&#281;&#380;nego &lt;span class="caps"&gt;ORM&lt;/span&gt;&amp;#8217;a i pot&#281;&#380;nych szablon&#243;w. Ma pe&#322;n&#261; obs&#322;ug&#281; Unicode, szablony Mako natywnie pracuj&#261; na obiektach unikodowych (nie myli&#263; ze stringami utf8). Mako maj&#261; dziedziczenie, pot&#281;&#380;ny, elastyczny cache i s&#261; bardzo &amp;#8220;pythonic&amp;#8221; w swym podej&#347;ciu do budowania modu&#322;&#243;w czy custom tag&#243;w. Niestety Pylons w ca&#322;o&#347;ci wymaga troch&#281; wi&#281;cej wiedzy i umiej&#281;tno&#347;ci aby go ustawi&#263; do pracy. Rails i Merb s&#261; tu znacznie prostsze. Dzi&#281;ki za&#347;  Merb przepa&#347;&#263; mi&#281;dzy wydajno&#347;ci&#261; i mo&#380;liwo&#347;ciami pythonowego Pylonso a frameworkami Rubiego mocno si&#281; zmniejszyla. Ci, co czepiali si&#281; wcze&#347;niej wydajno&#347;ci Rails nie maj&#261; teraz specjalnie na co narzeka&#263;. Ci co narzekali na ograniczenia Active Record, nie maj&#261; wym&#243;wki w zetkni&#281;ciu z Sequelem. No chyba, &#380;e kto&#347; nie chce si&#281; uczy&#263; Rubiego, jego sprawa. Ja jednak uwa&#380;am, &#380;e Ruby to pi&#281;kny j&#281;zyk i bardzo przyjemnie si&#281; w nim pisze. A prostota Merba w por&#243;wnaniu z Pylons jest (szczeg&#243;lnie dla os&#243;b zaczynaj&#261;cych prac&#281;) &amp;#8211; powalaj&#261;ca.&lt;/p&gt;


	&lt;p&gt;Celem tego tekstu by&#322;o pom&#243;c osobom w pocz&#261;tkowej fazie konfiguracji Pylons oraz Merb. Mam nadziej&#281;, &#380;e by&#322; pomocny i dalej ju&#380; ka&#380;dy sobie sam da rad&#281;. Je&#347;li chodzi o dodatkow&#261; pomoc, to warto zajrze&#263; na &lt;span class="caps"&gt;IRC&lt;/span&gt;. Na serwerze freeenode istniej&#261; kana&#322;y: #pylons, #merb, #sequel, #datamapper, #rubyonrails oraz  nasze polskie #rubyonrails.pl i #python-pl. S&#261; te&#380; polskie grupy dyskusyjne w Usenecie: news://pl.comp.lang.python i news://pl.comp.lang.ruby.&lt;/p&gt;


&lt;hr /&gt;

	&lt;p&gt;Kod &#378;r&#243;d&#322;owy z powy&#380;szymi przyk&#322;adami &lt;a href="http://blog.zabiello.com/files/szybki-start.zip"&gt;mo&#380;na pobra&#263; st&#261;d&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sun, 16 Dec 2007 07:31:00 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:2a1dd95d-df5f-466d-bce9-87eb2b231833</guid>
      <author>Jaros&#322;aw Zabie&#322;&#322;o</author>
      <link>http://blog.zabiello.com/articles/2007/12/16/howto-rails-merb-pylons</link>
      <category>rails</category>
      <category>merb</category>
      <category>pylons</category>
      <category>sqlalchemy</category>
      <category>mako</category>
      <category>sequel</category>
      <category>activerecord</category>
      <category>ruby</category>
      <category>python</category>
    </item>
    <item>
      <title>Shoulda - pozbycie si&#281; magii RSpec'a</title>
      <description>&lt;p&gt;Od jakiego&#347; czasu w kr&#281;gach Ruby on Rails mo&#380;na zauwa&#380;y&#263; przesuni&#281;cie paradygmatu w zakresie metodologii testowania kodu. Popularyzowane podej&#347;cie &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development"&gt;&lt;span class="caps"&gt;TDD&lt;/span&gt;&lt;/a&gt; (Test Driven Development) zostaje wypierane przez &lt;a href="http://en.wikipedia.org/wiki/Behavior_driven_development"&gt;&lt;span class="caps"&gt;BDD&lt;/span&gt;&lt;/a&gt; (Behaviour Driven Development). Jedn&#261; z bardziej promowanych bibliotek jest RSpec. Mimo &#380;e kusi sk&#322;adni&#261; przypominaj&#261;c&#261; naturalny j&#281;zyk angielski, pr&#243;ba wykorzystania &lt;a href="http://rspec.rubyforge.org/"&gt;RSPec&lt;/a&gt; w rzeczywistym projekcie szybko mo&#380;e sta&#263; si&#281; co najmniej k&#322;opotliwe.&lt;/p&gt;


	&lt;p&gt;RSpec aby wygl&#261;da&#322; tak &#322;adnie jak wygl&#261;da, wykorzystuje dynamik&#281; i specyficzne w&#322;a&#347;ciwo&#347;ci Rubiego. Ruby pozwala opuszcza&#263; nawiasy w metodach, niezauwa&#380;enie otwiera&#263; i modyfikowa&#263; klasy swoich bibliotek (w tym wbudowanych i standardowych) oraz og&#243;lnie dosy&#263; dobrze nadaje si&#281; do tworzenia nowych j&#281;zyk&#243;w do specyficznych zada&#324; (&lt;a href="http://en.wikipedia.org/wiki/Domain-specific_programming_language"&gt;&lt;span class="caps"&gt;DSL&lt;/span&gt;&lt;/a&gt;, Domain Specific Language). Innymi s&#322;owy, cen&#261; za korzystanie ze sk&#322;adni RSpec jest konieczno&#347;&#263; opanowania tego nowego j&#281;zyka. I tu zaczynaj&#261; si&#281; schody&amp;#8230;&lt;/p&gt;


	&lt;p&gt;Dokumentacja do RSpec jest najdelikatniej m&#243;wi&#261;c, niekompletna. Przyk&#322;ad&#243;w u&#380;ycia RSpec trzeba szuka&#263; po ca&#322;ym internecie. Dokumentacja wygenerowana z kodu &#378;r&#243;d&#322;owego &lt;span class="caps"&gt;API&lt;/span&gt; do RSpec jest r&#243;wnie&#380; s&#322;abej jako&#347;ci. Brakuje przyk&#322;ad&#243;w, brakuje nawet wzmianki co do niekt&#243;rych metod, jakie s&#261; u&#380;ywane. Np. we&#378;my taki prosty przyk&#322;ad:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;have_text&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;My String&lt;/span&gt;&lt;span class="punct"&gt;/)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Pr&#243;ba znalezienia czego&#347; sensownego na stronie z &lt;a href="http://rspec.rubyforge.org/rdoc/index.html"&gt;&lt;span class="caps"&gt;API&lt;/span&gt; RSpec&amp;#8217;a&lt;/a&gt; jest beznadziejna. Nie ma nic napisane o metodzie &amp;#8220;response&amp;#8221;, nie ma nic o metodzie &amp;#8220;have_text&amp;#8221; ani w og&#243;le o tym, co mo&#380;na przekaza&#263; do metody should(). Po prostu pojawia si&#281; co&#347; niczym kr&#243;lik z kapelusza magika. I tak jest z wieloma elementami sk&#322;adni RSpec&amp;#8217;a. S&#261; jakie&#347; pojedy&#324;cze elementy, ale nie wiadomo kt&#243;re, i jak je &#322;&#261;czy&#263; ze sob&#261;. &lt;strong&gt;Je&#347;li Ruby ma zbytnie sk&#322;onno&#347;ci do &amp;#8220;magii&amp;#8221;, to RSpec magia do kwadratu.&lt;/strong&gt; Co&#347; si&#281; pojawia i znika nie wiadomo sk&#261;d, ani dlaczego. To jest po prostu chore. Po wielu dniach takiej walki z RSpec doszed&#322;em do wniosku, &#380;e to droga donik&#261;d. Tylko, &#380;e z drugiej strony, cofni&#281;cie si&#281; z &lt;span class="caps"&gt;BDD&lt;/span&gt; do &lt;span class="caps"&gt;TDD&lt;/span&gt; te&#380; nie brzmi atrakcyjnie.&lt;/p&gt;


	&lt;p&gt;Czy jest zatem jakie&#347; wyj&#347;cie po&#347;rednie? Tzn. rozwi&#261;zanie kt&#243;re nie rezygnuj&#261;c z elegancji &lt;span class="caps"&gt;BDD&lt;/span&gt; by&#322;oby oparty na znanych, dobrze opisanych metodach, bez &#380;adnej kolejnej, magicznej sk&#322;adni? Wydaje si&#281;, &#380;e powy&#380;sze za&#322;o&#380;enia spe&#322;nia projekt  &lt;a href="http://thoughtbot.com/projects/shoulda"&gt;Shoulda&lt;/a&gt;. M&#243;j pierwsze wra&#380;enia z kontaktu z t&#261; bibliotek&#261;, jak na razie, s&#261; dosy&#263; dobre. Shoulda wprowadza du&#380;o mniej nowej sk&#322;adni ni&#380; RSpec. Wi&#281;kszo&#347;&#263; test&#243;w wykorzystuje stare, sprawdzone &amp;#8220;asserty&amp;#8221;. I o to chodzi. Nowa metodologia i znana, sprawdzona biblioteka standardowa Rubiego Test::Unit.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;UserTest&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;Test&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Unit&lt;/span&gt; 
  &lt;span class="ident"&gt;context&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;A User instance&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;setup&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
      &lt;span class="attribute"&gt;@user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;User&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:first&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;return its full name&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; 
      &lt;span class="ident"&gt;assert_equal&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;John Doe&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;full_name&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="ident"&gt;context&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;with a profile&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
      &lt;span class="ident"&gt;setup&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
        &lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;profile&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Profile&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:first&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;return true when sent #has_profile?&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; 
        &lt;span class="ident"&gt;assert&lt;/span&gt; &lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;has_profile?&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Jeszcze jedna uwaga odno&#347;nie implementacji &lt;span class="caps"&gt;BDD&lt;/span&gt; w innych j&#281;zykach. Troch&#281; mia&#322;em problem&#243;w ze znalezieniem biblioteki do Pythona. &lt;a href="http://pycheesecake.org/wiki/PythonTestingToolsTaxonomy"&gt;Znaleziona obszerna lista bibliotek&lt;/a&gt; do Pythona nie ma kategorii &lt;span class="caps"&gt;BDD&lt;/span&gt;. Wygl&#261;da na to, &#380;e &lt;span class="caps"&gt;BDD&lt;/span&gt; i w og&#243;le kwesti&#261; testowania bardziej przejmuj&#261; si&#281; u&#380;ytkownicy Rubiego. Ale na szcz&#281;&#347;cie na stronie na stronie &lt;a href="http://behaviour-driven.org/Implementations"&gt;http://behaviour-driven.org/Implementations&lt;/a&gt; zamieszczono informacje kt&#243;rych mi brakowa&#322;o. Jeden z link&#243;w prowadzi do &lt;a href="http://darcs.idyll.org/~t/projects/pinocchio/doc/"&gt;Pinocchio&lt;/a&gt;, kt&#243;ry jest  rozszerzeniem bibliotek &lt;a href="http://code.google.com/p/python-nose/"&gt;Nose&lt;/a&gt; u&#380;ywanej m.in. przez framework &lt;a href="http://pylonshq.com"&gt;Pylons&lt;/a&gt;. Co ciekawe, jest te&#380; nawet &lt;a href="http://jania.pe.kr/aw/moin.cgi/JSSpec"&gt;JSSpec&lt;/a&gt;, biblioteka &lt;span class="caps"&gt;BDD&lt;/span&gt; dla j&#281;zyka Javascript.&lt;/p&gt;


	&lt;p&gt;Zobacz te&#380;:&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Beyond Test Driven Development: Behaviour Driven Development&lt;/strong&gt;&lt;/p&gt;


	&lt;blockquote&gt;
		&lt;p&gt;Wyk&#322;adowc&#261; jest Dave Astels, wsp&#243;&#322;autor ksi&#261;&#380;ki &lt;a href="http://www.amazon.com/Practical-Guide-eXtreme-Programming/dp/0130674826"&gt;A Practical Guide to eXtreme Programming&lt;/a&gt; (Google TechTalks)&lt;/p&gt;
	&lt;/blockquote&gt;


&lt;object width="425" height="355"&gt;&lt;param name="movie" value="http://www.youtube.com/v/oOFfHzrIDPk&amp;#38;rel=1"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/oOFfHzrIDPk&amp;#38;rel=1" type="application/x-shockwave-flash" wmode="transparent" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

	&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://thoughtbot.com/projects/shoulda"&gt;Shoulda&lt;/a&gt; &amp;#8211; strona g&#322;&#243;wna&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://chneukirchen.org/blog/archive/2007/01/announcing-test-spec-0-3-a-bdd-interface-for-test-unit.html"&gt;Test/Spec&lt;/a&gt; &amp;#8211; podobny pomys&#322; co Shoulda &amp;#8211; &lt;span class="caps"&gt;BDD&lt;/span&gt; oparte a Unit::Test.&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://behaviour-driven.org/"&gt;Behavour Driven Development&lt;/a&gt; &amp;#8211; strona g&#322;&#243;wna &lt;span class="caps"&gt;BDD&lt;/span&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://behaviour-driven.org/Implementations"&gt;Behaviour Driven Development &amp;#8211; implementations&lt;/a&gt; &amp;#8211; implementacje &lt;span class="caps"&gt;BDD&lt;/span&gt; dla: Boo, C#, .NET, Delphi, Javy, Javascript, Ruby i &lt;strong&gt;Pythona&lt;/strong&gt;.&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://www.stifflog.com/pl/index.php/2006/11/27/behaviour-driven-development/"&gt;Wprowadzenie do RSpec &amp;#8211; &lt;span class="caps"&gt;BDD&lt;/span&gt;&lt;/a&gt; &amp;#8211; polskie wprowadzenie do RSpec i &lt;span class="caps"&gt;BDD&lt;/span&gt;&lt;/li&gt;
	&lt;/ul&gt;</description>
      <pubDate>Sun, 28 Oct 2007 18:45:00 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:eb7df053-32bf-4755-95ba-e9543b215f6d</guid>
      <author>Jaros&#322;aw Zabie&#322;&#322;o</author>
      <link>http://blog.zabiello.com/articles/2007/10/28/shoulda-rspec</link>
      <category>bdd</category>
      <category>tdd</category>
      <category>rspec</category>
      <category>rails</category>
      <category>metodologia</category>
      <category>pinnocchio</category>
      <category>ruby</category>
      <category>python</category>
      <category>pylons</category>
    </item>
    <item>
      <title>Django ograniczeniem w rozwoju?</title>
      <description>&lt;p&gt;&lt;a href="http://djangoproject.com"&gt;Django&lt;/a&gt; do dobry i szybki framework do wi&#281;kszo&#347;ci zastosowa&#324;. Jednak&#380;e ci, kt&#243;rzy chc&#261; za jego pomoc&#261; stworzy&#263; stworzy&#263; wi&#281;kszy projekt, mog&#281; mie&#263; p&#243;&#378;niej problemy z dalszym jego rozwojem&amp;#8230;&lt;/p&gt;


	&lt;p&gt;Jednym z wi&#281;kszych polskich projekt&#243;w stworzonych w Django jest serwis spo&#322;eczno&#347;ciowy &lt;a href="http://grono.net"&gt;grono.net&lt;/a&gt;. Niedawno w jednym z blog&#243;w ukaza&#322; si&#281; &lt;a href="http://www.pydev.pl/?p=127"&gt;wywiad&lt;/a&gt; z dyrektorem dzia&#322;u IT tego serwisu &amp;#8211;  Albertem Szybi&#324;skim. Grono.net pierwotnie u&#380;ywa&#322;o Javy, lecz to si&#281; nie sprawdzi&#322;o. Java zosta&#322; zast&#261;piona pythonowym frameworkiem &lt;a href="http://djangoproject.com"&gt;Django&lt;/a&gt;. Pocz&#261;tkowo wszystko by&#322;o dobrze, ale serwis si&#281; rozrasta&#322; i&amp;#8230;&lt;/p&gt;


	&lt;blockquote&gt;
		&lt;p&gt;&amp;#8220;Doszli&#347;my ju&#380; do takiego poziomu, w kt&#243;rym Django zacz&#261;&#322; by&#263; dla nas problemem. W czasie tworzenia portalu by&#322; to bardzo dobry framework, ale przy obecnym stopniu zaawansowania, Django sta&#322; si&#281; dla nas ograniczeniem w rozwoju i sami musimy go modyfikowa&#263;.&amp;#8221;&lt;/p&gt;
	&lt;/blockquote&gt;


	&lt;p&gt;Czyli potwierdzi&#322;o si&#281; to co &lt;a href="http://blog.zabiello.com/articles/2007/08/25/aplikacja-webowa-wybor-technologii"&gt;pisa&#322;em wcze&#347;niej o Django&lt;/a&gt;. To system zbyt monolityczny i zdecydowanie za ma&#322;o elastyczny. Django ci&#261;gnie za sob&#261; dziedzictwo &lt;span class="caps"&gt;CMS&lt;/span&gt; &lt;a href="http://www.ellingtoncms.com/"&gt;Elington&lt;/a&gt; z jakiego wyr&#243;s&#322;. Mo&#380;e dobrze sprawdza si&#281; w projektach typu &amp;#8220;wertykalny portal dla wydawnictwa&amp;#8221;, ale jak ju&#380; chcemy co&#347; nietypowego, zaczynaj&#261; si&#281; problemy.&lt;/p&gt;


	&lt;p&gt;Na pytanie &amp;#8220;Czy posiadaj&#261;c tak&#261; wiedz&#281; jak&#261; macie dzi&#347;, czy zdecydowaliby&#347;cie si&#281; na zaimplementowanie Grono.net ponowie wykorzystuj&#261;c Django, czy mo&#380;e co&#347; innego?&amp;#8221; pad&#322;o wskazanie na &lt;a href="http://pylonshq.com"&gt;Pylons&lt;/a&gt;. Mo&#380;e nie ma tak &#322;adnie zgrupowanych modu&#322;&#243;w w jednym miejscu jak Django, ale na pewno ma wi&#281;kszy potencja&#322; do tworzenia nietypowych czy skomplikowanych serwis&#243;w.&lt;/p&gt;


	&lt;p&gt;To nie Django jest prawdziw&#261; pythonow&#261; konkurencj&#261; dla popularnego &lt;a href="http://rubyonrails.org"&gt;Ruby on Rails&lt;/a&gt; (&#380;e nie wspomn&#281; frameworki pehapowe). Moim zdaniem, bardziej gro&#378;n&#261; dla nich konkurencj&#261; jest &lt;strong&gt;Pylons&lt;/strong&gt;.&lt;/p&gt;</description>
      <pubDate>Sat, 27 Oct 2007 16:31:00 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:39425c6c-58e5-4fc6-b40f-12de8d5e4be6</guid>
      <author>Jaros&#322;aw Zabie&#322;&#322;o</author>
      <link>http://blog.zabiello.com/articles/2007/10/27/django-ograniczeniem-w-rozwoju</link>
      <category>pylons</category>
      <category>django</category>
      <category>python</category>
    </item>
    <item>
      <title>Aplikacja webowa - wyb&#243;r technologii</title>
      <description>&lt;p&gt;Do&#347;&#263; cz&#281;sto spotykam si&#281; z pro&#347;b&#261; o to, jak&#261; technologi&#281; (i framework) bym poleci&#322; do tworzenia aplikacji webowych. Dziedzina aplikacji webowych rozwija si&#281; bardzo pr&#281;&#380;nie i trudno tak naprawd&#281; przewidzie&#263;, co b&#281;dzie najlepszym wyborem za par&#281; lat. Niniejszy artyku&#322; stanowi kr&#243;tkie podsumowanie moich do&#347;wiadcze&#324; w tej dziedzinie na dzie&#324; &lt;em&gt;dzisiejszy&lt;/em&gt;.&lt;/p&gt;


	&lt;h3&gt;Nie wierz&#281; w przysz&#322;o&#347;&#263; architektur webowych opartych na j&#281;zykach kompilowanych,  statycznie typowanych.&lt;/h3&gt;


	&lt;p&gt;Na ten temat mo&#380;na by du&#380;o dyskutowa&#263;. Po prostu nie wierz&#281; w przysz&#322;o&#347;&#263; budowania aplikacji webowych w j&#281;zykach C# czy Java. Ka&#380;da zmiana kodu wymaga rekompilacji, wymiany dll itp. Mam z&#322;e do&#347;wiadczenia z &lt;span class="caps"&gt;ASP&lt;/span&gt;.NET. Bardzo wolno i mozolnie si&#281; tworzy i debuguje kod. Z kolei Java (tzn. aplikacja webowa pisana napisana w tym j&#281;zyku) jest ci&#281;&#380;ka i skomplikowana. Aplikacje webowe s&#261; dosy&#263; specyficzne. Znacznie lepiej sprawdzaj&#261; si&#281; tam j&#281;zyki wysokopoziomowe i &lt;em&gt;interpretowane&lt;/em&gt;. Nie trzeba nic restartowa&#263;, rekompilowa&#263;, efekty zmian s&#261; prawie natychmiastowo dost&#281;pne. My&#347;l&#281;, &#380;e wi&#281;kszo&#347;&#263; aplikacji webowych, szczeg&#243;lniej ma&#322;ej i &#347;redniej klasy z&#322;o&#380;ono&#347;ci, zostanie opanowana przez j&#281;zyki interpretowane. Po cz&#281;&#347;ci ju&#380; to uczyni&#322; &lt;span class="caps"&gt;PHP&lt;/span&gt;. Jednak&#380;e&amp;#8230;&lt;/p&gt;


	&lt;h3&gt;Nie wierz&#281; w przysz&#322;o&#347;&#263; &lt;span class="caps"&gt;PHP&lt;/span&gt;.&lt;/h3&gt;


	&lt;p&gt;Na temat wszystkich wad &lt;span class="caps"&gt;PHP&lt;/span&gt; m&#243;g&#322;bym du&#380;o pisa&#263;. W ka&#380;dym razie nie mog&#281; si&#281; nadziwi&#263;, dlaczego jego tworcy wci&#261;&#380; inspiruj&#261; si&#281; sk&#322;adni&#261; Perla i Javy zamiast wzorowa&#263; si&#281; na prostocie i elegancji Pythona czy Ruby.  &lt;span class="caps"&gt;PHP&lt;/span&gt; nie nadaje si&#281; do modularyzacji, tworzenia komponent&#243;w. Pr&#243;bowa&#322;em ju&#380; kiedy&#347; uzyska&#263; efekt komponent&#243;w w &lt;a href="http://smarty.php.net/"&gt;Smarty&lt;/a&gt;. Nie da si&#281;. G&#322;&#243;wnym problemem jest kolizja nazw klas i funkcji. Przestrze&#324; nazw mia&#322;a by&#263; dodana w &lt;span class="caps"&gt;PHP5&lt;/span&gt;. Nie dodali, bo nie umieli. &lt;span class="caps"&gt;PHP&lt;/span&gt; brnie wi&#281;c nadal w swoim chaosie wielkiej, wsp&#243;lnej przestrzeni nazw dla swych wszystkich, chaotycznie nazwanych funkcji o niesp&#243;jnej kolejno&#347;ci przekazywanych parametr&#243;w. Czy w &lt;span class="caps"&gt;PHP&lt;/span&gt; nie da si&#281; napisa&#263; dobrej aplikacji? Owszem, da. Jak kto&#347; si&#281; uprze to i w Assemblerze tak&#261; napisze. Tylko jakim kosztem&amp;#8230; Kierunek rozwoju tego j&#281;zyka wygl&#261;da na przypadkowy. Sam j&#281;zyk jest &#378;le zaprojektowany. Nie wierz&#281; w jego przysz&#322;o&#347;&#263; tym bardziej, &#380;e wyros&#322;a mu powa&#380;na konkurencja.&lt;/p&gt;


	&lt;h3&gt;Fenomem Ruby on Rails.&lt;/h3&gt;


	&lt;p&gt;Powodem rosn&#261;cej popularno&#347;ci &lt;a href="http://rubyonrails.org"&gt;Ruby on Rails&lt;/a&gt;, wbrew zazdrosnym opiniom co niekt&#243;rych, na pewno nie jest &lt;em&gt;tylko&lt;/em&gt; efekt dobrego marketingu. To tak&#380;e efekt pewnej, nowej filozofii w podej&#347;ciu do tematu. Rails mocno postawi&#322; na prostot&#281; i elegancj&#281; kodu po&#322;&#261;czon&#261; z kompleksowo&#347;ci&#261; rozwi&#261;zania. Do tego dobrze dobrane warto&#347;ci domy&#347;lne (ograniczaj&#261;ce ilo&#347;&#263; niezb&#281;dnych konfiguracji) plus wygodne generatory kodu prowadz&#261; cz&#322;owieka dos&#322;ownie za r&#281;k&#281;. Rails doczeka&#322; si&#281; te&#380; &lt;a href="http://deadlock.netbeans.org/hudson/job/ruby/"&gt;dobrego &lt;span class="caps"&gt;IDE&lt;/span&gt;&lt;/a&gt; co powoduje, &#380;e w tym frameworku pracuje si&#281; dosy&#263; przyjemnie. Jest przejrzysty i czytelnie zorganizowany. Zainspirowa&#322; i znalaz&#322; ju&#380; wielu na&#347;ladowc&#243;w, ale nigdy nie uzyskaj&#261; one jego elegancji i prostoty z prostej przyczyny: si&#322;&#261; Rails jest prostota i dynamika j&#281;zyka &lt;a href="http://ruby-lang.org"&gt;Ruby&lt;/a&gt;. Dynamika wi&#281;ksza nawet od &lt;a href="http://python.org"&gt;Pythona&lt;/a&gt;. Sukces Rails&#243;w wzmacnia tak&#380;e &lt;a href="http://weblog.rubyonrails.org/2007/5/7/ruby-and-rails-continues-book-bonanza"&gt;lawinowo rosn&#261;ca ilo&#347;&#263; ksi&#261;&#380;ek&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;Rails zaczyna si&#281; wdziera&#263; do rozwi&#261;za&#324; korporacyjnych. Nie zast&#261;pi on z pewno&#347;ci&#261; Javy, ale na pewno lepiej od niej nadaje si&#281; do szybkiego stworzenia dynamicznych interfejs&#243;w webowych. Z chwil&#261; okrzepni&#281;cia JRuby (javowej implementacji Rubiego), Rails oparty na Javie (ale ze sk&#322;adni&#261; Rubiego) mo&#380;e zrobi&#263; jeszcze sporo zamieszania.&lt;/p&gt;


	&lt;p&gt;Jednak&#380;e, jest technologia, mo&#380;e nie a&#380; tak prosta, ale na pewno pod wieloma wzgl&#281;dami dojrzalsza i szybsza.&lt;/p&gt;


	&lt;h3&gt;Django i Pylons.&lt;/h3&gt;


	&lt;p&gt;Tak jak Rails, &lt;a href="http://djangoproject.com"&gt;Django&lt;/a&gt; powsta&#322; na bazie aplikacji zastosowywanej komercyjnie. Podobnie te&#380; do  Rails, stara si&#281; podchodzi&#263; kompleksowo podaj&#261;c na tacy okre&#347;lony (autorski) &lt;span class="caps"&gt;ORM&lt;/span&gt;, system szablon&#243;w i spos&#243;b rozwi&#261;zywania adres&#243;w &lt;span class="caps"&gt;URL&lt;/span&gt;. Django jest  szybsze od Rails&#243;w. Wynika to dw&#243;ch rzeczy. Python (jako j&#281;zyk) jest szybszy od Rubiego oraz Django od pocz&#261;tku by&#322;o projektowane z my&#347;l&#261; o optymalizacji wydajno&#347;ciowej. (Rails skupia&#322; si&#281; od pocz&#261;tku na uzyskaniu funkcjonalno&#347;ci, zrzucaj&#261;c optymalizacj&#281; na p&#243;&#378;niej). O zaletach Django &lt;a href="http://blog.zabiello.com/articles/2006/05/27/django-wy%C5%BCszy-poziom-abstrakcji"&gt;pisa&#322;em rok temu&lt;/a&gt;. Django po&#380;era te&#380; wyra&#378;nie mniej pami&#281;ci ni&#380; Rails odpalany na &lt;a href="http://mongrel.rubyforge.org/"&gt;mongrelach&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;Niestety, mo&#380;na si&#281; przyczepi&#263; do kilku rzeczy. G&#322;&#243;wna krytyka Django dotyczy jego zbytniej monolityczno&#347;ci i mniejszej elastyczno&#347;ci. Rails pozwala podpina&#263; si&#281; do wielu baz, Django tylko do jednej. Rails posiada migracje do kontrolowania ewolucji struktury bazy. Django nic takiego nie posiada (tzn. jest w fazie rozwojowej gdzie&#347; w bocznych ga&#322;&#281;ziach repozytorium &lt;span class="caps"&gt;SVN&lt;/span&gt;). Rails posiada &#347;wietne helpery do  generowania adres&#243;w &lt;span class="caps"&gt;URL&lt;/span&gt;. Djangowy system adres&#243;w jest znacznie gorszy. Nie ma mo&#380;liwo&#347;ci zmiany struktury aplikacji i wygenerowania zmienionych link&#243;w we wszystkich szablonach. Rails, dzi&#281;ki helperom to umo&#380;liwia. Rails posiada &#347;wietne helpery i specjalne szablony uproszczaj&#261;ce korzystanie z &lt;span class="caps"&gt;AJAX&lt;/span&gt;&amp;#8217;a, Django nic tu nie oferuje.&lt;/p&gt;


	&lt;p&gt;Wiele z wymienionych wy&#380;ej wad nie posiada pythonowy konkurent Django &amp;#8211; &lt;a href="http://pylonshq.com"&gt;Pylons&lt;/a&gt;. Pisalem o tym &lt;a href="http://blog.zabiello.com/articles/2006/03/04/pylons-czyli-si%C5%82a-myghty-i-wygoda-rubyonrails"&gt;ponad roku temu&lt;/a&gt;,  &#380;e Pylons skopiowa&#322; prawie wszystkie helpery jakie posiada Rails. Rozwi&#261;zuje adresy te&#380; w podobny spos&#243;b umo&#380;liwiaj&#261;c bardziej elastyczne modyfikowanie struktury kontroler&#243;w. Wszystkie zale&#380;ne adresy &lt;span class="caps"&gt;URL&lt;/span&gt; s&#261; generowane za pomoc&#261; helper&#243;w w spos&#243;b podobny do Rails. Pylons w zasadzie to taka sklejanka zewn&#281;trznych bibliotek Pythona. Wykorzystuj&#261;c standardowy mechanizm &lt;a href="http://www.python.org/dev/peps/pep-0333/"&gt;&lt;span class="caps"&gt;WSGI&lt;/span&gt;&lt;/a&gt;, Pylons pozwala na wymian&#281; prawie ka&#380;dego swojego elementu, od &lt;span class="caps"&gt;ORM&lt;/span&gt;, po system szablon&#243;w czy resolver adres&#243;w &lt;span class="caps"&gt;URL&lt;/span&gt;.&lt;/p&gt;


	&lt;p&gt;Wiele z element&#243;w, jakie domy&#347;lnie oferuje Pylons przewy&#380;sza elastyczno&#347;ci&#261; i mo&#380;liwo&#347;ciami to, co dost&#281;pne jest w Django.  Np. Pylons korzysta domy&#347;lnie z &lt;a href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt;, kt&#243;ry jest pot&#281;&#380;niejszy od tego w Rails i Django&lt;sup&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;. Nie tylko bez problemu pozwala podpi&#261;&#263; si&#281; do kilku baz, ale tak&#380;e do bardziej skomplikowanych struktur&lt;sup&gt;&lt;a href="#fn2"&gt;2&lt;/a&gt;&lt;/sup&gt;. Domy&#347;lny system szablon&#243;w &lt;a href="http://makotemplates.org"&gt;Mako&lt;/a&gt; jest najlepszym systemem jaki znam. Oferuje rozbudowan&#261;&lt;sup&gt;&lt;a href="#fn3"&gt;3&lt;/a&gt;&lt;/sup&gt; obiektowo&#347;&#263;, prost&#261; sk&#322;adni&#281;, doskona&#322;y cache dowolnego fragmentu i wg dowolnych zasad. Co&#347;, o czym np. Rails mo&#380;e tylko pomarzy&#263;&lt;sup&gt;&lt;a href="#fn4"&gt;4&lt;/a&gt;&lt;/sup&gt;. No i Pylons udost&#281;pnia szybki, wielow&#261;tkowy serwer &lt;span class="caps"&gt;HTTP&lt;/span&gt; kt&#243;ry mo&#380;na bez problemu u&#380;ywa&#263; zar&#243;wno do deweloperki jak i produkcyjnie. Ani Django ani Rails nie obs&#322;uguj&#261; wielow&#261;tkowo&#347;ci. Pylons obs&#322;uguje, dzi&#281;ki czemu te&#380; ma mniejsze zapotrzebowanie na pami&#281;&#263;.&lt;/p&gt;


	&lt;p&gt;Pylons oferuje wi&#281;c wi&#281;cej mo&#380;liwo&#347;ci, ale kosztem wi&#281;kszej z&#322;o&#380;ono&#347;ci. Django jest troch&#281; prostszy do opanowania. Ca&#322;a dokumentacja jest w jednym miejscu. Nauka Pylons wymaga si&#281;gni&#281;cia do oddzielnej dokumentacji SQLAlchemy, Mako czy &lt;a href="http://routes.groovie.org/"&gt;Routes&lt;/a&gt;. Sam SQLAlchemy jest te&#380; bardzo rozbudowany i trudniejszy do opanowania.&lt;/p&gt;


	&lt;h3&gt;Zope 3 &amp;#8211; najwy&#380;sza poprzeczka.&lt;/h3&gt;


	&lt;p&gt;Napisany w Pythonie, obiektowy, wielow&#261;tkowy serwer aplikacyjny &lt;a href="http://zope.org"&gt;Zope&lt;/a&gt; od dawna uchodzi&#322; za &amp;#8220;zab&#243;jcz&#261; aplikacj&#281;&amp;#8221; Pythona. Powsta&#322;o do niego tysi&#261;ce plugin&#243;w. Jednym z najs&#322;ynniejszych jest &lt;a href="http://plone.org"&gt;Plone &lt;span class="caps"&gt;CMS&lt;/span&gt;&lt;/a&gt; (wg mnie najlepszy &lt;span class="caps"&gt;CMS&lt;/span&gt; jaki napisano). Zope 2.x by&#322; jednak&#380;e krytykowany za zbytni&#261; z&#322;o&#380;ono&#347;&#263; i ba&#322;agan w &lt;span class="caps"&gt;API&lt;/span&gt;. Nauczenie si&#281; Zope wymaga&#322;o sporo wysi&#322;ku tak&#380;e z powodu s&#322;abej dokumentacji. Wi&#281;kszo&#347;&#263; lekkich framework&#243;w Pythona powsta&#322;o troch&#281; na bazie kontestacji Zope. Na szcz&#281;&#347;cie, tw&#243;rcy wyci&#261;gn&#281;li lekcj&#281; z Zope 2 i kod dla wersji 3 zosta&#322; napisany od zera. Znacznie uproszczono i uporz&#261;dkowano &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/p&gt;


	&lt;p&gt;Rails, Django czy Pylons s&#261; gro&#378;n&#261; konkurencj&#261; przede wszystkim dla popularnego &lt;span class="caps"&gt;PHP&lt;/span&gt;. To jest mniej wi&#281;cej dziedzina ich zastosowa&#324;. Zope za&#347; to troch&#281; inna filozofia i inna  grupa docelowa. Zope ma ambicj&#281; aby konkurowa&#263; z najpowa&#380;niejszymi zastosowaniami klasy enterprise opanowanymi g&#322;&#243;wnie przez Jav&#281;.&lt;/p&gt;


	&lt;p&gt;Przede wszystkim Zope to serwer aplikacyjny w pe&#322;ni obiektowy i o budowie komponentowej. Ka&#380;dy komponent ma &#347;ci&#347;le zdefiniowane interfejsy&lt;sup&gt;&lt;a href="#fn5"&gt;5&lt;/a&gt;&lt;/sup&gt;. Zope korzysta standardowo z &lt;a href="https://launchpad.net/zodb"&gt;prawdziwej bazy obiektowej&lt;/a&gt;. Nie potrzebuje &#380;adnego wspierania si&#281; wrapperami &lt;span class="caps"&gt;ORM&lt;/span&gt;. Baza obiektowa zapisuje ca&#322;e obiekty Pythona w spos&#243;b transparentny i stanowi naturalne przed&#322;u&#380;enie pisanego kodu. Zope 3 ma oczywi&#347;cie wbudowane wszystko co potrzebuje typowa aplikacja webowa, dobry cache, wbudowane mechanizmy bezpiecze&#324;stwa, przep&#322;ywu danych, lokalizacji interfejsu (i18n, l10n) itp. Pozwala te&#380; na prze&#322;adowywanie komponent&#243;w bez restartu serwera. Je&#347;li potrzeba nowej funkcjonalno&#347;ci, wystarczy napisa&#263; nowy komponent i podpi&#261;&#263; do serwera. Nie ma znaczenia czy tym komponentem b&#281;dzie &amp;#8220;artyku&#322;&amp;#8221;, czy &amp;#8220;plik graficzny&amp;#8221; czy ca&#322;y system redakcyjny, b&#261;d&#378; &lt;span class="caps"&gt;CMS&lt;/span&gt;.&lt;/p&gt;


	&lt;p&gt;Te mo&#380;liwo&#347;ci jednak nie s&#261; bez wad. Do najwi&#281;kszych zaliczy&#322;bym s&#322;ab&#261; promocj&#281; (to w og&#243;le jaka&#347; przypad&#322;o&#347;&#263; Pythona i jego aplikacji) oraz wci&#261;&#380; za s&#322;ab&#261; dokumentacj&#281;. Na szcz&#281;&#347;cie ta ostatnia rzec si&#281; troch&#281; poprawi&#322;a. Nawet s&#261; ju&#380; ksi&#261;&#380;ki dedykowane &lt;a href="http://worldcookery.com/"&gt;specjalnie dla Zope 3.3&lt;/a&gt;. Zope potrzebuje te&#380; znacznie wi&#281;cej pami&#281;ci ni&#380; taki Django czy Pylons.&lt;/p&gt;


	&lt;h3&gt;Ku przysz&#322;o&#347;ci.&lt;/h3&gt;


	&lt;p&gt;Dziedzina aplikacji webowych rozwija si&#281; bardzo dynamicznie i trudno co&#347; przewidzie&#263; w dalszej przysz&#322;o&#347;ci. W zwi&#261;zku z dynamicznym rozwojem procesor&#243;w wielordzeniowych, kto wie, czy renesansu nie prze&#380;yje j&#281;zyk &lt;a href="http://pl.wikipedia.org/wiki/Erlang_%28j%C4%99zyk_programowania%29"&gt;Erlang&lt;/a&gt; (bo doskonale si&#281; nadaje do programowania wsp&#243;&#322;bie&#380;nego). By&#263; mo&#380;e renesans prze&#380;yj&#261; serwery kontynuacyjne, kt&#243;re zapewniaj&#261; zachowanie stanu dla aplikacji webowej eliminuj&#261;c potrzeb&#281;  u&#380;ywania sesji. W serwerze kontynuacyjnym, pisanie kodu dla aplikacji webowej, czy desktopowej, prawie niczym si&#281; nie r&#243;&#380;ni. Na razie najbardziej znanym serwerem tego typu jest napisany w Smalltalku &amp;#8211; &lt;a href="http://en.wikipedia.org/wiki/Seaside_%28software%29"&gt;Seaside&lt;/a&gt;. Du&#380;y potencja&#322; ma te&#380; Ruby, bo posiada wbudowan&#261; obs&#322;ug&#281; kontynuacji.&lt;/p&gt;


	&lt;p&gt;Na pewno obawa co do znalezienia pracy w Rails czy Pythonie raczej nie ma du&#380;ego uzasadnienia. Jest wiele firm u&#380;ywaj&#261;cych Pythona. Najlepszym przyk&#322;adem jest Google, kt&#243;ry prawie wszystko ma oparte na Pythonie. Od czasu do czasu pojawiaj&#261; si&#281; w Polsce dramatyczne ;) wo&#322;ania o programist&#243;w Pythona, wi&#281;c cho&#263; ofert nie ma tyle co dla Javy i .NET, to mo&#380;na co&#347; wyszuka&#263;. Je&#347;li chodzi o Ruby on Rails, to ofert zaczyna by&#263; coraz wi&#281;cej, jest ju&#380; troch&#281; wi&#281;cej ni&#380; dla Pythona. Rails p&#281;dzi na pe&#322;nej szybko&#347;ci i dzi&#281;ki dobre promocji wdziera si&#281; coraz szerzej do &#347;wiadomo&#347;ci programist&#243;w g&#322;&#243;wnego nurtu. Sam pracuj&#281; w firmie gdzie g&#322;&#243;wny system oparty jest na Springu i Hibernate, technologiach Javy. Jednak&#380;e do tworzenia aplikacji webowej zosta&#322; wybrany Ruby on Rails.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Podsumowuj&#261;c&lt;/strong&gt; powiedzia&#322;bym, &#380;e wsp&#243;&#322;czesny programista musi operowa&#263; kilkoma j&#281;zykami i technologiami. Dzisiaj pracuje si&#281; w heterogenicznym &#347;rodowisku i wykorzystuje mieszane technologie. Kto my&#347;li, &#380;e nauczy si&#281; tylko jednego, doskona&#322;ego j&#281;zyka i tylko jednego frameworka, pewnie mocno si&#281; rozczaruje w zderzeniu z rzeczywisto&#347;ci&#261;&lt;sup&gt;&lt;a href="#fn6"&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;


	&lt;p&gt;Do lekkich webowych interfejs&#243;w z du&#380;&#261; ilo&#347;ci&#261; &lt;span class="caps"&gt;AJAX&lt;/span&gt;&amp;#8217;a wybra&#322;bym Rails. Tak&#380;e dla os&#243;b pocz&#261;tkuj&#261;cych, pragn&#261;cych nabra&#263; dobrych nawyk&#243;w, wybra&#322;bym Rails&lt;sup&gt;&lt;a href="#fn7"&gt;7&lt;/a&gt;&lt;/sup&gt;.  Tam, gdzie wymagane jest jednak dobre wsparcie dla Unicode oraz gdzie wydajno&#347;&#263; jest bardziej kluczowa, wybra&#322;bym Pylons&lt;sup&gt;&lt;a href="#fn8"&gt;8&lt;/a&gt;&lt;/sup&gt;. Z kolei, je&#347;li chcemy uderzy&#263; w rynek bardziej kompleksowych rozwi&#261;za&#324; dla biznesu, Zope 3 b&#281;dzie raczej trudny do pobicia. I w ko&#324;cu, tam, gdzie potrzebujemy gotowego, dobrego forum i taniego hostingu najlepiej sprawdzi si&#281; &lt;span class="caps"&gt;PHP&lt;/span&gt; (tak, najlepsze, gotowe do u&#380;ycia forum s&#261; napisane w &lt;span class="caps"&gt;PHP&lt;/span&gt;).&lt;/p&gt;


	&lt;p&gt;&lt;em&gt;Apendix&lt;/em&gt;: Jest dost&#281;pne &lt;a href="http://www.makotemplates.org/trac"&gt;kolorowanie sk&#322;adni&lt;/a&gt; Mako dla kilku edytor&#243;w (TextMate, Emacs, Vim i Komodo). &lt;small&gt;(2007-09-12)&lt;/small&gt;&lt;/p&gt;


&lt;hr size="1" /&gt;

	&lt;p id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; Tak w og&#243;le to kwestia por&#243;wnania Active Record, SQLAlchemy i Django nie jest taka oczywista. Ka&#380;dy posiada co&#347;, czego nie posiada drugi. Np. Active Record posiada eleganckie walidatory, kt&#243;rych nie ma w SQLAlchemy. Z kolei Django nie umie podpi&#261;&#263; si&#281; do wielu baz, ale posiada wygodne, wysokopoziomowe pola w modelu. SQLAlchemy za&#347; podepnie si&#281; do najbardziej pogmatwanej struktury, wielu baz i to wszystko w elegancki, obiektowy spos&#243;b, a nie z brutalnymi wstawkami kodu &lt;span class="caps"&gt;SQL&lt;/span&gt;, jak ma miejsce w Active Record. Poza tym SQLAlchemy potrafi optymalizowa&#263; zapytania tak, aby kilka polece&#324; wykona&#263; mo&#380;liwe najmniejsz&#261; ilo&#347;ci&#261; kwerend i po&#322;&#261;cze&#324; z baz&#261;.&lt;/p&gt;


	&lt;p id="fn2"&gt;&lt;sup&gt;2&lt;/sup&gt; Rails nie ma problemu podpi&#281;cia si&#281; do wielu baz. Za&#347; struktury takie jak z&#322;o&#380;one klucze g&#322;&#243;wne potrafi obs&#322;u&#380;y&#263; za pomoc&#261; &lt;a href="http://compositekeys.rubyforge.org/"&gt;odpowiednich plugin&#243;w&lt;/a&gt;.&lt;/p&gt;


	&lt;p id="fn3"&gt;&lt;sup&gt;3&lt;/sup&gt; Mako pozwala na wi&#281;cej ni&#380; proste dziedziczenie tak jak ma Django. Pozwala na &lt;a href="http://www.makotemplates.org/docs/inheritance.html#inheritance_next"&gt;kaskad&#281; dziedziczenia&lt;/a&gt;.&lt;/p&gt;


	&lt;p id="fn4"&gt;&lt;sup&gt;4&lt;/sup&gt; Rails ma s&#322;abe mo&#380;liwo&#347;ci w zakresie buforowania danych. Np. nie potrafi buforowa&#263; fragment&#243;w szablon&#243;w w oparciu o unikalny klucz (z parametr&#243;w adresu &lt;span class="caps"&gt;URL&lt;/span&gt; lub w oparciu o inne kryteria).&lt;/p&gt;


	&lt;p id="fn5"&gt;&lt;sup&gt;5&lt;/sup&gt; Sam Python nie posiada wbudowanej sk&#322;adni do tworzenia interfejs&#243;w czy klas abstrakcyjnych. Autorzy Zope 3 jednak je cz&#281;&#347;ciowo zasymulowali korzystaj&#261;c z tego, co oferuje j&#281;zyk.&lt;/p&gt;


	&lt;p id="fn6"&gt;&lt;sup&gt;6&lt;/sup&gt; W tym wszystkim za ma&#322;o pad&#322;o o znajomo&#347;ci JavaScript. Nie jest to zbyt prosty i intuicyjny j&#281;zyk, a ma spore mo&#380;liwo&#347;ci i jest bardzo dynamiczny. Jego znajomo&#347;&#263; jest absolutnie niezb&#281;dna dla ka&#380;dej osoby chc&#261;cej tworzy&#263; aplikacje internetowe. &lt;span class="caps"&gt;O CSS&lt;/span&gt; i &lt;span class="caps"&gt;XHTML&lt;/span&gt; nie wspominam, bo to oczywiste podstawy.&lt;/p&gt;


	&lt;p id="fn7"&gt;&lt;sup&gt;7&lt;/sup&gt; Rozpoczynanie przygody z tworzeniem aplikacji webowych od &lt;span class="caps"&gt;PHP&lt;/span&gt; jest najgorsz&#261; rzecz&#261; jak&#261; mo&#380;emy zrobi&#263;. &lt;span class="caps"&gt;PHP&lt;/span&gt; za bardzo zach&#281;ca i utrwala z&#322;e nawyki programistyczne. To w&#322;a&#347;nie mia&#322; na my&#347;li &lt;span class="caps"&gt;DHH&lt;/span&gt;, tw&#243;rca Rails, kiedy por&#243;wna&#322; &lt;span class="caps"&gt;PHP&lt;/span&gt; do&amp;#8230; diab&#322;a. :)&lt;/p&gt;


	&lt;p id="fn8"&gt;&lt;sup&gt;8&lt;/sup&gt;  A co z Django? Je&#347;li mam wybiera&#263; mi&#281;dzy Django a Pylons, to wol&#281; Pylons. Ma wi&#281;ksze mo&#380;liwo&#347;ci i osi&#261;gn&#261;&#322; wystarczaj&#261;c&#261; dojrza&#322;o&#347;&#263; aby podwa&#380;y&#263; dominacj&#281; i popularno&#347;&#263; Django w kr&#281;gach pythonistas. Jedn&#261; z polskich firm, kt&#243;ra wdro&#380;y&#322;a i u&#380;ywa z sukcesem Pylons&#243;w jest firma, w kt&#243;rej sp&#281;dzi&#322;em par&#281; lat i gdzie wdro&#380;y&#322;em kiedy&#347; Pythona &amp;#8211; &lt;a href="http://www.muratordom.pl"&gt;Wydawnictwo Murator&lt;/a&gt;. Pozdrowienia dla ekipy. :)&lt;/p&gt;</description>
      <pubDate>Sat, 25 Aug 2007 21:22:00 +0200</pubDate>
      <guid isPermaLink="false">urn:uuid:f173a45f-730a-4ea9-97f1-330d88a27cc0</guid>
      <author>Jaros&#322;aw Zabie&#322;&#322;o</author>
      <link>http://blog.zabiello.com/articles/2007/08/25/aplikacja-webowa-wybor-technologii</link>
      <category>python</category>
      <category>ruby</category>
      <category>php</category>
      <category>rails</category>
      <category>django</category>
      <category>pylons</category>
    </item>
    <item>
      <title>Szablony i wzorzec MVC - cz. III</title>
      <description>&lt;p&gt;W &lt;a href="http://blog.zabiello.com/articles/2007/01/06/templates-and-mvc-2"&gt;poprzedniej cz&#281;&#347;ci&lt;/a&gt; om&#243;wiono szablony dedykowane g&#322;&#243;wnie do wsp&#243;&#322;pracy z designerami boj&#261;cymi si&#281; programowa&#263;. By&#322;y to albo szablony wk&#322;adaj&#261;ce swoj&#261; logik&#281; do znacznik&#243;w &lt;span class="caps"&gt;XML&lt;/span&gt;/XHTML (ZPT, SimpleTAL, Kid, Genshi, MasterView, &lt;span class="caps"&gt;PHP TAL&lt;/span&gt;), albo szablony posiadaj&#261;ce sw&#243;j w&#322;asny, specjalizowany j&#281;zyk (Smarty, Django, Jinja, Liquid). W za&#322;o&#380;eniu taki j&#281;zyk mia&#322; by&#263; prostym substytutem j&#281;zyka u&#380;ywanego w warstwie kontrolera. Szablony tego typu mo&#380;e s&#261; wygodne dla designera, ale na pewno nie dla programisty, kt&#243;ry musi je &lt;strong&gt;przygotowa&#263;&lt;/strong&gt;. Po pierwsze, trzeba si&#281; uczy&#263; kolejnego j&#281;zyka&amp;#8230; szablon&#243;w. Po drugie, wszystkie tego typu j&#281;zyki s&#261; ograniczaj&#261;ce dla programisty. Mimo &#380;e pozornie prostsze, w praktyce s&#261; bardziej z&#322;o&#380;one i to nawet dla designera. Np. szablony Django i Jinja nie posiadaj&#261; &#380;adnej sk&#322;adni na okre&#347;lenie trywialnego warunku wi&#281;kszo&#347;ci, mniejszo&#347;ci:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_django "&gt;{{ x }} jest 
{% if x &amp;gt; y %} 
    wi&#281;ksze 
{% else %} 
    mniejsze 
{% endif %} 
od {{ y }}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Powoduje to mas&#281; k&#322;opot&#243;w dla programisty&lt;sup&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;. Prymitywna sk&#322;adnia szablonu wymusza wi&#281;c kup&#281; dodatkowej pracy. O ile&#380; pro&#347;ciej jest u&#380;y&#263; po prostu pe&#322;nej sk&#322;adni j&#281;zyka tak jak to robi&#261; szablony Cheetah:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_cheetah "&gt;$x jest 
# if x &amp;gt; y 
    wi&#281;ksze 
# else 
    mniejsze 
# endif  
od $y&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Dochodzimy tu wi&#281;c do sytuacji kiedy by&#347;my chcieli co&#347; bardziej elastycznego &amp;#8211; pe&#322;nego dost&#281;pu do j&#281;zyka programowania. Taka sytuacja jest po&#380;&#261;dana wtedy kiedy i tak szablony buduj&#261; programi&#347;ci na podstawie dostarczonego czystego kodu &lt;span class="caps"&gt;HTML&lt;/span&gt; od designer&#243;w.&lt;/p&gt;


	&lt;h2&gt;&lt;span class="caps"&gt;PHP&lt;/span&gt;&lt;/h2&gt;


	&lt;p&gt;J&#281;zyk &lt;span class="caps"&gt;PHP&lt;/span&gt; jest dosy&#263; specyficzny, bo w istocie jest to j&#281;zyk szablon&#243;w. Tzn. pierwotnie tak zosta&#322; stworzony, ale z czasem si&#281; skomplikowa&#322; &#380;e dost&#261;pi&#322; miana j&#281;zyka skryptowego. Ale jego korzenie wida&#263; w sk&#322;adni, kt&#243;ra z definicji jest zagnie&#380;d&#380;aniem instrukcji &lt;span class="caps"&gt;PHP&lt;/span&gt; wewn&#261;trz kodu &lt;span class="caps"&gt;HTML&lt;/span&gt;. Id&#261;c tym tropem, niekt&#243;rzy pomy&#347;leli, &#380;e nie ma sensu wymy&#347;la&#263; dodatkow&#261; sk&#322;adni&#281;. Wystarczy u&#380;y&#263; &lt;span class="caps"&gt;PHP&lt;/span&gt; w odpowiedni spos&#243;b. I tak powsta&#322;y szablony &lt;a href="http://phpsavant.com/yawiki/"&gt;&lt;strong&gt;Savant&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;W&#322;a&#347;ciwie to Savant nie powinny nazywa&#263; si&#281; szablonami, bo to nic innego jak &lt;span class="caps"&gt;PHP&lt;/span&gt; u&#380;yty w taki spos&#243;b, aby rozdzieli&#263; warstw&#281; prezentacji, od wartwy logiki biznesowej. A skoro to &lt;span class="caps"&gt;PHP&lt;/span&gt;, to odpada nauka dodatkowej sk&#322;adni oraz potrzeby kompilacji specjalizowanego j&#281;zyka szablon&#243;w do ko&#324;cowego kodu &lt;span class="caps"&gt;PHP&lt;/span&gt; (jak to ma miejsce w szablonach &lt;a href="http://smarty.php.net"&gt;Smarty&lt;/a&gt;).&lt;/p&gt;


	&lt;p&gt;Czy takie podej&#347;cie jest lepsze, czy gorsze, to ju&#380; ka&#380;dy mo&#380;e sobie oceni&#263;. Moim zdaniem &lt;span class="caps"&gt;PHP&lt;/span&gt; ma na tyle m&#281;tn&#261; sk&#322;adni&#281;, &#380;e warstwa prezentacji u&#380;ywaj&#261;ca Smarty&lt;sup&gt;&lt;a href="#fn2"&gt;2&lt;/a&gt;&lt;/sup&gt; jest bardziej czytelna ni&#380; czysty &lt;span class="caps"&gt;PHP&lt;/span&gt; jaki u&#380;ywaj&#261; Savant.&lt;/p&gt;


	&lt;h2&gt;Ruby&lt;/h2&gt;


	&lt;p&gt;Kiedy m&#243;wimy o Ruby w kontek&#347;cie stron internetowych to najcz&#281;&#347;ciej mamy na my&#347;li jego najs&#322;ynniejszy framework &amp;#8211; &lt;a href="http://rubyonrails.org"&gt;Rails&lt;/a&gt;. Ruby sam w sobie nie jest zwi&#261;zany z &lt;span class="caps"&gt;HTML&lt;/span&gt; tak jak &lt;span class="caps"&gt;PHP&lt;/span&gt;. Rails do szablon&#243;w u&#380;ywa biblioteki &lt;a href="http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/"&gt;&lt;strong&gt;ERb&lt;/strong&gt;&lt;/a&gt; (ang. &amp;#8220;embed Ruby&amp;#8221;,  czyli &amp;#8220;zagnie&#380;d&#380;ony Ruby&amp;#8221;) Sk&#322;adnia jest bardzo podobna do &lt;span class="caps"&gt;PHP&lt;/span&gt;, &lt;a href="http://java.sun.com/products/jsp/"&gt;&lt;span class="caps"&gt;JSP&lt;/span&gt;&lt;/a&gt; czy &lt;span class="caps"&gt;ASP&lt;/span&gt;. Ruby jest tu j&#281;zykiem zagnie&#380;d&#380;onym w &lt;span class="caps"&gt;HTML&lt;/span&gt;.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="punct"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="string"&gt; x %&amp;gt; jest 
&amp;lt;% if x &amp;gt; y %&amp;gt; 
    wi&#281;ksze 
&amp;lt;% else %&amp;gt;
    mniejsze 
&amp;lt;% end %&amp;gt;
od &amp;lt;%&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;y&lt;/span&gt; &lt;span class="punct"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h2&gt;Python&lt;/h2&gt;


	&lt;p&gt;Je&#347;li chodzi o Pythona to istnieje &lt;a href="http://wiki.python.org/moin/Templating"&gt;ca&#322;a masa system&#243;w szablonowych&lt;/a&gt;. Ogranicz&#281; si&#281; do paru, tych bardziej interesuj&#261;cych.&lt;/p&gt;


	&lt;h3&gt;Spyce&lt;/h3&gt;


	&lt;p&gt;Szablony &lt;a href="http://spyce.sourceforge.net/"&gt;Spyce&lt;/a&gt; zosta&#322;o stworzone jako pythonowa konkurencja dla &lt;span class="caps"&gt;PHP&lt;/span&gt;. Podobnie potrafi&#261; przeplata&#263; j&#281;zyk (tu: Pythona) z tagami &lt;span class="caps"&gt;HTML&lt;/span&gt;. Najbardziej oczywista przewaga Spyce wynika z faktu u&#380;ywania j&#281;zyka &lt;a href="http://python.org"&gt;Python&lt;/a&gt;, kt&#243;ry jest znacznie lepiej zaprojektowany i posiada du&#380;o wi&#281;cej mo&#380;liwo&#347;ci ni&#380; &lt;span class="caps"&gt;PHP&lt;/span&gt;. Spyce potrafii budowa&#263; w&#322;asne znaczniki (custom tags, podobnie jak to potrafi&#261; javowe &lt;span class="caps"&gt;JSP&lt;/span&gt;) oraz &#322;atwiej w nich zbudowa&#263; komponenty, kod  do wielokrotnego u&#380;ytku.&lt;/p&gt;


	&lt;h3&gt;Myghty&lt;/h3&gt;


	&lt;p&gt;Szablony &lt;a href="http://myghty.org"&gt;Myghty&lt;/a&gt; powsta&#322;y pierwotnie jako pythonowa implementacja systemu komponentowego &lt;a href="http://masonhq.com"&gt;Mason&lt;/a&gt; dla &lt;a href="http://www.perl.org/"&gt;Perla&lt;/a&gt;. Mimo wielu podobie&#324;stw, Myghty s&#261; znacznie pot&#281;&#380;niejsze o Masona, np. intensywnie wykorzystuj&#261; &lt;strong&gt;obiektowo&#347;&#263;&lt;/strong&gt; Pythona (zobacz &lt;a href="http://www.myghty.org/docs/technical.myt#technical_differences"&gt;r&#243;&#380;nice&lt;/a&gt;). Od pocz&#261;tku Myghty powsta&#322;o z my&#347;l&#261; o du&#380;ych obci&#261;&#380;eniach, ci&#281;&#380;kich, profesjonalnych zastosowaniach. Maj&#261; doskona&#322;y w&#322;asny cache, s&#261; bardzo szybkie, odporne na w&#261;tki, &#347;wietnie si&#281; nadaj&#261; do tworzenia komponent&#243;w.&lt;/p&gt;


	&lt;p&gt;Programi&#347;ci Pythona bardzo ceni&#261; sobie filozofi&#281; tego j&#281;zyka (kt&#243;ra podkre&#347;la prostot&#281;, oczywisto&#347;&#263; i elegancj&#281; sk&#322;adni).  Problem w tym, &#380;e Myghty dla pythonowc&#243;w jest zbyt podobne do Perla. S&#261; za ma&#322;o &amp;#8220;pythonic&amp;#8221; &amp;#8211; pythonowe. Dodatkowo, poprzez podkre&#347;lanie swojej komponentowo&#347;ci, Myghty niekt&#243;rym zbyt kojarzy si&#281; z filozofi&#261; komponentow&#261; frameworku &lt;span class="caps"&gt;ASP&lt;/span&gt;.NET (a wzorzec &lt;span class="caps"&gt;MVC&lt;/span&gt; wydaje si&#281; by&#263; bardziej przejrzysty ni&#380; wzorzec sterowanych zdarzeniami komponent&#243;w).  Myghty, jako system komponentowy, mo&#380;e by&#263; u&#380;ywane w stylu &lt;span class="caps"&gt;MVC&lt;/span&gt;, ale nie jest to takie naturalne i oczywiste. (Na szcz&#281;&#347;cie, wszystko zmieni&#322; &lt;a href="http://pylonshq.com"&gt;Pylons&lt;/a&gt; kt&#243;ry zapewnia przejrzyst&#261; prac&#281; zgodn&#261; z wzorcem &lt;span class="caps"&gt;MVC&lt;/span&gt; i Myghty s&#261; tam zredukowane tylko do obs&#322;ugi szablon&#243;w szablon&#243;w. Sterowaniem zajmuj&#261; si&#281; kontrolery Pylonsa a nie Myghty).&lt;/p&gt;


	&lt;h3&gt;Cheetah&lt;/h3&gt;


	&lt;p&gt;Szablony &lt;a href="http://cheetahtemplate.org"&gt;Cheetah&lt;/a&gt; posiadaj&#261; jedn&#261; z najbardziej czytelnych i eleganckich sk&#322;adni. Autorzy wzorowali si&#281; troch&#281; na javowych Velocity i Webmacro. Tym, kt&#243;rzy ceni&#261; sobie czysto&#347;&#263; Pythona z prostot&#261; sk&#322;adni szablon&#243;w z pewno&#347;ci&#261; Cheetah mog&#261; przypa&#347;&#263; do gustu. Przez d&#322;u&#380;szy czas by&#322;y moimi ulubionymi szablonami i do te pory lubi&#281; ich klarowno&#347;&#263;. Cheetah posiadaj&#261; eleganck&#261; sk&#322;adni&#281; obiektowego budowania szablon&#243;w potomnych na bazie ju&#380; istniej&#261;cych. Myghty te&#380; to potrafi, ale Cheetah to ma bardzo czytelnie zrobione. Cheetah s&#261; r&#243;wnie&#380; bardzo szybkie.&lt;/p&gt;


	&lt;h3&gt;Mako&lt;/h3&gt;


	&lt;p&gt;&lt;a href="http://makotemplates.org"&gt;Mako&lt;/a&gt; to najm&#322;odsze dziecko stworzone przez programist&#243;w &lt;a href="http://pylonshq.com"&gt;Pylons&#243;w&lt;/a&gt;. Powsta&#322;y na bazie do&#347;wiadcze&#324; i najlepszych cech szablon&#243;w Myghty, Cheetah, Django/Jinja czy Smarty. Najwi&#281;ksz&#261; ich zalet&#261; jest &lt;strong&gt;ogromna szybko&#347;&#263; i pythonowa filozofia pracy&lt;/strong&gt;. Programi&#347;ci Pythona mog&#261; zauwa&#380;y&#263;, &#380;e sk&#322;adnia Mako jest bardzo bliska sposobowi pracy w samym j&#281;zyku Python. Mako posiadaj&#261; wielk&#261; &#322;atwo&#347;&#263; tworzenia komponent&#243;w, ale w spos&#243;b bardzo podobny do tego jak Python wykorzystuje swoje modu&#322;y. Szablony mo&#380;na tak&#380;e rozszerza&#263; na drodze obiektowego dziedziczenia. Dziedziczenie szablon&#243;w mo&#380;e by&#263; okre&#347;lane w spos&#243;b dynamiczny (tego ju&#380; nie potrafi&#261; np. Cheetah). Dowolny fragment szablonu mo&#380;na r&#243;wnie&#380; zamieni&#263; na komponent z w&#322;asnym cache. Mako posiadaj&#261; tak&#380;e sk&#322;adni&#281; modyfikator&#243;w, kt&#243;re spopularyzowane zosta&#322;y przez Smarty. Dzia&#322;aj&#261; one jak uniksowe pipes (${zmienna|filter}.&lt;/p&gt;


	&lt;p&gt;Mako automatycznie kompiluj&#261; sw&#243;j kod do modu&#322;&#243;w Pythona. Mo&#380;na je wi&#281;c debugowa&#263; tak jak zwyk&#322;y kod Pythona (Cheeatah wymaga r&#281;cznej kompilacji). Mo&#380;na je bez problemu u&#380;ywa&#263; jako niezale&#380;n&#261; bibliotek&#281;. Nie s&#261; one zale&#380;ne od jakiegokolwiek frameworku. Mo&#380;na je zintegrowa&#263; z dowolnym frameworkiem u&#380;ywaj&#261;cym standardu&lt;sup&gt;&lt;a href="#fn3"&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;span class="caps"&gt;WSGI&lt;/span&gt;. U&#380;ytkownicy frameworka &lt;a href="http://pylonshq.com"&gt;Pylons&lt;/a&gt; prawie nic nie musz&#261; robi&#263;, bo Mako s&#261; ju&#380; tam przygotowane do pracy.&lt;/p&gt;


	&lt;p&gt;Og&#243;lnie rzecz bior&#261;c, &lt;strong&gt;Mako&lt;/strong&gt; to &amp;#8211; moim zdaniem &amp;#8211; najlepszy aktualnie istniej&#261;cy system szablon&#243;w dla Pythona.&lt;/p&gt;


	&lt;p&gt;Za&#347; co do framework&#243;w&amp;#8230; to coraz bardziej zaczynam nabiera&#263; pewno&#347;ci, &#380;e przysz&#322;o&#347;&#263; nie nale&#380;y ani do Django, ani do Rails&#243;w. Nic nie mo&#380;e r&#243;wna&#263; si&#281; z mo&#380;liwo&#347;ciami czystego, 100%  frameworka &lt;span class="caps"&gt;WSGI&lt;/span&gt;. Takim jest np. &lt;a href="http://pylonshq.com"&gt;Pylons&lt;/a&gt;. Ale o tym, i o &amp;#8220;bitwie&amp;#8221; Pylons vs. Django i Rails, napisz&#281; ju&#380; innym razem.&lt;/p&gt;


	&lt;p&gt;Appendix 2007-01-29: Zobacz te&#380; &lt;a href="http://blog.zabiello.com/articles/2007/01/27/haml-nast%C4%99pna-generacja-szablon%C3%B3w"&gt;szablony Haml&lt;/a&gt;
.&lt;/p&gt;


&lt;hr /&gt;

	&lt;p id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; Poprawnie rzecz m&#243;wi&#261;c, powinien napisa&#263; swojego helpera. Je&#347;li wi&#281;c chcemy wy&#347;wietli&#263; w p&#281;tli rekordy z bazy, a widok jest czu&#322;y na zawarto&#347;&#263; tych danych, to powinni&#347;my ca&#322;a list&#281; rekord&#243;w wrzuci&#263; do helplera, przeiterowa&#263; w p&#281;tli, doda&#263; dodatkow&#261; zmienn&#261; i dopiero tak zmodyfikowan&#261; list&#281; przekaza&#263; do szablonu.&lt;/p&gt;


	&lt;p id="fn2"&gt;&lt;sup&gt;2&lt;/sup&gt; Smarty mimo posiadania w&#322;asnej sk&#322;adni pozwala na odpalanie pe&#322;nego j&#281;zyka &lt;span class="caps"&gt;PHP&lt;/span&gt;: {php}print phpinfo();{/php}. Tak praktyka jest jednak odradzana, bo wprowadza jeszcze wi&#281;kszy ba&#322;agan ni&#380; ju&#380; jest w &lt;span class="caps"&gt;PHP&lt;/span&gt;.&lt;/p&gt;


	&lt;p id="fn3"&gt;&lt;sup&gt;3&lt;/sup&gt; Zobacz artyku&#322; &lt;a href="http://www.xml.com/pub/a/2006/09/27/introducing-wsgi-pythons-secret-web-weapon.html"&gt;Introducing &lt;span class="caps"&gt;WSGI&lt;/span&gt;: Python&amp;#8217;s Secret Web Weapon&lt;/a&gt; oraz &lt;a href="http://www.groovie.org/articles/2006/09/18/wsgi-paste-pylons-and-all-that"&gt;prezentacj&#281; koncepcji &lt;span class="caps"&gt;WSGI&lt;/span&gt;&lt;/a&gt; na video.&lt;/p&gt;</description>
      <pubDate>Sun, 14 Jan 2007 04:25:00 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:85bc8794-3490-4af9-9c34-8cd949244305</guid>
      <author>Jaros&#322;aw Zabie&#322;&#322;o</author>
      <link>http://blog.zabiello.com/articles/2007/01/14/templates-and-mvc-3</link>
      <category>templates</category>
      <category>savant</category>
      <category>spyce</category>
      <category>myghty</category>
      <category>cheetah</category>
      <category>mako</category>
      <category>django</category>
      <category>jinja</category>
      <category>pylons</category>
      <category>rails</category>
      <category>python</category>
      <category>ruby</category>
      <category>php</category>
      <category>mvc</category>
    </item>
    <item>
      <title>Pylons + Mako</title>
      <description>&lt;p&gt;Mako &amp;#8211; nowy, wydajny system szablon&#243;w dla Pylons&#243;w jest ju&#380; dost&#281;pny publicznie na stronie &lt;a href="http://makotemplates.org"&gt;http://makotemplates.org&lt;/a&gt;. Mo&#380;na go pobra&#263; za pomoc&#261; setuptools&#243;w, czyli skryptem: &lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;easy_install&lt;/span&gt; &lt;span class="constant"&gt;Mako&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;</description>
      <pubDate>Tue, 02 Jan 2007 04:46:00 +0100</pubDate>
      <guid isPermaLink="false">urn:uuid:eee84405-3f35-4b43-99f8-6cd160fc9822</guid>
      <author>Jaros&#322;aw Zabie&#322;&#322;o</author>
      <link>http://blog.zabiello.com/articles/2007/01/02/pylons-mako</link>
      <category>Pylons</category>
      <category>pylons</category>
      <category>mako</category>
    </item>
    <item>
      <title>Pylons 0.9.4</title>
      <description>&lt;p&gt;Wysz&#322;a nowa wersja pythonowego megaframeworka (korzysta z zewn., ju&#380; istniej&#261;cych bibliotek) do szybkiego budowania aplikacji webowych &amp;#8211; &lt;a href="http://pylonshq.com"&gt;Pylons&lt;/a&gt;. W wersji 0.9.4 &lt;a href="http://groups.google.com/group/pylons-discuss/browse_thread/thread/1f05fee97b1e5217/2147ea572dc235b5?hl=en#2147ea572dc235b5"&gt;dodano troch&#281; poprawek i ulepsze&#324;&lt;/a&gt;. M.in. jest nowy &lt;a href="http://routes.groovie.org/"&gt;Routes&lt;/a&gt; 1.6.1, ulepszono introspekcj&#281; dla &lt;span class="caps"&gt;XML&lt;/span&gt;-RPC,Akcje kontroler&#243;w s&#261; teraz generatorami a helpery oraz metoda _ (z gettext) generuj&#261; teraz obiekty Unicodowe zamiast string&#243;w &lt;span class="caps"&gt;UTF&lt;/span&gt;-8. Znowu Pylons znacznie wyprzedza, to co dopiero jest w powijakach dla &lt;a href="http://djangoproject.com"&gt;Django&lt;/a&gt;.&lt;/p&gt;


	&lt;h3&gt;SQLAlchemy&lt;/h3&gt;


	&lt;p&gt;Coraz bardziej zaznacza si&#281; tendencja w u&#380;ywanych pod Pythonem &lt;span class="caps"&gt;ORM&lt;/span&gt;&amp;#8217;ach (Pylons mo&#380;e korzysta&#263; z dowolnych) aby zamiast &lt;a href="http://www.sqlobject.org/"&gt;SQLObject&lt;/a&gt; wybiera&#263; raczej &lt;a href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt;. Ten drugi jest uwa&#380;any za znacznie bardziej wyrafinowany i o wi&#281;kszych mo&#380;liwo&#347;ciach. Na li&#347;cie dyskusyjnej Pylons&#243;w Ben Bangert pokaza&#322; &lt;a href="http://groups.google.com/group/pylons-discuss/browse_thread/thread/1f05fee97b1e5217/2147ea572dc235b5?hl=en#2147ea572dc235b5"&gt;jak w praktyce mo&#380;na u&#380;ywa&#263; SQLAlchemy&lt;/a&gt;.&lt;/p&gt;


	&lt;h3&gt;Szablony&lt;/h3&gt;


	&lt;p&gt;Pylons potrafi korzysta&#263; z dowolnych system&#243;w szablon&#243;w dost&#281;pnych dla 
Pythona. Generalnie, popularne s&#261; dwa podej&#347;cia. Szablony pisane pod propgramist&#243;w lub pod webmaster&#243;w/designer&#243;w, kt&#243;rzy zajmuj&#261; si&#281; sam&#261; kwesti&#261; prezentacji, wizualizacji i pracuj&#261; najcz&#281;&#347;ciej ze &amp;#8220;wspomagaczami&amp;#8221; takimi jak edytor Dreamweaver.&lt;/p&gt;


	&lt;p&gt;Dla tych drugich, polecane jest podej&#347;cie albo takie jak promuje Django, czyli &lt;a href="http://www.djangoproject.com/documentation/templates/"&gt;specjalnie uproszczony j&#281;zyk szablon&#243;w&lt;/a&gt; umo&#380;liwiaj&#261;cym generowanie zar&#243;no &lt;span class="caps"&gt;HTML&lt;/span&gt; jak i &lt;span class="caps"&gt;CSS&lt;/span&gt;, czy innej dowolnej tre&#347;ci.&lt;/p&gt;


	&lt;p&gt;Albo podej&#347;cie jeszcze bardziej uproszczone (dla designer&#243;w, nie dla programist&#243;w), czyli koncepcja, jak&#261; promuj&#261; tw&#243;rcy frameworka &lt;a href="http://zope.org"&gt;Zope&lt;/a&gt;. Chodzi o szablony &lt;a href="http://www.zope.org/Documentation/ZopeBook/ZPT.stx"&gt;&lt;span class="caps"&gt;ZPT&lt;/span&gt;&lt;/a&gt;, &lt;a href="http://kid-templating.org/"&gt;Kid&lt;/a&gt; czy &lt;a href="http://genshi.edgewall.org/"&gt;Genshi&lt;/a&gt; gdzie logika szablon&#243;w jest dodana do atrybut&#243;w. Dzi&#281;ki temu s&#261; one przezroczyste dla edytor&#243;w takich jak Dreamweaver i je&#347;li wymagana jest cz&#281;sta konsultacja z designerami to takie podej&#347;cie oszcz&#281;dza ponownego, r&#281;cznego dodawania logiki dla programist&#243;w.&lt;/p&gt;


	&lt;h3&gt;Nadchodzi Mako&lt;/h3&gt;


	&lt;p&gt;Moim zdaniem, znacznie lepszym podej&#347;ciem jest podej&#347;cie jakie oferuj&#261; szablony &lt;a href="http://www.cheetahtemplate.org/"&gt;Cheetah&lt;/a&gt; czy &lt;a href="http://myghty.org"&gt;Myghty&lt;/a&gt;. Te drugie, s&#261; aktualnie najpote&#380;niejsze. Maj&#261; nie tylko du&#380;a szybko&#347;&#263;, ale bardzo dobrze dzia&#322;aj&#261;cy (lepszy od Cheetah) cache oraz &#322;atwo&#347;&#263; do tworzenia komponent&#243;w, klock&#243;w wielokrotnego u&#380;ycia.&lt;/p&gt;


	&lt;p&gt;Nadchodzi jednak co&#347; jeszcze lepszego. Developerzy Pylons&#243;w szykuj&#261; zupe&#322;nie nowy system szablon&#243;w zwany Mako (&lt;a href="http://makotemplates.org"&gt;http://makotemplates.org&lt;/a&gt;). Jest on wzorowany na najlepszych funkcjach jakie oferuj&#261; Myghty, Cheetah, Genshi i Django. Jest te&#380; bardzo szybki.&lt;/p&gt;


	&lt;p&gt;Mako oferuj&#261; wielostrefowe dziedziczenie (podobnie jak Cheetah i Django). Nie jest te&#380; wymagane tak jak w Myghty, aby wstawki Pythona (zaczynaj&#261;ce si&#281; w szablonach Myghty od znaku procenta) zaczyna&#322;y wiersz. Mo&#380;na wstawia&#263; je w &#347;rodku tekstu lub gdziekolwiek w ramach tag&#243;w &amp;lt;% kod Pythona %&amp;gt;.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="punct"&gt;&amp;lt;%&lt;/span&gt;&lt;span