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

<channel>
	<title>PunNeng, polyglot developer</title>
	<atom:link href="http://weblog.punneng.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://weblog.punneng.net</link>
	<description>Hacking, playing and shooting</description>
	<lastBuildDate>Wed, 23 Jan 2013 19:11:29 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.2</generator>
		<item>
		<title>Hello Pyramid [Part 2] – Test First</title>
		<link>http://weblog.punneng.net/2013/01/hello-pyramid-part-2-test-first/</link>
		<comments>http://weblog.punneng.net/2013/01/hello-pyramid-part-2-test-first/#comments</comments>
		<pubDate>Thu, 17 Jan 2013 19:20:22 +0000</pubDate>
		<dc:creator>PunNeng</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[pyramid]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[sqlalchemy]]></category>
		<category><![CDATA[TDD]]></category>
		<category><![CDATA[unittest]]></category>

		<guid isPermaLink="false">http://weblog.punneng.net/?p=99</guid>
		<description><![CDATA[Post ก่อนเรามี test แล้ว ตอนนี้เราต้อง implement code เราให้เข้ากับ test นั่น Model พระเอกของเราจริงๆ ที่น่าจะพูดถึงก่อนน่าจะเป็น SQLAlchemy นอกจากการบันทึกที่มีรายละเอียดนิดหน่อยแล้ว ยังมีเรือ่ง transaction เข้ามาเกี่ยวด้วย คือมันเหมือนจะไม่มีอะไร แต่ถ้าเป็น code template ที่ generate มาจาก tool ของ Pyramid มันจะมี transaction ที่แปลกออกไป เริ่มที่การประกาศ fields ก่อน Base = declarative_base&#40;&#41; class Todo&#40;Base&#41;: __tablename__ = 'todos' id = Column&#40;Integer, primary_key=True&#41; task = Column&#40;String&#40;512&#41;, nullable=False&#41; created_at = Column&#40;DateTime, server_default=text&#40;'NOW()'&#41;, nullable=False&#41; [...]]]></description>
			<content:encoded><![CDATA[<p><a title="Hello Pyramid [Part 1] – Test First" href="http://weblog.punneng.net/2012/12/hello-pyramid-part-1-test-first/" target="_blank">Post ก่อน</a>เรามี test แล้ว ตอนนี้เราต้อง implement code เราให้เข้ากับ test นั่น</p>
<h4>Model</h4>
<p>พระเอกของเราจริงๆ ที่น่าจะพูดถึงก่อนน่าจะเป็น SQLAlchemy นอกจากการบันทึกที่มีรายละเอียดนิดหน่อยแล้ว ยังมีเรือ่ง transaction เข้ามาเกี่ยวด้วย คือมันเหมือนจะไม่มีอะไร แต่ถ้าเป็น code template ที่ generate มาจาก tool ของ Pyramid มันจะมี transaction ที่แปลกออกไป</p>
<p>เริ่มที่การประกาศ fields ก่อน</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">Base = declarative_base<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">class</span> Todo<span style="color: black;">&#40;</span>Base<span style="color: black;">&#41;</span>:
    __tablename__ = <span style="color: #483d8b;">'todos'</span>
    <span style="color: #008000;">id</span> = Column<span style="color: black;">&#40;</span>Integer, primary_key=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    task = Column<span style="color: black;">&#40;</span>String<span style="color: black;">&#40;</span><span style="color: #ff4500;">512</span><span style="color: black;">&#41;</span>, nullable=<span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
    created_at = Column<span style="color: black;">&#40;</span>DateTime, server_default=text<span style="color: black;">&#40;</span><span style="color: #483d8b;">'NOW()'</span><span style="color: black;">&#41;</span>, nullable=<span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
    done_at = Column<span style="color: black;">&#40;</span>DateTime<span style="color: black;">&#41;</span>
    priority = Column<span style="color: black;">&#40;</span>Integer, default=<span style="color: #ff4500;">5</span><span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># 1 =&amp;gt; the most priority, 10 =&amp;gt; not important now </span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, task, done_at=<span style="color: #008000;">None</span>, priority=<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>Todo, <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">task</span> = task
        <span style="color: #008000;">self</span>.<span style="color: black;">done_at</span> = done_at
        <span style="color: #008000;">self</span>.<span style="color: black;">priority</span> = priority</pre></div></div>

<p>ก็ง่ายๆ มี fields บอกรายละเอียด บันทึกเวลาตอนสร้าง เสร็จตอนไหน แล้วก็ระดับความสำคัญของ todo</p>
<p>อีกหนึ่งความมึนคือ transaction</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">DBSession = scoped_session<span style="color: black;">&#40;</span>sessionmaker<span style="color: black;">&#40;</span>extension=ZopeTransactionExtension<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>code บรรทัดบน จะทำการสร้างการเชื่อมต่อสำหรับฐานข้อมูลซึ่งส่ง ZopeTransactionExtension ทำหน้าที่ commit transaction หลังจากที่ process งานตาม request ที่มีมา ถ้ามีการ raise เกิดขึ้น จะมีการ rollback สิ่งที่เราสั่งไป</p>
<p>ปัญหาคือ ถ้าเราทำตามตัวอย่างในเว็บมันเนี่ย มันจะให้เราสร้าง transaction เองมาใหม่ด้วย transaction.manager พอจบ scope ของ transaction.manager มันจะ commit ทีนี้ ตอนเรา tear down เราจะไม่มีอะไรให้ rollback ที่ผมต้องเพิ่ม <a href="http://docs.sqlalchemy.org/en/latest/orm/session.html#sqlalchemy.orm.scoping.scoped_session.remove" target="_blank">DBSession.remove()</a> มาก็เพื่อจะสั่งลบข้อมูลหลังจาก test เสร็จ ซึ่งตัวอย่างใน <a href="http://pyramid.readthedocs.org/en/latest/tutorials/wiki2/tests.html" target="_blank">doc</a> มันไม่ต้องเพราะมัน test ด้วย sqlite ที่เป็นแค่ฐานข้อมูลในหน่วยความจำชั่วคราว จบการ test เสร็จมันก็เคลียร์ไปเลย</p>
<p>ทีนี้ มันมีกระบวณท่าเพิ่มเข้ามาอีกเพื่อทำให้การ test ราบรื่น ต้องมาเริ่มที่ ORM method ของ SQLAlchemy ไม่ว่าจะสั่งให้เพิ่มหรือลบอะไรผ่าน session ของ SQLAlchemy มันจะเหมือนเราสั่งเก็บไว้ใน stack ก่อน แล้วค่อย execute ทีเดียว แต่สิ่งที่ผมคาดหวังไว้ คือ หลังจาก save แล้ว ผมควรจะได้ผลการ save ณ ตอนนั้นเลย วิธีการง่ายๆ คือใช้คำสั่ง</p>

<div class="wp_syntax"><div class="code"><pre class="pyrhon" style="font-family:monospace;">DBSession.flush()</pre></div></div>

<p>พอ flush แล้ว todo instance ก็จะมีการเซ็ตค่าหลังจากมีการบันทึก เช่น ถ้าหาก new todo ยังไม่มี id จะถือว่าเป็น create พอ flush แล้ว instance นี้จะมี id กำกับตาม id ในฐานข้อมูล หากไม่ flush ก็ไม่มี id ก็จะไม่รู้เลยว่าบันทึกผ่านหรือเปล่า ต้องมาคอยจับ exception เอา ซึ่งตอนนี้ยังไม่รู้เลยว่าจะจับยังไง เพราะมันจะ execute ตามที่อธิบายไปก่อนหน้านี้และอยู่นอก scope ของ code ด้วย</p>
<p>กลับมาเรื่อง code ต่อ มันคงไม่สนุก ถ้าหากจะต้องมา flush ทุกครั้งหลังจากการบันทีกหรือลบ ผมเลยห่อมันไว้เป็น class แม่ซะ พร้อมทั้งเพิ่มส่วนของ validation helper ไปด้วย ที่ SQLAlchemy ไม่มี</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> AppBase<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    _errors = <span style="color: #008000;">None</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> save<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>.<span style="color: black;">is_valid</span>:
            <span style="color: #ff7700;font-weight:bold;">try</span>:
                DBSession.<span style="color: black;">add</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
                DBSession.<span style="color: black;">flush</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
                <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
            <span style="color: #ff7700;font-weight:bold;">except</span> IntegrityError:
                <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> update<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>.<span style="color: black;">is_valid</span>:
            <span style="color: #ff7700;font-weight:bold;">try</span>:
                DBSession.<span style="color: black;">merge</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
                DBSession.<span style="color: black;">flush</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
                <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
            <span style="color: #ff7700;font-weight:bold;">except</span> IntegrityError:
                <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> delete<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">try</span>:
            DBSession.<span style="color: black;">delete</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
            DBSession.<span style="color: black;">flush</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">except</span> IntegrityError: <span style="color: #808080; font-style: italic;"># still dont have an idea to test this failure</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span></pre></div></div>

<p>และส่วนของ validation ที่เพิ่มเข้ามาเอง</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">    @<span style="color: #008000;">property</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> errors<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>._errors
&nbsp;
    @<span style="color: #008000;">property</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> is_valid<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">bool</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>._errors<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> validate<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, validator, key, value<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">self</span>._errors:
            <span style="color: #008000;">self</span>._errors = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
        <span style="color: #ff7700;font-weight:bold;">try</span>:
            validator.<span style="color: black;">to_python</span><span style="color: black;">&#40;</span>value<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">except</span> Invalid <span style="color: #ff7700;font-weight:bold;">as</span> e:
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">self</span>._errors.<span style="color: black;">get</span><span style="color: black;">&#40;</span>key<span style="color: black;">&#41;</span>:
                <span style="color: #008000;">self</span>._errors<span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span> = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
            <span style="color: #008000;">self</span>._errors<span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #008000;">str</span><span style="color: black;">&#40;</span>e<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>SQLAlchemy มี hook ไว้ที่นึงชื่อ validates เป้น decorator จะถูกเรียกตอนที่ assign ค่า</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">    @validates<span style="color: black;">&#40;</span><span style="color: #483d8b;">'task'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> validate_task<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, key, value<span style="color: black;">&#41;</span>:
        <span style="color: #808080; font-style: italic;"># validate</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> value</pre></div></div>

<p>โดย task ก็คือ field ที่เราระบุไว้ใน Todo class โดย method นี้จะอยู่ใน Todo class เช่นกัน โดยเชื่อมระหว่าง class แม่นี้ กับ Todo ด้วย</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">Base = declarative_base<span style="color: black;">&#40;</span>cls=AppBase<span style="color: black;">&#41;</span></pre></div></div>

<p>และ code ใน models.py ก็จะมีหน้าตาเป็นแบบนี้</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> sqlalchemy <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: black;">&#40;</span>
    Column,
    Integer,
    Text,
    String,
    DateTime,
    Integer
    <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> sqlalchemy.<span style="color: black;">ext</span>.<span style="color: black;">declarative</span> <span style="color: #ff7700;font-weight:bold;">import</span> declarative_base
<span style="color: #ff7700;font-weight:bold;">from</span> sqlalchemy.<span style="color: black;">sql</span>.<span style="color: black;">expression</span> <span style="color: #ff7700;font-weight:bold;">import</span> text
<span style="color: #ff7700;font-weight:bold;">from</span> sqlalchemy.<span style="color: black;">exc</span> <span style="color: #ff7700;font-weight:bold;">import</span> IntegrityError
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> sqlalchemy.<span style="color: black;">orm</span> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: black;">&#40;</span>
    scoped_session,
    sessionmaker,
    validates,
    <span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> zope.<span style="color: black;">sqlalchemy</span> <span style="color: #ff7700;font-weight:bold;">import</span> ZopeTransactionExtension
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> formencode <span style="color: #ff7700;font-weight:bold;">import</span> validators, Invalid
&nbsp;
DBSession = scoped_session<span style="color: black;">&#40;</span>sessionmaker<span style="color: black;">&#40;</span>extension=ZopeTransactionExtension<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;">#DBSession = scoped_session(sessionmaker())</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> AppBase<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    _errors = <span style="color: #008000;">None</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> save<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>.<span style="color: black;">is_valid</span>:
            <span style="color: #ff7700;font-weight:bold;">try</span>:
                DBSession.<span style="color: black;">add</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
                DBSession.<span style="color: black;">flush</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
                <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
            <span style="color: #ff7700;font-weight:bold;">except</span> IntegrityError:
                <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> update<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>.<span style="color: black;">is_valid</span>:
            <span style="color: #ff7700;font-weight:bold;">try</span>:
                DBSession.<span style="color: black;">merge</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
                DBSession.<span style="color: black;">flush</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
                <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
            <span style="color: #ff7700;font-weight:bold;">except</span> IntegrityError:
                <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> delete<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">try</span>:
            DBSession.<span style="color: black;">delete</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
            DBSession.<span style="color: black;">flush</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">except</span> IntegrityError: <span style="color: #808080; font-style: italic;"># still dont have an idea to test this failure</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
&nbsp;
    @<span style="color: #008000;">property</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> errors<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>._errors
&nbsp;
    @<span style="color: #008000;">property</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> is_valid<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">bool</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>._errors<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> validate<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, validator, key, value<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">self</span>._errors:
            <span style="color: #008000;">self</span>._errors = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
        <span style="color: #ff7700;font-weight:bold;">try</span>:
            validator.<span style="color: black;">to_python</span><span style="color: black;">&#40;</span>value<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">except</span> Invalid <span style="color: #ff7700;font-weight:bold;">as</span> e:
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">self</span>._errors.<span style="color: black;">get</span><span style="color: black;">&#40;</span>key<span style="color: black;">&#41;</span>:
                <span style="color: #008000;">self</span>._errors<span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span> = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
            <span style="color: #008000;">self</span>._errors<span style="color: black;">&#91;</span>key<span style="color: black;">&#93;</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #008000;">str</span><span style="color: black;">&#40;</span>e<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
Base = declarative_base<span style="color: black;">&#40;</span>cls=AppBase<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> Todo<span style="color: black;">&#40;</span>Base<span style="color: black;">&#41;</span>:
    __tablename__ = <span style="color: #483d8b;">'todos'</span>
    <span style="color: #008000;">id</span> = Column<span style="color: black;">&#40;</span>Integer, primary_key=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    task = Column<span style="color: black;">&#40;</span>String<span style="color: black;">&#40;</span><span style="color: #ff4500;">512</span><span style="color: black;">&#41;</span>, nullable=<span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
    created_at = Column<span style="color: black;">&#40;</span>DateTime, server_default=text<span style="color: black;">&#40;</span><span style="color: #483d8b;">'NOW()'</span><span style="color: black;">&#41;</span>, nullable=<span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
    done_at = Column<span style="color: black;">&#40;</span>DateTime<span style="color: black;">&#41;</span>
    priority = Column<span style="color: black;">&#40;</span>Integer, default=<span style="color: #ff4500;">5</span><span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># 1 =&amp;gt; the most priority, 10 =&amp;gt; not important now </span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, task, done_at=<span style="color: #008000;">None</span>, priority=<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>Todo, <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">task</span> = task
        <span style="color: #008000;">self</span>.<span style="color: black;">done_at</span> = done_at
        <span style="color: #008000;">self</span>.<span style="color: black;">priority</span> = priority
&nbsp;
    @validates<span style="color: black;">&#40;</span><span style="color: #483d8b;">'task'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> validate_task<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, key, value<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">validate</span><span style="color: black;">&#40;</span>validators.<span style="color: black;">String</span><span style="color: black;">&#40;</span>not_empty=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>, key, value<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> value
&nbsp;
    @validates<span style="color: black;">&#40;</span><span style="color: #483d8b;">'priority'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> validate_priority<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, key, value<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">validate</span><span style="color: black;">&#40;</span>validators.<span style="color: black;">Int</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>, key, value<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> value</pre></div></div>

<p>โดยตัว validation ที่ผมเลือกเข้ามาช่วยคือ <a href="http://www.formencode.org/en/latest/" target="_blank">formencode</a> ที่ validate ได้ค่อนข้างครอบคลุม อย่างในตัวอย่าง ผมต้องการแค่ validators.String(not_empty=True) คือ ห้ามเป็นค่าว่าง กับ validators.Int() คือต้องเป็น Int เท่านั้น</p>
<h4>Controller</h4>
<p>Framework ฝั่ง Python เนี่ย มักจะใช้คำว่า views แทน controllers ซึ่งผมไม่ค่อยชอบเท่าไหร่ มันแปลกๆ กับ MVC เลยเปลีย่นเป็น controller(s) ซะเลย โดยสร้าง package เป็น controllers แล้วแยกเป็น file ไป โดยผมตั้งชื่อให้เป็น todos.py ตามใน test ที่มี</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> todolist.<span style="color: black;">controllers</span>.<span style="color: black;">todos</span> <span style="color: #ff7700;font-weight:bold;">import</span> create</pre></div></div>

<p>ใส่ไว้</p>
<p>มีท่ายากมานิดหน่อยสำหรับ template ที่จะใช้ เนื่องจาก template engine ตัว builtin มันเลยคือ <a href="http://bugs.repoze.org/issue139" target="_blank">ZPT(Chameleon)</a> แต่มันไปติด bug นี้ ปัญหาคือ ใช้ macro แล้วใส่ doctype ไม่ได้ ผมเลยเปลี่ยนมาใช้ mako แทน</p>
<h5>Read</h5>
<p>ในส่วนนี้จะเป็นหน้า list ของ todo</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> get_todo_set<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    todos = DBSession.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Todo<span style="color: black;">&#41;</span>.<span style="color: #008000;">filter</span><span style="color: black;">&#40;</span>Todo.<span style="color: black;">done_at</span>==<span style="color: #008000;">None</span><span style="color: black;">&#41;</span>.<span style="color: black;">order_by</span><span style="color: black;">&#40;</span>Todo.<span style="color: black;">priority</span>.<span style="color: black;">desc</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    done_todos = DBSession.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Todo<span style="color: black;">&#41;</span>.<span style="color: #008000;">filter</span><span style="color: black;">&#40;</span>Todo.<span style="color: black;">done_at</span><span style="color: #66cc66;">!</span>=<span style="color: #008000;">None</span><span style="color: black;">&#41;</span>.<span style="color: black;">order_by</span><span style="color: black;">&#40;</span>Todo.<span style="color: black;">priority</span>.<span style="color: black;">desc</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> todos, done_todos
&nbsp;
@view_config<span style="color: black;">&#40;</span>route_name=<span style="color: #483d8b;">'todo_index'</span>, renderer=<span style="color: #483d8b;">'todos/index.mako'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> index<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:
    todos, done_todos = get_todo_set<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">dict</span><span style="color: black;">&#40;</span>todos=todos, done_todos=done_todos<span style="color: black;">&#41;</span></pre></div></div>

<p>ผมห่อ get_todo_set แยกไว้ เพราะจะมีการเรียกใช้ที่อื่นด้วย ถ้าเปิดตัว test เทียบดูก็จะเห็น</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'todos'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>, <span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'done_todos'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># test ordering</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'todos'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">task</span>, <span style="color: #483d8b;">&quot;First task&quot;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'todos'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span>.<span style="color: black;">task</span>, <span style="color: #483d8b;">&quot;Second task&quot;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>จะเห็นว่ามีการเช็คด่ากับ response ซึ่งเป็นสิ่งที่ method ส่งกลับไป<br />
ใน method ที่เหลือ ก็ลองเปิด test เทียบเอานะครับ</p>
<p>code นี้จะมี <a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/api/view.html#pyramid.view.view_config">@view_config</a> เป็น decorator ทำหน้าที่ config หน้าที่หลายๆ อย่างสำหรับ controller(หรือ view) เบื้องต้นทีใช้ในตัวอย่างก็มี</p>
<ul>
<li>route_name ไว้เชื่อมกับการกำหนด path ของ uri ซึ่งต้องกำหนดก่อน</li>
<li>renderer ไว้กำหนดไฟล์ที่จะ render หรือถ้ากำหนดเป็น xml หรือ json มันจะแปลงข้อมูลจะส่งไปเป็น xml หรือ json ให้เลย</li>
<li>request_method ไว้กำหนด request method</li>
<li>xhr ไว้กำหนดให้รับ request ที่เป็น ajax</li>
</ul>
<p>หรือดู list ทั้งหมดได้ที่ <a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/viewconfig.html#view-configuration-parameters" target="_blank">doc</a></p>
<p>สำหรับการ test ในสวนของ @view_config มีความจำเป็นต้องไป test ด้วย functional test ครับ เช่น test ว่า status เป็น 200 หรือเปล่า หรือคืน content-type เป็น json หรือเปล่า จำพวกนี้ครับ</p>
<h5>Create</h5>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">@view_config<span style="color: black;">&#40;</span>route_name=<span style="color: #483d8b;">'todo_create'</span>, renderer=<span style="color: #483d8b;">'json'</span>, request_method=<span style="color: #483d8b;">'POST'</span>, xhr=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> create<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:
    todo = Todo<span style="color: black;">&#40;</span>
        task=request.<span style="color: black;">POST</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'task'</span>, <span style="color: #008000;">None</span><span style="color: black;">&#41;</span>,
        priority=request.<span style="color: black;">POST</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'priority'</span>, <span style="color: #008000;">None</span><span style="color: black;">&#41;</span>
    <span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> todo.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'errors'</span>: todo.<span style="color: black;">errors</span><span style="color: black;">&#125;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'id'</span>: todo.<span style="color: #008000;">id</span>, <span style="color: #483d8b;">'task'</span>: todo.<span style="color: black;">task</span>, <span style="color: #483d8b;">'priority'</span>:todo.<span style="color: black;">priority</span>, <span style="color: #483d8b;">'messages'</span>: <span style="color: #483d8b;">'%s has been created'</span> <span style="color: #66cc66;">%</span> todo.<span style="color: black;">task</span> <span style="color: black;">&#125;</span></pre></div></div>

<p>จะเห็นว่าต้องการรับ request มาเป้น ajax เท่านั้น<br />
และการยืนยันว่า todo ที่สร้าง ถูกสร้างอย่างแน่นอนคือถ้าสร้างเสร็จแล้วจะต้องมีการเซ็ทค่า id ให้กับ todo ตามที่ผมอธิบายไปข้างบน คือ ถ้าไม่ flush() ก็จะไม่รู้เลยว่ามันบันทึกจริงๆ หรือเปล่า จะจับ exception ก็ยังคิดท่าไม่ออก แต่ข้อดีของการเช็คแบบนี้คือ มันเช็คได้ละเอียดแยกเป็นขั้นตอนได้ดีกว่า</p>
<h5>Update</h5>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">@view_config<span style="color: black;">&#40;</span>route_name=<span style="color: #483d8b;">'todo_update'</span>, renderer=<span style="color: #483d8b;">'json'</span>, request_method=<span style="color: #483d8b;">'POST'</span>, xhr=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> update<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:
    todo_id = request.<span style="color: black;">matchdict</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'id'</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        todo = DBSession.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Todo<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span>=todo_id<span style="color: black;">&#41;</span>.<span style="color: black;">one</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> NoResultFound:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'errors'</span>: <span style="color: #483d8b;">&quot;No todo id: %s&quot;</span> <span style="color: #66cc66;">%</span> todo_id<span style="color: black;">&#125;</span>
&nbsp;
    todo.<span style="color: black;">task</span>=request.<span style="color: black;">POST</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'task'</span>, <span style="color: #008000;">None</span><span style="color: black;">&#41;</span>
    todo.<span style="color: black;">priority</span>=request.<span style="color: black;">POST</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'priority'</span>, <span style="color: #008000;">None</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> todo.<span style="color: black;">update</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'errors'</span>: todo.<span style="color: black;">errors</span><span style="color: black;">&#125;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'task'</span>: todo.<span style="color: black;">task</span>, <span style="color: #483d8b;">'priority'</span>:todo.<span style="color: black;">priority</span>, <span style="color: #483d8b;">'messages'</span>: <span style="color: #483d8b;">'%s has been updated'</span> <span style="color: #66cc66;">%</span> todo.<span style="color: black;">task</span><span style="color: black;">&#125;</span></pre></div></div>

<p>เบี้องหลังของการ update จริงๆ คือ merge() โดยการใส่ instance ลงไปใน DBSession ถ้ามี id อยู่ จะทำการเปลี่ยนค่า แต่ถ้าไม่มี id มันจะสร้างใหม่ให้เลย</p>
<h5>Delete</h5>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">@view_config<span style="color: black;">&#40;</span>route_name=<span style="color: #483d8b;">'todo_delete'</span>, renderer=<span style="color: #483d8b;">'json'</span>, request_method=<span style="color: #483d8b;">'POST'</span>, xhr=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> delete<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:
    todo_id = request.<span style="color: black;">matchdict</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'id'</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        todo = DBSession.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Todo<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span>=todo_id<span style="color: black;">&#41;</span>.<span style="color: black;">one</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> NoResultFound:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'errors'</span>: <span style="color: #483d8b;">&quot;No todo id: %s&quot;</span> <span style="color: #66cc66;">%</span> todo_id<span style="color: black;">&#125;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> todo.<span style="color: black;">delete</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'errors'</span>: <span style="color: #483d8b;">&quot;%s can't be deleted&quot;</span> <span style="color: #66cc66;">%</span> todo.<span style="color: black;">task</span><span style="color: black;">&#125;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'id'</span>: todo.<span style="color: #008000;">id</span>, <span style="color: #483d8b;">'messages'</span>: <span style="color: #483d8b;">'%s has been deleted'</span> <span style="color: #66cc66;">%</span> todo.<span style="color: black;">task</span><span style="color: black;">&#125;</span></pre></div></div>

<p>และอันสุดท้ายก็เป็นการลบ พร้อมกับข้อควมต่างๆ กรณีที่มีปัญหา</p>
<p>จริงๆ ผมตั้งใจจะทำฝั่ง UI ให้ดูด้วย เป็น ajax แต่พอทำไปสักพักแล้วรู้สึกนานไปละ และผมอยากให้ดูแค่เรื่องของการ test ก็เลยพักไว้แค่นี้</p>
<h5>สรุป</h5>
<p>การ test เบี้องต้นผมพอใจนะ สำหรับการ test แค่ integration test เครืองมืออย่าง nosetest ไว้ทำ code coverage เอามารวมปับ unittest ถือว่าสะดวกสบายพอสมควร ตัว unitest เองก็ test ได้ครอบคลุมดี อาจจะเพราะผมเองมือใหม่ บางท่าอาจจะยังไม่ถูกต้องนัก โดยเฉพาะกับ SQLAlchemy ถ้ามีโอกาสอยู่กับมันนานๆ คงจะมีท่าดีๆ กว่านี้</p>
<p>สามารถ <a href="https://bitbucket.org/punneng/pyramid-testfirst/downloads" target="_blank">Download source code</a> มาดูทั้งหมดได้ครับ</p>
<pre>hg clone https://bitbucket.org/punneng/pyramid-testfirst</pre>
<p>หรือจะ folk กันก็เอาตามสะดวก</p>
<p>ไว้มีเวลาจะมาลอง functional test ให้ดูครับ ตอนนี้ขอสลับโหมดไป Rails ก่อน <img src='http://weblog.punneng.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  เลี้ยงปากเลี้ยงท้องหน่อยครับ</p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.punneng.net/2013/01/hello-pyramid-part-2-test-first/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hello Pyramid [Part 1] &#8211; Test First</title>
		<link>http://weblog.punneng.net/2012/12/hello-pyramid-part-1-test-first/</link>
		<comments>http://weblog.punneng.net/2012/12/hello-pyramid-part-1-test-first/#comments</comments>
		<pubDate>Wed, 19 Dec 2012 18:00:46 +0000</pubDate>
		<dc:creator>PunNeng</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[pyramid]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[sqlalchemy]]></category>
		<category><![CDATA[TDD]]></category>

		<guid isPermaLink="false">http://weblog.punneng.net/?p=70</guid>
		<description><![CDATA[เคยมองหา framework ทางเลือกที่คล้ายๆ Ruby on Rails บน Python ขอแค่มี MVC  มี config น้อยๆ แต่ถ้าจะปรับ ต้องทำได้ มีการจัดการ Routing และเครืองมือสำหรับเขียน Test ที่สนใจก็มี Django, web2py และ Pyramid ซึ่ง Django ก็ใช้ทำงานอยู่ และ Pyramid ก็ดูเบากว่า web2py เลยเลือกตัวนี้ หลังจากอยู่กับมันได้ 2-3 อาทิตย์ พบว่าเป็น framework ที่ไม่ซับซ้อนอะไรมาก เอกสารค่อนข้างชัดเจน แต่ความยากสำหรับคนฝั่ง Rails อย่างผม คือ ผมขี้เกียจหา ORM ดูอยู่นาน ซึ่งผมเลือก SQLalchemy วัดดวงกับมัน ลองดูจริงๆ ก็ไม่ได้เลวร้ายอะไรมาก แค่ต้องทำความคุ้นเคยกับการอ่านเอกสารนิดหน่อย ตั้งแต่เริ่มเขียนเว็บ อ่านเอกสารมาก็เยอะ เพิ่งเจอว่าเอกสารของ [...]]]></description>
			<content:encoded><![CDATA[<p>เคยมองหา framework ทางเลือกที่คล้ายๆ Ruby on Rails บน Python ขอแค่มี MVC  มี config น้อยๆ แต่ถ้าจะปรับ ต้องทำได้ มีการจัดการ Routing และเครืองมือสำหรับเขียน Test ที่สนใจก็มี Django, web2py และ Pyramid ซึ่ง Django ก็ใช้ทำงานอยู่ และ Pyramid ก็ดูเบากว่า web2py เลยเลือกตัวนี้</p>
<p>หลังจากอยู่กับมันได้ 2-3 อาทิตย์ พบว่าเป็น framework ที่ไม่ซับซ้อนอะไรมาก เอกสารค่อนข้างชัดเจน แต่ความยากสำหรับคนฝั่ง Rails อย่างผม คือ ผมขี้เกียจหา ORM ดูอยู่นาน ซึ่งผมเลือก SQLalchemy วัดดวงกับมัน ลองดูจริงๆ ก็ไม่ได้เลวร้ายอะไรมาก แค่ต้องทำความคุ้นเคยกับการอ่านเอกสารนิดหน่อย  ตั้งแต่เริ่มเขียนเว็บ อ่านเอกสารมาก็เยอะ เพิ่งเจอว่าเอกสารของ Pyramid มีการเขียน test ในหน้าแรกๆ ของเอกสารเลย ยิ่งประทับใจเข้าไปใหญ่ แค่แอบงงกับการจัดหมวดหมู่ของ Test ของมันนิดหน่อย จริงๆ อยากได้ BDD แบบ RSpec เลย แต่ยังหาไม่ได้</p>
<p>ผมจะเริ่มต้นด้วยการจินตนาการก่อน ว่าผมจะเขียน Test ไปให้ครอบคลุมกับ CRUD ของ Todolist ขำๆ ตัว test ของผมจะต้อง fail แล้วผมจะค่อยๆ implement ไป ทำให้มันผ่าน ซึ่งการเริ่มต้นของ project ขอให้ setup ตาม <a title="Pyramid Installation" href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/tutorials/wiki2/installation.html" target="_blank">doc</a> นี้ก่อน พอติดตั้งแล้วจะเจอเครืองมือสำหรับ Test 2 ตัวคือ Unittest กับ Test Coverage โดยตัว Unittest ก็เป็น <a title="25.3. unittest — Unit testing framework" href="http://docs.python.org/2/library/unittest.html" target="_blank">Unittest ของ Python</a> เอง(หรือ PyUnit) ส่วนตัว Test Coverage เป็นการรวมตัวของ <a title="Ned Batchelder: coverage.p" href="http://nedbatchelder.com/code/coverage/" target="_blank">Coverage</a> ที่เอาไว้วัดว่า code ถูกเรียกใช้ขนาดไหนเมื่อเทียบกับ code ทั้งหมด กับ <a title="Installation and quick start &amp;mdash; nose 1.2.1 documentation" href="https://nose.readthedocs.org/en/latest/" target="_blank">nose</a> ที่จะทำให้เรากำหนดว่าเราจะเรียก test file ไหนบ้าง  ก่อนที่จะไปดูตัวอย่าง ต้องมาพููดถึงระดับหรือมุมมองการ test ก่อน ใน Pyramid<a title="Unit, Integration, and Functional Testing" href="http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/testing.html" target="_blank"> มันแบ่งเป็น 3 แบบ</a>คือ</p>
<ul>
<li>Unit test คือการ test ในส่วนย่อยสุด(unit) เช่น function เฉพาะงานหรือตัวแปร</li>
<li>Integration test คือการ test ที่รวมสิ่งทีเ่ป็น unit ข้างบน มา test รวมกัน ซื่งในตัวอย่างจะเป็น Integration test ทั้งหมด</li>
<li>Functional test คือการ test เหมือน integration test แต่ run ด้วย code จริง มี stack กี่ชั้นก็ทำงานหมด ไม่มีการ mock/stub ไว้ก่อน</li>
</ul>
<h4>My test cases</h4>
<p>จากนั้น มานึกดูว่า test เราควรจะมีส่วนอะไรบ้าง..</p>
<ul>
<li>C &#8211; Create ควรจะส่ง request ไปที่ function แล้วคืนค่ามาเป็น dict ที่บรรจุข้อมูลของ todo ที่สร้าง</li>
<li>R &#8211; Read(also Edit and list) ควรจะ get ไป server แล้วคืนค่ามาเป็น dict ของ id หรือ list ของ dict ของ todo</li>
<li>U &#8211; Update ใจจริงอยากให้เป็น restful ด้วยการทำให้เป็น put แตผมเพิ่งเริ่ม ท่าพลิกแพลงไว้คราวหลัง ตอนนี้ขอเป็น post ชุดของข้อมูลทีจะ update แล้วคืนค่ามาเป็น dict ของ todo ที่ update แล้ว</li>
<li>D &#8211; Delete ให้เป็น post ไป server แล้วคืนค่ามาเป็น id ของัตวที่ถูกลบ</li>
</ul>
<p>สังเกตดู สิ่งที่ผมพิจารณาคือ มันคืนค่ามาเป็นอะไรใน function ที่เตรียมไว้ ซึ่งจริงๆ มันควรจะมี http status เป็น 200 หรือ ข้อมูลที่คืนมาควจะมี content-type เป็น json ซึ่งจริงๆ ตรงส่วนนี้จะมีเครืองมีที่จำลอง http request จริงๆ ส่งไป test ใน functional test อีกที คืิดว่าคงเห็นภาพที่มันพยายามแบ่ง Integration/Functional test ไว้แล้ว</p>
<h4>Set up and Tear down</h4>
<p>อีกสักนิด จะขาดไม่ได้ ต้องแนะนำสิ่งทีจำเป็นใน Unittest ก่อน&#8230; set up/tear down เป็นพื้นที่สำหรับใส่ code ทีจะให้ถูกเรียกใช้ตอนที่เริ่ม test กับหลัง test ซึ่งโดยทั่วไป มักจะเป็นการเริ่มคำสั่งเตรียม stack ของการ test หรือใส่ชุดข้อมูลสำหรับ test ลงใน database ใน set up พอ test เสร็จแล้ว ก็ต้องเคลียร์ออกที่ใส่ไป ใน tear down  หน้าตาประมาณนี้</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> TestIntegrationTodolist<span style="color: black;">&#40;</span><span style="color: #dc143c;">unittest</span>.<span style="color: black;">TestCase</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> setUp<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">config</span> = testing.<span style="color: black;">setUp</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">from</span> sqlalchemy <span style="color: #ff7700;font-weight:bold;">import</span> create_engine
        <span style="color: #808080; font-style: italic;">#engine = create_engine('sqlite:///todolist.sqlite')</span>
        engine = create_engine<span style="color: black;">&#40;</span><span style="color: #483d8b;">'postgresql://dev:dev@localhost:5432/todolist_test'</span><span style="color: black;">&#41;</span>
&nbsp;
        DBSession.<span style="color: black;">configure</span><span style="color: black;">&#40;</span>bind=engine<span style="color: black;">&#41;</span>
        Base.<span style="color: black;">metadata</span>.<span style="color: black;">create_all</span><span style="color: black;">&#40;</span>engine<span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># the transaction commited after added so rolling back doesnt work on Postgres</span>
        <span style="color: #808080; font-style: italic;">#with transaction.manager:</span>
        instances = <span style="color: black;">&#40;</span>
            Todo<span style="color: black;">&#40;</span>task=<span style="color: #483d8b;">'Second task'</span>, priority=<span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>,
            Todo<span style="color: black;">&#40;</span>task=<span style="color: #483d8b;">'Thrid task'</span>, priority=<span style="color: #ff4500;">5</span><span style="color: black;">&#41;</span>,
            Todo<span style="color: black;">&#40;</span>task=<span style="color: #483d8b;">'First task'</span>, priority=<span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>,
            Todo<span style="color: black;">&#40;</span>task=<span style="color: #483d8b;">&quot;Done task&quot;</span>, priority=<span style="color: #ff4500;">5</span>, done_at=<span style="color: #dc143c;">datetime</span>.<span style="color: black;">now</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: black;">&#41;</span>
        DBSession.<span style="color: black;">add_all</span><span style="color: black;">&#40;</span>instances<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> tearDown<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        DBSession.<span style="color: black;">remove</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        testing.<span style="color: black;">tearDown</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>จากตัวอย่างข้างบน จะเริ่มต้นจากการต่อเข้า PosgreSQL จากนั้นก็เชื่อมต่อระหว่าง Pyramid กับ SQLAlchemy แล้วจึงใส่ code ในส่วนของการเตรียมขอ้มูลสำหรับทดสอบลงไป พอเทสจบ ก็เคลียร์ค่าเก่าไป</p>
<h5>Read</h5>
<p>การ read ใน todolist ของผม จะมีแค่ 1 งานเท่านั้น คือการแสดงรายการใน todolist ทั้งหมด สิ่งที่ผมสนใจคือ พอ request แล้ว มันควรจะคืนค่ามาเป็นชุดของ dict ทีบรรจุข้อมูลของ todo แต่ละอัน</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">    <span style="color: #ff7700;font-weight:bold;">def</span> test_index_pass<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot; test_index_pass &quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">from</span> todolist.<span style="color: black;">controllers</span>.<span style="color: black;">todos</span> <span style="color: #ff7700;font-weight:bold;">import</span> index
        request = testing.<span style="color: black;">DummyRequest</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        response = index<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'todos'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>, <span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'done_todos'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># test ordering</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'todos'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">task</span>, <span style="color: #483d8b;">&quot;First task&quot;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'todos'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span>.<span style="color: black;">task</span>, <span style="color: #483d8b;">&quot;Second task&quot;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>ก็ตรงไปตรงมา สิ่งที่ดูไม่เหมือนคนอื่นเลยคือจะมี done_todos ซึ่งจริงๆ แล้วผมตั้งใจว่าจะแยกตัวที่ทำไปแล้ว ไปไว้อีกกลุ่มนึง เลยแยกไว้ และทำการใส่ค่าเริ่มต้นจากตอน setup ด้วยการใส่ done_at เป็นการบอกว่าตัวนี้ทำแล้ว</p>
<p>ความสะดวกอย่างนึงของ Pyramid คือ มีการจำลอง request ซึ่งง่ายมากด้วย testing.DummyRequest() ซึ่งถ้าเป็นการ post จะมีในตัวอย่างถัดไป</p>
<h5>Create</h5>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">    <span style="color: #ff7700;font-weight:bold;">def</span> test_create_pass<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot; test_create_pass &quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">from</span> todolist.<span style="color: black;">controllers</span>.<span style="color: black;">todos</span> <span style="color: #ff7700;font-weight:bold;">import</span> create
        params = <span style="color: black;">&#123;</span><span style="color: #483d8b;">'task'</span>:<span style="color: #483d8b;">'New task'</span>, <span style="color: #483d8b;">'priority'</span>:<span style="color: #ff4500;">1</span><span style="color: black;">&#125;</span>
        request = testing.<span style="color: black;">DummyRequest</span><span style="color: black;">&#40;</span>params=params, post=params<span style="color: black;">&#41;</span>
        response = create<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertTrue</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'id'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'task'</span><span style="color: black;">&#93;</span>, params<span style="color: black;">&#91;</span><span style="color: #483d8b;">'task'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'priority'</span><span style="color: black;">&#93;</span>, params<span style="color: black;">&#91;</span><span style="color: #483d8b;">'priority'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> test_create_fail<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot; test_create_fail &quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">from</span> todolist.<span style="color: black;">controllers</span>.<span style="color: black;">todos</span> <span style="color: #ff7700;font-weight:bold;">import</span> create
        params = <span style="color: black;">&#123;</span><span style="color: #483d8b;">'task'</span>:<span style="color: #483d8b;">&quot;&quot;</span>, <span style="color: #483d8b;">'priority'</span>: <span style="color: #483d8b;">&quot;low&quot;</span><span style="color: black;">&#125;</span>
        request = testing.<span style="color: black;">DummyRequest</span><span style="color: black;">&#40;</span>params=params, post=params<span style="color: black;">&#41;</span>
        response = create<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertTrue</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'errors'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'errors'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'priority'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'errors'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'task'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'errors'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'task'</span><span style="color: black;">&#93;</span>, <span style="color: black;">&#91;</span><span style="color: #483d8b;">'Please enter a value'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>ส่วนของ create เนี่ย ผมเตรียม test ไว้ 2 กรณี คือ ผ่าน กับ ไม่ผ่าน โดยถ้าไม่ผ่านจะมีข้อความเตือนหน่อยนึง อย่างกรณีนี้ ผมใส่เป็นข้อความเปล่าๆ ไป มันควรจะบอกผมว่า ผมควรใส่ค่าอะไรไปหน่อยนะ</p>
<p>สว่น priority ไว้บอกความเร่งด่วน ค่อนข้างตรงไปตรงมา</p>
<p>ตัวอย่างการ post ด้วย DummyRequest ก็แค่ใส่ค่า post ใน parameter ตอนสร้าง DummyRequest เท่านั้นเอง ส่วน parameter จะมีอะไรบ้าง ก็ลองเปิดอ่าน <a title="Source code for pyramid.testing" href="http://pyramid.readthedocs.org/en/latest/_modules/pyramid/testing.html#DummyRequest" target="_blank">doc</a> ดูครับ จะเห็นตัวอย่างการจำลอง session ด้วย</p>
<h5>Update</h5>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">    <span style="color: #ff7700;font-weight:bold;">def</span> test_update_pass<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot; test_update_pass &quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">from</span> todolist.<span style="color: black;">controllers</span>.<span style="color: black;">todos</span> <span style="color: #ff7700;font-weight:bold;">import</span> update
        params = <span style="color: black;">&#123;</span><span style="color: #483d8b;">'task'</span>:<span style="color: #483d8b;">'Updated task'</span>, <span style="color: #483d8b;">'priority'</span>:<span style="color: #ff4500;">1</span><span style="color: black;">&#125;</span>
        <span style="color: #808080; font-style: italic;"># provide the todo with id</span>
        todo = DBSession.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Todo<span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        request = testing.<span style="color: black;">DummyRequest</span><span style="color: black;">&#40;</span>params=params, matchdict=<span style="color: black;">&#123;</span><span style="color: #483d8b;">'id'</span>:todo.<span style="color: #008000;">id</span><span style="color: black;">&#125;</span>, post=params<span style="color: black;">&#41;</span>
        response = update<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>
        updated_todo = DBSession.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Todo<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span>=todo.<span style="color: #008000;">id</span><span style="color: black;">&#41;</span>.<span style="color: black;">one</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'task'</span><span style="color: black;">&#93;</span>, params<span style="color: black;">&#91;</span><span style="color: #483d8b;">'task'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>updated_todo.<span style="color: black;">task</span>, params<span style="color: black;">&#91;</span><span style="color: #483d8b;">'task'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> test_update_fail<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot; test_update_fail &quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">from</span> todolist.<span style="color: black;">controllers</span>.<span style="color: black;">todos</span> <span style="color: #ff7700;font-weight:bold;">import</span> update
        <span style="color: #808080; font-style: italic;"># test query not found</span>
        request = testing.<span style="color: black;">DummyRequest</span><span style="color: black;">&#40;</span>params=<span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>, matchdict=<span style="color: black;">&#123;</span><span style="color: #483d8b;">'id'</span>:<span style="color: #ff4500;">1</span><span style="color: black;">&#125;</span>, post=<span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>
        response = update<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'errors'</span><span style="color: black;">&#93;</span>, <span style="color: #483d8b;">&quot;No todo id: 1&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># test validation</span>
        params = <span style="color: black;">&#123;</span><span style="color: #483d8b;">'task'</span>:<span style="color: #483d8b;">&quot;&quot;</span>, <span style="color: #483d8b;">'priority'</span>: <span style="color: #483d8b;">&quot;low&quot;</span><span style="color: black;">&#125;</span>
        todo = DBSession.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Todo<span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        request = testing.<span style="color: black;">DummyRequest</span><span style="color: black;">&#40;</span>params=params, matchdict=<span style="color: black;">&#123;</span><span style="color: #483d8b;">'id'</span>:todo.<span style="color: #008000;">id</span><span style="color: black;">&#125;</span>, post=params<span style="color: black;">&#41;</span>
        response = update<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertTrue</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'errors'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'errors'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'priority'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'errors'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'task'</span><span style="color: black;">&#93;</span>, <span style="color: black;">&#91;</span><span style="color: #483d8b;">'Please enter a value'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'errors'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'priority'</span><span style="color: black;">&#93;</span>, <span style="color: black;">&#91;</span><span style="color: #483d8b;">'Please enter an integer value'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>เหมือนของ create เลย แตต่างกันตรงที่ตอน test การ validate  ข้อมูล ผมคิดว่าจะให้ priority เป็นตัวเลข แต่ใน test นี้ผมดันใส่เป็นข้อความไป มันควรจะบอกสิว่าให้ใส่ตัวเลขนะ</p>
<h5>Delete</h5>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">    <span style="color: #ff7700;font-weight:bold;">def</span> test_delete_pass<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot; test_delete_pass &quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">from</span> todolist.<span style="color: black;">controllers</span>.<span style="color: black;">todos</span> <span style="color: #ff7700;font-weight:bold;">import</span> delete
        todo = DBSession.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Todo<span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        request = testing.<span style="color: black;">DummyRequest</span><span style="color: black;">&#40;</span>params=<span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>,matchdict=<span style="color: black;">&#123;</span><span style="color: #483d8b;">'id'</span>:todo.<span style="color: #008000;">id</span><span style="color: black;">&#125;</span>, post=<span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>
        response = delete<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>
        todo_count = DBSession.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Todo<span style="color: black;">&#41;</span>.<span style="color: black;">count</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertIsNot</span><span style="color: black;">&#40;</span>todo, DBSession.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Todo<span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'messages'</span><span style="color: black;">&#93;</span>, <span style="color: #483d8b;">'%s has been deleted'</span> <span style="color: #66cc66;">%</span> todo.<span style="color: black;">task</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>todo_count, <span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> test_delete_fail<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot; test_delete_fail &quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">from</span> todolist.<span style="color: black;">controllers</span>.<span style="color: black;">todos</span> <span style="color: #ff7700;font-weight:bold;">import</span> delete
        <span style="color: #808080; font-style: italic;"># test query not found</span>
        request = testing.<span style="color: black;">DummyRequest</span><span style="color: black;">&#40;</span>params=<span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>, matchdict=<span style="color: black;">&#123;</span><span style="color: #483d8b;">'id'</span>:<span style="color: #ff4500;">1</span><span style="color: black;">&#125;</span>, post=<span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>
        response = delete<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">assertEqual</span><span style="color: black;">&#40;</span>response<span style="color: black;">&#91;</span><span style="color: #483d8b;">'errors'</span><span style="color: black;">&#93;</span>, <span style="color: #483d8b;">&quot;No todo id: 1&quot;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>สำหรับ 2 กรณีอีกเช่นกัน แต่การ test กรณีที่ไม่ผ่าน ก็จะมีข้อความอะไรเล็กหน้อยมาบอกว่าถ้าหากถูกลบไปแล้ว ถ้ากรณีผ่าน จำนวนทั้งหมดควรจะถูกลดไปด้วย</p>
<p>จาก test ทั้งหมดข้างบนเนี่ย พอรันด้วย nosetest หน้าตาจะเป็นแบบนี้</p>
<div id="attachment_90" class="wp-caption alignnone" style="width: 310px"><a href="http://weblog.punneng.net/wp-content/uploads/2012/12/failed-nosetest.png"><img class="size-medium wp-image-90 " title="failed-nosetest" src="http://weblog.punneng.net/wp-content/uploads/2012/12/failed-nosetest-300x201.png" alt="Failed nosetest" width="300" height="201" /></a><p class="wp-caption-text">Failed nosetest</p></div>
<p>จะเห็นถึง error นิดหน่อยคือ พยายามจะ import มา แตเรายังไมได้ implement มันก็เลยพัง<br />
อีกส่วนคือหน้าตาของ code coverage</p>
<p>Post ต่อไปจะ implement แล้วครับ</p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.punneng.net/2012/12/hello-pyramid-part-1-test-first/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fix the handsfree/speaker &#8216;no sound&#8217; issue</title>
		<link>http://weblog.punneng.net/2011/02/fix-the-earphone-speaker-no-sound-issue/</link>
		<comments>http://weblog.punneng.net/2011/02/fix-the-earphone-speaker-no-sound-issue/#comments</comments>
		<pubDate>Sun, 06 Feb 2011 05:52:37 +0000</pubDate>
		<dc:creator>PunNeng</dc:creator>
				<category><![CDATA[Utilities]]></category>
		<category><![CDATA[iphone]]></category>

		<guid isPermaLink="false">http://weblog.punneng.net/?p=63</guid>
		<description><![CDATA[iPhone กากครับ ปัญหาคือ มันดันจำไว้ว่าตอนนี้ยังเสียบ handsfree อยู่ ทั้งๆ ที่ถอดมันออกมาแล้ว เครืองผมเองก็เคยเป็น จำได้ว่าแก้ปัญหานี้ด้วยการเสียบเข้าเสียบออก แล้วมันก็หายเอง พอมาดู video นี้ก็คิดว่าเป็นไปได้ที่การเสียบเข้าเสียบออกจะไปโดนฝุ่นที่เกาะตัว sensor สำหรับ handsfree หรือถ้าไม่หาย ก็ลองวิธีนี้(ตอนหลังของ video) ปล. หากไม่มีน้ำยาที่ไว้เช็ดอุปกรณ์อิเล็กทรอนิกส์ ใช้แอลกอฮอล์เช็ดแผลแทนก็ได้ คำเตือน.. ห้ามกิน]]></description>
			<content:encoded><![CDATA[<p>iPhone กากครับ<br />
ปัญหาคือ มันดันจำไว้ว่าตอนนี้ยังเสียบ handsfree อยู่ ทั้งๆ ที่ถอดมันออกมาแล้ว<br />
เครืองผมเองก็เคยเป็น จำได้ว่าแก้ปัญหานี้ด้วยการเสียบเข้าเสียบออก แล้วมันก็หายเอง<br />
พอมาดู video นี้ก็คิดว่าเป็นไปได้ที่การเสียบเข้าเสียบออกจะไปโดนฝุ่นที่เกาะตัว sensor สำหรับ handsfree</p>
<p><iframe title="YouTube video player" width="640" height="390" src="http://www.youtube.com/embed/TM0GQ58pUEg" frameborder="0" allowfullscreen></iframe></p>
<p>หรือถ้าไม่หาย ก็ลองวิธีนี้(ตอนหลังของ video)</p>
<p><iframe title="YouTube video player" width="640" height="390" src="http://www.youtube.com/embed/ayScKPuZPuM" frameborder="0" allowfullscreen></iframe><br />
ปล. หากไม่มีน้ำยาที่ไว้เช็ดอุปกรณ์อิเล็กทรอนิกส์ ใช้แอลกอฮอล์เช็ดแผลแทนก็ได้<br />
คำเตือน.. ห้ามกิน</p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.punneng.net/2011/02/fix-the-earphone-speaker-no-sound-issue/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Simple startup script for OSX</title>
		<link>http://weblog.punneng.net/2011/01/55/</link>
		<comments>http://weblog.punneng.net/2011/01/55/#comments</comments>
		<pubDate>Thu, 20 Jan 2011 19:38:52 +0000</pubDate>
		<dc:creator>PunNeng</dc:creator>
				<category><![CDATA[Utilities]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[osx]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[startup]]></category>

		<guid isPermaLink="false">http://weblog.punneng.net/?p=55</guid>
		<description><![CDATA[พี่เกรียนเพิ่งได้คอมห่วยๆ มาตัวนึง อยากจะสั่งให้ทำอะไรบางอย่างทุกครั้งที่เปิดเครื่อง ทำได้ด้วยการเขียน startup script มีขั้นตอนไม่มาก อันดับแรกสร้าง folder ใน /Library/StartupItems แล้วก็สร้างไฟล์ plist แล้วก็ไฟล์ที่ไว้สั่งคำสั่ง แค่นี้ ไม่เยอะ เริ่มต้นด้วยการเปิด Teminal แล้วตามนี้.. $ cd /Library/StartupItems $ sudo mkdir WhatEverNameYouWant $ cd WhatEverNameYouWant แล้วก็สร้างไฟล์ชื่อ StartupParameters.plist ถนัดโปรแกรมอะไร ก็ใช้เลย ใส่ไปในไฟล์ว่า.. { Description = &#34;WhatEverNameYouWant&#34;; Provides = (&#34;ScriptNameYouWant&#34;); OrderPreference = &#34;None&#34;; } จากนั้นก็สร้างไฟล์ที่ไว้ execute แล้วใส่คำส่ังที่จะสั่ง อย่าลืม shebang #!/bin/sh ที่หัวไฟล์ ไม่งั้นมันจะไม่รู้ว่าสั่งด้วยภาษาอะไร หน้าตาจะประมาณ #!/bin/sh [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://blog.pokpitch.com/" target="_blank">พี่เกรียน</a>เพิ่งได้คอมห่วยๆ มาตัวนึง อยากจะสั่งให้ทำอะไรบางอย่างทุกครั้งที่เปิดเครื่อง</p>
<p>ทำได้ด้วยการเขียน startup script มีขั้นตอนไม่มาก<br />
อันดับแรกสร้าง folder ใน /Library/StartupItems แล้วก็สร้างไฟล์ plist แล้วก็ไฟล์ที่ไว้สั่งคำสั่ง แค่นี้ ไม่เยอะ</p>
<p>เริ่มต้นด้วยการเปิด Teminal แล้วตามนี้..</p>
<pre class="terminal">$ cd /Library/StartupItems
$ sudo mkdir WhatEverNameYouWant
$ cd WhatEverNameYouWant</pre>
<p>แล้วก็สร้างไฟล์ชื่อ StartupParameters.plist<br />
ถนัดโปรแกรมอะไร ก็ใช้เลย ใส่ไปในไฟล์ว่า..</p>

<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">{
  Description     = &quot;WhatEverNameYouWant&quot;;
  Provides        = (&quot;ScriptNameYouWant&quot;);
  OrderPreference = &quot;None&quot;;
}</pre></div></div>

<p>จากนั้นก็สร้างไฟล์ที่ไว้ execute แล้วใส่คำส่ังที่จะสั่ง<br />
อย่าลืม shebang #!/bin/sh ที่หัวไฟล์ ไม่งั้นมันจะไม่รู้ว่าสั่งด้วยภาษาอะไร</p>
<p>หน้าตาจะประมาณ</p>

<div class="wp_syntax"><div class="code"><pre class="shell" style="font-family:monospace;">#!/bin/sh
The commands to execute
commands
and the last command</pre></div></div>

<p>save แล้วก็สั่งให้ไฟล์มัน executable ได้</p>
<pre class="terminal">sudo chmod +x ScriptNameYouWant</pre>
<p>restart เครื่อง แล้วดูว่าจะทำให้อะไรในเครื่องเสียบ้าง ฮาา</p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.punneng.net/2011/01/55/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quick start to Ruby with MySQL</title>
		<link>http://weblog.punneng.net/2010/12/quick-start-to-ruby-with-mysql/</link>
		<comments>http://weblog.punneng.net/2010/12/quick-start-to-ruby-with-mysql/#comments</comments>
		<pubDate>Tue, 14 Dec 2010 09:35:27 +0000</pubDate>
		<dc:creator>PunNeng</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[quickstarts]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://weblog.punneng.net/?p=25</guid>
		<description><![CDATA[เคยแต่เขียน Ruby on Rails ต่อกับ MySQL แต่ไม่เคยเขียน Ruby ต่อกับ MySQL ตรงๆ เลย เพิ่งจะจำเป็นต้องมาใช้เพื่อเช็คอะไรบางอย่างเล็กๆ น้อยๆ ไม่อยากจะต้องสร้าง rails process มาทั้งดุ้น มันเปลือง ram เอาแบบด่วนๆ เร็วๆ ละกัน เอา driver มาติดตั้งก่อน โดยจะมีตัวที่เขียนด้วยภาษา C กับเขียนด้วยภาษา Ruby ซึ่งจริงๆ แล้ว ถ้าใครมี gem ก็ติดต้ังผ่าน gem ได้เลย ด้วย $ sudo gem install mysql ตัวนี้จะเป็น driver ที่เขียนด้วยภาษา C ขั้นตอนการต่อก็ค่อนข้างง่าย เริ่มต้นด้วยการ require &#8216;mysql&#8217; แต่สำหรับการติดตั้งผ่าน gem ต้อง require [...]]]></description>
			<content:encoded><![CDATA[<p>เคยแต่เขียน Ruby on Rails ต่อกับ MySQL แต่ไม่เคยเขียน Ruby ต่อกับ MySQL ตรงๆ เลย เพิ่งจะจำเป็นต้องมาใช้เพื่อเช็คอะไรบางอย่างเล็กๆ น้อยๆ ไม่อยากจะต้องสร้าง rails process มาทั้งดุ้น มันเปลือง ram</p>
<p>เอาแบบด่วนๆ เร็วๆ ละกัน</p>
<p>เอา driver มาติดตั้งก่อน โดยจะมีตัวที่<a title="MySQL driver for Ruby written by C" href="http://www.tmtm.org/en/mysql/ruby/" target="_blank">เขียนด้วยภาษา C</a> กับ<a title="MySQL driver for Ruby written by Ruby" href="http://www.tmtm.org/en/ruby/mysql/" target="_blank">เขียนด้วยภาษา Ruby</a> ซึ่งจริงๆ แล้ว ถ้าใครมี gem ก็ติดต้ังผ่าน gem ได้เลย ด้วย</p>
<pre class="terminal">$ sudo gem install mysql</pre>
<p>ตัวนี้จะเป็น driver ที่เขียนด้วยภาษา C</p>
<p>ขั้นตอนการต่อก็ค่อนข้างง่าย เริ่มต้นด้วยการ require &#8216;mysql&#8217; แต่สำหรับการติดตั้งผ่าน gem ต้อง require &#8216;rubygems&#8217; มาก่อน แล้วสร้าง connection แล้วค่อย query</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'mysql'</span>
&nbsp;
db = Mysql::new<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;host&quot;</span>, <span style="color:#996600;">&quot;user&quot;</span>, <span style="color:#996600;">&quot;passwd&quot;</span>, <span style="color:#996600;">&quot;db&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
res = db.<span style="color:#9900CC;">query</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;select * from mytable&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
res.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>row<span style="color:#006600; font-weight:bold;">|</span>
  <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;id: #{row[0]}&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>ผลที่ได้</p>
<pre class="terminal">id: 1
id: 2
id: 3
id: 4
...
...</pre>
<p>ค่อนข้างง่าย index ของ array ก็จะเรียงตาม columns ของ table นั้นๆ</p>
<p>หรือถ้าจะระบุเป็น column name เลยก็ได้ ด้วย each_hash</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">res.<span style="color:#9900CC;">each_hash</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>row<span style="color:#006600; font-weight:bold;">|</span>
  <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;id: #{row['id']}&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>คู่มือที่เหลือก็ดูได้จากหน้าเว็บของมันเองได้เลย</p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.punneng.net/2010/12/quick-start-to-ruby-with-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>What the heck is JSONP?</title>
		<link>http://weblog.punneng.net/2010/12/what-the-heck-is-jsonp/</link>
		<comments>http://weblog.punneng.net/2010/12/what-the-heck-is-jsonp/#comments</comments>
		<pubDate>Sat, 04 Dec 2010 16:21:34 +0000</pubDate>
		<dc:creator>PunNeng</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[browser]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[jsonp]]></category>

		<guid isPermaLink="false">http://weblog.punneng.net/?p=6</guid>
		<description><![CDATA[เคยทำให้มันทำงานได้ แต่ไม่เข้าใจเลยว่าเบื้องหลังมันมีอะไร JSONP มีไว้หลีกเลี่ยงปัญหา same origin policy คือ ถ้ามี request จาก browser เช่น javascript หรือ flash มันจะไม่อนุญาตให้เพราะต้องป้องกันเรื่อง script injection คือสามารถเข้าถึง resource อย่าง http cookies(session หรือ authentication หลายๆ อย่างก็เก็บใน cookies) ได้ เราแก้มันไม่ได้ แต่หลีกเลี่ยงได้ โดยแนวคิดเริ่มแรกก็มาจากเราสามารถเรียก javascript จาก script tag จากไหนก็ได้ &#60;script src=&#34;http://somewhere/jsfile&#34; type=&#34;text/javascript&#34;&#62;&#60;/script&#62; หลังจากที่โหลดเสร็จมันก็จะ execute javascript ก้อนนี้ได้ แต่ถ้า request ไปแล้วได้ json กลับมา execute ได้จริง แต่ไม่มีอะไรเกิดขึ้น น่าจะมีอะไรมารองรับ json [...]]]></description>
			<content:encoded><![CDATA[<p>เคยทำให้มันทำงานได้ แต่ไม่เข้าใจเลยว่าเบื้องหลังมันมีอะไร</p>
<p>JSONP มีไว้หลีกเลี่ยงปัญหา <a title="Same origin policy" href="http://en.wikipedia.org/wiki/Same_origin_policy" target="_blank">same origin policy</a> คือ ถ้ามี request จาก browser เช่น javascript หรือ flash มันจะไม่อนุญาตให้เพราะต้องป้องกันเรื่อง script injection คือสามารถเข้าถึง resource อย่าง http cookies(session หรือ authentication หลายๆ อย่างก็เก็บใน cookies) ได้</p>
<p>เราแก้มันไม่ได้ แต่หลีกเลี่ยงได้ โดยแนวคิดเริ่มแรกก็มาจากเราสามารถเรียก javascript จาก script tag จากไหนก็ได้</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #339933;">&lt;</span>script src<span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;http://somewhere/jsfile&quot;</span> type<span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;text/javascript&quot;</span><span style="color: #339933;">&gt;&lt;/</span>script<span style="color: #339933;">&gt;</span></pre></div></div>

<p>หลังจากที่โหลดเสร็จมันก็จะ execute javascript ก้อนนี้ได้</p>
<p>แต่ถ้า request ไปแล้วได้ json กลับมา execute ได้จริง แต่ไม่มีอะไรเกิดขึ้น น่าจะมีอะไรมารองรับ json ก้อนนี้หน่อย แต่การที่จะให้ server return อะไรแบบนี้มันก็ดูวุ่นวาย</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">var</span> json_var <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><span style="color: #3366CC;">'var1'</span><span style="color: #339933;">:</span><span style="color: #3366CC;">'val1'</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>ที่น่าจะ ok ก็น่าจะเป็นการที่เราสามารถกำหนด callback ของเราเองได้</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">myCallback<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span><span style="color: #3366CC;">'var1'</span><span style="color: #339933;">:</span><span style="color: #3366CC;">'val1'</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>โดยตรงส่วนนี้ server ต้องเตรียมให้เราด้วย ซึ่งตรงนี้จะเป็น padding สำหรับรองรับข้อมูลเข้ามา</p>
<p>หน้าที่ต่อไปของเราก็คือประกาศ function นี้ (myCallback) ใน code ของเรา เพื่อรองรับข้อมูล</p>
<p>โดยตัวอย่างจาก graph ของ facebook ก็อนุญาตให้เราเพิ่ม callback นี้ได้เอง</p>
<pre><a href="https://graph.facebook.com/175459332480559/albums?callback=myCallback" target="_blank">https://graph.facebook.com/175459332480559/albums?callback=myCallback</a></pre>
<p>โดยใส่ ?callback=yourCallback เข้าไป</p>
<p>แต่สิ่งที่ทำให้ผมเข้าใจผิดตอนแรก เพราะจาก jQuery ที่สามารถ<a href="http://api.jquery.com/jQuery.ajax/" target="_blank">ประกาศ dataType เป็น jsonp</a> ผมก็เข้าใจว่ามันเป็น async  request ที่สามารถข้าม domain name บน browser ได้ หลงเข้าใจผิดอยู่นาน ซึ่งจริงๆ แล้วเบื้องหลังมันก็เตรียม function แบบนี้ให้เราอยู่ดี หน้าตาของ uri จะเป็นแบบนี้</p>
<pre><a href="https://graph.facebook.com/19292868552?callback=jsonp1291493679672" target="_blank">https://graph.facebook.com/19292868552?callback=jsonp1291493679672</a></pre>
<p>ตัว jsonp1291493679672 จะเป็น callback function ที่ถูกประกาศไว้ใน window แบบ window['jsonp1291493679672'] ซึ่งจะเซ็ต data แล้วคืนค่ามาให้ callback ที่เราประกาศไว้ตอนเรียกด้วย ajax อีกที โดย jQuery จะไปต่อท้าย uri ด้วย ?callback=? จากนั้นจะแทนที่ ? ด้วย jsonp1291493679672 ซึ่ง ตัวเลขด้านหลังมาจาก now() </p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.punneng.net/2010/12/what-the-heck-is-jsonp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The return of PunNeng</title>
		<link>http://weblog.punneng.net/2010/08/hello-world/</link>
		<comments>http://weblog.punneng.net/2010/08/hello-world/#comments</comments>
		<pubDate>Sat, 14 Aug 2010 20:15:31 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://weblog.punneng.net/?p=1</guid>
		<description><![CDATA[มันกลับมาแล้ว]]></description>
			<content:encoded><![CDATA[<p>มันกลับมาแล้ว</p>
]]></content:encoded>
			<wfw:commentRss>http://weblog.punneng.net/2010/08/hello-world/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
