Saturday, November 12, 2005

Behaviour Driven Development

ช่วงนี้เริ่มได้ยินศัพท์ BDD มากขึ้น
BDD ย่อจาก Behavior Driven Development
เป็น concept ที่ evolve มาจาก TDD
หรืือ Test Driven Development

ที่เกิด concept ใหม่นี้ขึ้นมาก็เพราะ
เขามองกันว่า TDD มันใช้ wording ที่มีแต่คำว่า "test"
เช่น เราเขียน TestCase ซึ่งภายในมี method testXXX
และ run TestCase ด้วย TestRunner
ไอ้คำว่า "Test" ทั้งหลายนี่มัน block
mental ของเราให้เห็นแต่เรื่อง test
(บางคนก็มี mental block ที่ว่า ยังไม่ทันเขียน program เลย
จะให้เขียน test ได้อย่างไร)


Sapir-Whorf hypothesis (SWH)
there is a systematic relationship between the
grammatical categories of the language a person
speaks and how that person both understands the
world and behaves in it


BDD ก็เลยเกิดขึ้นมา
โดยพยายามเปลี่ยน wording เพื่อให้
หลีกเลี่ยง mental block
โดยพยายาม shift ไปสู่ Specification หรือ Behavior แทน

BDD focus บน

Bob Martin’s "Specification, not Verification"
  • you’re not verifying that the code works correctly
  • you’re specifying what it means for it to work correctly



ตอนนี้เท่าที่เห็น opensource project ที่เริ่มทำกันก็มี


ซึ่งถ้าตามไปดูเอกสารของทั้ง 2
ก็ดูเหมือนว่า มันเป็นแค่การเปลี่ยนแค่คำศัพท์ที่ใช้เท่านั้น
เช่นจาก assert -> should
ก็ต้องตามดูกันต่อไปว่า
แค่การเปลี่ยนศัพท์เฉยๆ จะทำให้เกิด effect อย่างที่เขาว่าจริงหรือไม่

แต่มี Idea ที่น่าสนใจอยู่อัน
อันนี้เจอที่ obie, has it been 9 years already?
เขาลองร่าง DSL (Domain Specific Language) ที่ implment ด้วย ruby ขึ้นมา
หน้าตาแบบนี้ (จริงๆไม่อยาก copy มาเท่าไรเลย แต่อยากให้เห็นภาพกัน)

actors :audits => Audit::Logger,
:db => PayrollDatabase,
:employee => Employee,
:transaction => Transaction

story "A new employee is added by the receipt of an AddEmployee message." do

Name = "William Jones"
AnnualSalary = 60000
MonthlySalary = 5000
MonthlyType = Employee::Monthly

scenario "add a salaried employee earning 60k per year" do

db.pretend! do
on_create :with => (:name => Name, :salary => AnnualSalary),
:return => Employee.new(Name, MonthlyType, AnnualSalary)
end

transaction = AddSalariedEmployeeMsg.new(Name, AnnualSalary)
transaction.execute()

to_verify do
employee = db.find_by_name(Name)
employee_classification == MonthlyType
employee_salary == MonthlySalary

audits_received :log, NEW_EMPLOYEE
end

end

end

เห็นแล้วรู้สึกน่าสนใจทีเดียว
และก็ชอบตรงที่ Obie เขียนว่า

I started figuring out that there are significantly better ways to achieve the goals of RSpec in Ruby, as long as we remember that Ruby is not Java.


Note:
blockquote ตัดมาจาก presentation ของ Dave Astels

Related link from Roti

Friday, November 11, 2005

gem_server

[ruby]

สงสััยมานานแล้ว
เวลาที่เรา install ruby module ด้วยคำสั่ง gem install
มันจะมีอยู่ช่วงหนึ่งที่มัน generate rdoc ให้เรา
ซึ่ง rdoc ที่ generate ก็ช่างซ่อนอยู่ใน directory
ที่ยากแก่การเข้าถึงเสียเหลือเกิน

พึงรู้วันนี้ว่ามีคำสั่ง gem_server
ที่ start webbrick บน port 8808
ทำให้เรา access rdoc ได้จาก browser

Related link from Roti

DWR

DWR - Direct Web Remoting
คือ framework ที่ช่วยให้เราสามารถ acesss
Java Object ที่อยู่บน Server ได้จาก javascript โดยตรง
ซึ่ง feature ที่น่าสนใจก็คือ มันสามารถใช้กับ spring bean ได้ด้วย

ลองดูตัวอย่าง
สมมติเรามี Bean ที่ทำหน้าที่ค้นหาชื่อลูกค้า

public class CustomerSvr {
.. // hibernate stuff

public List findCustomerByName(String name) {
...
}

}

public class CustomerDao {
private long id;
private String name;

.. // setter & getter here
}



ถ้าเราต้องการให้ DWR expose CustomerSvr
ก็ต้อง config ดังนี้
เริ่มจาก add DWR Servlet ก่อน

<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

จากนั้นก็ต้อง config DWR ว่ามี Bean อะไรที่ expose function
ให้ javascript เห็นได้บ้าง

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE dwr PUBLIC
"-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
"http://www.getahead.ltd.uk/dwr/dwr10.dtd">

<dwr>
<allow>
<convert
converter="bean"
match="service.*"/>

<create
creator="spring"
javascript="customerSvr">
<param name="beanName" value="customerSvr"/>
</create>
</allow>
</dwr>

โดยเราสามารถกำหนด
  • method อะไรที่ต้องการให้เห็นบ้าง
  • วิธีการ convert ผลลัพท์ที่ได้
    เท่าที่เห็นคร่าวๆ ก็มี
    • Bean converter
    • Hibernate converter
      ต่างกับ bean converter ตรงที่ทำ lazy loaded properties ได้
    • JDom Converter


ให้สังเกตุว่าเราใช้ creator="spring"
เป็นการบอกว่าให้ไป lookup หา bean จาก spring context

ถ้า config ไว้ถูกต้อง เราก็จะสามารถทดสอบการทำงานเบื้องต้นได้โดย
ใช้ browser เปิด url http://server:port/dwr/
DWR servlet จะแสดงหน้าจอที่ใช้ในการทดสอบการทำงานของ Bean เรา



ที่นี้ลองดูที่ฝั่ง javascript บ้าง
ถ้าต้องการที่จะ call CustomerSvr
ที่ฝั่ง javascript จะเขียนดังนี้

เริ่มจากให้ include generated script ก่อน

<script type='text/javascript' src='/dwr/interface/customerSvr.js'></script>
<script type='text/javascript' src='/dwr/engine.js'></script>
<script type='text/javascript' src='/dwr/util.js'></script>


ส่วน script ที่เรียกใช้งาน หน้าตาดังนี้

<script>

function findCustomerName(name) {
customerSvr.findByName(name, displayCustomerName);
}

function displayCustomerName(customers) {
var x = $("custName");

var text = 'Customers: <br><ul>';
for(i = 0; i < customers.length; i++) {
text += '<li>';
text += customers[i].name;

text += ' (';
text += customers[i].id;
text += ')';
text += '</li>';
}
x.innerHTML = text;
}

</script>

มีที่น่าสังเกตุคือ เราเรียกใช้ bean โดยอ้างถึงชื่อที่เรา config ใน dwr.xml ได้เลย
ในที่นี้ก็คือประโยค customerSvr.findByName
แต่ต้องเพิ่ม callback function เข้าไปใน parameters ที่ส่งให้ findByName ด้วย

ส่วนใน callback function จะเห็นว่า
เราใช้ $('xxx') ซึ่งเป็น util function
ที่เรา include เข้ามาจาก DWRUtil.js
โดย function นี้ใช้ในการ access DHTML Element

สุดท้ายก็คือ html element ที่เรียกใช้งาน

<body>
<input type="text" onBlur="findCustomerName(this.value)"/><br/>
<span id="custName">Name<span>
</span>
</body>


ข้อดีของ DWR
จะเห็นว่า DWR ซ่อนรายละเอียดต่างๆไว้เบื้องหลังหมด
เราแค่รู้ชื่อ service กับชื่อ method ตลอดจน return structure เท่านั้นเอง

ข้อเสียเท่าที่เห็น ก็มี
เนื่องจาก object ที่ส่งกลับ ต้องมีการทำ serialize กับ deSerialize
ดังนั้น object ที่ DWR support สามารถใช้ได้ก็จะมีจำกัด
ส่งผลให้บางครั้ง เราต้องเพิ่ม method ที่ให้ผลลัพท์แบบที่ DWR สามารถ convert ได้
ทำให้มี method ที่ redundant ใน business layer เพิ่มขึ้นจำนวนหนึ่ง

อ่านเพิ่มเติมได้ที่นี่
Ajax with Direct Web Remoting

Related link from Roti

ri18n error on Windows Platform

สืบเนื่องจาก post เรื่อง ri18n ในครั้งก่อน
ได้รับแจ้งว่า ไม่สามารถ run บน windows platform ได้
โดยเกิด error เมื่อเรียกใช้ rake i18n

C:\users\pok\rails\test>rake i18n
(in C:/users/pok/rails/test)
rake aborted!
uninitialized constant Catalog::Iconv


ค้นหาบน google พบว่าเกิดจาก missing library ที่ชื่อ iconv
โดย ruby ที่ install จาก one install click
จะไม่ include library ตัวนี้ให้เรา
เราต้อง install library เอง
ตัว installer ของ iconv หาได้จากที่นี่ link

Related link from Roti

Wednesday, November 09, 2005

Spring 's Configuration file with Jacn

ปัจจุบันผมใช้ Spring framework
เป็น container หลักในการพัฒนา Application
แต่มีปัญหาหนึ่งที่กวนใจอยู่บ้าง
ก็คือ configuration file ของ spring
ซึ่งประเด็นปัญหาที่กวนใจก็คือ
  • การอ่าน xml file
    ถ้าใครเคยเปิด config file ที่ตัวใหญ่ๆดู
    จะรู้ว่ามันลายตาขนาดไหน มีแต่คำว่า bean เต็มไปหมด
  • การ edit xml file
    เรื่องสะกดผิด เป็นเรื่องปกติ
    ซึ่งต้อง run ก่อนจึงจะเห็น stack trace ที่เกิดขึ้น


ใน post นี้จะพูดถึงกรณีที่ 2 เพียงอย่างเดียวก่อน
solution ที่มักใช้กันสำหรับปัญหานี้ก็คือ
Spring IDE
ซึ่งก็ใช้ได้ดีที่เดียว เพียงแต่มีข้อจำกัดว่า project เราต้องใช้ Eclipse ในการพัฒนา

solution อีกอันหนึ่งสำหรับประเด็นปัญหานี้ก็คือ
Jacn (ไม่รู้ย่อมาจากอะไร)
ซึ่งเปลี่ยนจากการ config ด้วย xml file
ไปเป็นการ config ด้วย java file แทน

ลองดูตัวอย่าง config file
กรณีใช้ xml file
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="messageSvr" class="test.MessageImpl"/>

<bean id="greeting" class="test.Greeting">
<property name="messageSvr" ref="messageSvr"/>
</bean>
</beans>


กรณีใช้ Jacl
package test;

import bran.spring.jacn.AbstractJacnConfigFile;

public class Beans extends AbstractJacnConfigFile {

static Greeting greeting;
static MessageImpl messageImpl;

public void wire() throws Exception {
greeting.setMessageSvr(messageImpl);
}

}


เวลาเรียกใช้งาน ApplicationContext
เราจะเรียกใช้ผ่านทาง bran.spring.jacn.JacnApplicationContext แทน
ตามตัวอย่างนี้
public class TestJacn extends TestCase {

ApplicationContext context;

protected void setUp() throws Exception {
context = new JacnApplicationContext(test.Beans.class);
}

public void test() {
Greeting greeting = (Greeting) context.getBean("greeting");
assertEquals("hello world", greeting.getMessage());
}

}


ข้อดีสำหรับ Jacn ก็คือ แทนที่จะเขียน xml โดยตรง เราก็เปลี่ยนมา
เขียน java แทน ซึ่งทำให้เราสามารถใช้ feature code completion
เข้ามาช่วยเขียน รวมทั้งใช้ javac ช่วยในการตรวจสอบ validation ได้ด้วย

การทำงานภายในของ Jacn ที่น่าสนใจก็คือ
คนเขียนเขาใช้วิธีการแปลง class file ที่เราเขียน ให้เป็น DOM
ซึ่งอยู่ในรูปแบบเดียวกับที่ spring ใช้ในอ่าน xml file
เมื่อได้ DOM มาแล้วก็ค่อยส่งต่อให้ spring เป็นผู้ resolve และทำ dependency injection ต่อไป

ส่วนวิธีการที่ใช้ implement การแปลง class file ไปเป็น DOM นั้น
เขาใช้ ASM เข้ามาช่วยทำ โดย Implement ClassVisitor ขึ้นมา

Related link from Roti

Tuesday, November 08, 2005

NASA & Eclipse RCP


Did you know that one of the mission-critical operations tools for the Spirit and Opportunity Mars rover mission is built on the Eclipse Rich Client Platform? NASA has also chosen the Eclipse Rich Client Platform as the framework for many of the mission operations tools that will be used on the 2007 Mars lander and 2009 Mars rover missions.


อ่านรายละเอียดใน blog ของ Scott Schram's Blog

Related link from Roti