Friday, August 18, 2006

Ruby กับ Ingres

หลังจอเฝ้ารอว่าจะมีใครเขียน adapter ให้ ruby สามารถคุยกับ ingres ได้
ในที่สุด ก็รอไม่ไหว หันไปหาทางเลือกอื่น
นั่นคือ ช่องทาง ODBC

solution ก็คือ
  • unixODBC
    อันนี้ไม่มีอะไรพิเศษ ติดตั้งได้จากพวก Package Manager ของ Distro ได้เลย
    ประเด็นถัดมา ก็คือการสร้าง datasource
    ถ้าสร้างด้วยมือ ดูจะโหดร้ายไปหน่อย
    ingres ก็เลยเตรียม utility ที่ชื่อ iiodbcadmn



  • Ruby ODBC
    ตัวนี้มีปัญหานิดหน่อย
    ตรงวิธีที่เขาเขียน
    เนื่องจากเขาเขียนโดยอิง mysql เป็นหลัก
    พอมาใช้กับ database ตัวอื่น ก็อาจจะเป็นปัญหาบ้าง
    ในกรณีของ ingres ถ้าลอง build แล้ว run testcase ดูจะพบ error นี้
    [pphetra@fedora ruby-odbc-0.9991]$ ruby test.rb sso
    connect.............ok
    create_table........ok
    insert.............../test/20insert.rb:6:in `call': 37000 (2913)
    [unixODBC][Ingres][Ingres ODBC Driver][Ingres]line 1, You cannot assign
    a value of type 'varchar' to a column of type 'integer'.
    Explicitly convert the value to the required type. (ODBC::Error)
    from ./test/20insert.rb:6
    from test.rb:20
    from test.rb:16

    ถ้าเข้าไปดูใน testcase จะเห็น code ที่มีปัญหา หน้าตาเป็นแบบนี้
    $p = $c.proc("insert into test (id, str) values (?, ?)") {}
    $p.call(3, 'FOO')

    ประเด็นที่เป็นปัญหา ก็คือวิธีที่ใช้ในการ binding parameter เข้ากับ statement
    หลังจากไล่ source code ของ ruby-odbc ซักพัก ผมก็เจอจุด quickfix
    ก็เลยแก้ง่ายๆลงไป
    @@ -6265,6 +6266,7 @@
    if (coldef == 0) {
    switch (ctype) {
    case SQL_C_LONG:
    + stype = SQL_NUMERIC;
    coldef = 10;
    break;
    case SQL_C_DOUBLE:

  • Ruby DBI
    ตัวนี้ไม่มีอะไรพิเศษ
    ผมเลือกลงแต่ dbd ที่ต่อเข้า ODBC อย่างเดียว


ขั้นตอนถัดไป ระหว่างรอให้มี ODBC Adapter(สำหรับ rails) ดีๆออกมา
ก็จะทดลองเขียน rails adapter ที่ต่อเข้ากับ ruby dbi ไปพลางๆก่อน
(ใช้ตัวอย่างจาก sqlserver adapter ที่มี mode ที่ใช้ odbc ด้วย)

Related link from Roti

Monday, August 14, 2006

เรื่องเล่าจากทางเหนือ

สมัยเรียนหนังสือ ถึงช่วงปิดเทอมเมื่อไร
ก็จะทำกิจกรรมค่ายอาสา
ขึ้นดอยไปใช้ชีวิตกับชาวเขา
กลางวันช่วยสอนหนังสือเด็ก
กลางคืนช่วยสอนหนังสือผู้ใหญ่
บางวันก็ไปช่วยทำไร่

จบมาแล้ว เวลาเห็นข่าวที่เกี่ยวกับชาวเขา
ก็รู้สึกว่าเป็นความผูกพันที่ต้องติดตามอ่าน

วันนี้อ่านเจอ blog ของ คุณจตุพร วิศิษฎ์โชติอังกูร
ที่ทำงานอยู่ในพื้นที่
เขามีเรื่องเล่าหลายเรื่องที่น่าสนใจทีเดียว

แต่มีอยู่เรื่องที่อ่านแล้วก็เกิดอาการสะดุดทางใจ
เรียนรู้ชีวิตกลางคืน.. ผ่านบาร์อะโกโก้
คือเมื่อก่อนผมก็เคยรวบรวมข้อมูลหญิงบริการที่เป็นชาวเขา
ได้มีโอกาสเห็นสมุดบัญชีของพวกซ่อง ที่จดจำนวนการใช้บริการ
เห็นตัวเลขแล้ว ก็รู้สึกช็อคเหมือนกัน
อธิบายไม่ถูก
คืออยากเห็นคนปฎิบัติต่อคนในฐานะเพื่อนมนุษย์ด้วยกัน
มากกว่าเป็นแค่วัตถุรองรับอารมณ์

Related link from Roti

หนุ่มญี่ปุ่นกับเมืองปาย

ลองอ่านบทสัมภาษณ์ของ หนุ่มญี่ปุ่นคนหนึ่ง
ที่เรียกเมืองปายว่า "บ้าน"
Innerview : Toron

Related link from Roti

Hivemind #2

ลองดูเรื่อง Configuration บ้าง
concept ของ Configuration ใน Hivemind ก็คือ
เวลาเรานำ service ไปใช้, service ย่อมต้องการ parameter บางอย่าง
เพื่อที่จะได้กำหนดพฤติกรรมให้เหมาะกับสภาพแวดล้อมที่ service run อยู่

ใน Hivemind เราแบ่งส่วนของ configuration ออกเป็น 2 ส่วนคือ
  • ส่วนที่ difine นิยาม และการนำไปใช้
    เรียกตรงนี้ว่า Configuration Point
  • ส่วนที่ provide configuration data
    มีศัพท์เรียกว่า Contribution


ดูตัวอย่างดีกว่า
สมมติเรามี service ตัวหนึ่งชื่อ FooService
public interface FooService {

public void connect();

}


public class FooServiceImpl implements FooService {

private List<ServerInfo> serverInfos;

/**
*
@param serverInfo the serverInfo to set
*/

public void setServerInfos(List<ServerInfo> infos) {
this.serverInfos = infos;
}

/* (non-Javadoc)
* @see test.FooService#connect()
*/

public void connect() {
System.out.println("connect to " + serverInfos.get(0).getHost());
}
}


public class ServerInfo {
private String host;
private String port;
private String uri;

// setter & getter ..
}

ในตัวอย่างข้างบนนี้ serverInfos ก็คือ configuration point
ที่เราอยากให้สามารถปรับเปลี่ยนได้ตามบริบทที่ service นี้ไป run อยู่
ดังนั้นใน descriptor file ของเรา เราก็จะกำหนดดังนี้

เริ่มด้วยการกำหนด configuration point ก่อน
<configuration-point id="serverInfo" >
<schema>
<element name="serverInfo">
<attribute name="host" required="true"/>
<attribute name="port" required="false"/>
<attribute name="uri" required="true"/>
<conversion class="test.ServerInfo"/>
</element>
</schema>
</configuration-point>

ให้สังเกตว่าเรามีการ define schema ของรูปแบบของข้อมูลด้วย

จากนั้นก็กำหนดบอกว่า FooService เราต้องการ information จาก configuration-point นี้นะ
<service-point id="foo" interface="FooService">
<invoke-factory>
<construct class="impl.FooServiceImpl">
<set-configuration property="serverInfos" configuration-id="serverInfo"/>
</construct>
</invoke-factory>
</service-point>

สุดท้าย เวลาที่เราต้องการ customize กำหนดค่า serverInfo
เราจะทำผ่าน contribution
โดยรูปแบบข้อมูล ต้องตรงตามที่กำหนดเป็น schema ไว้ใน configuration-point
<contribution configuration-id="serverInfo">
<serverInfo host="128.1.1.10" uri="/app/service"/>
</contribution>


ลองเปรียบเทียบกับ spring บ้าง
ตามตัวอย่างข้างบนนี้ spring จะเขียนเป็นแบบนี้
<bean id="fooService" class="test.impl.FooServiceImpl">
<property name="serverInfos">
<list>
<bean class="test.ServerInfo">
<property name="host" value="128.1.1.10"/>
<property name="uri" value="/app/service"/>
</bean>
</list>
</property>
</bean>


ดูแค่นี้ ก็จะเห็นว่า spring มี configuration ที่ดูเข้าใจง่ายกว่า (จริงๆแล้วขึ้นกับว่า ผู้ดูเป็นใคร?)

แต่ hivemind มีข้อได้เปรียบกว่า spring ตรงที่
contribution ของ hivemind มัน auto-discovery ได้
คือเราสามารถแยก component ออกเป็น module ย่อยๆ
โดยแต่ละ module ก็มี descriptor file ของตัวเอง
ดังนั้นเราอาจจะ package service ไว้ใน module หนึ่ง
แล้วอาจจะ pack contribution ไว้ในอีก module หนึ่ง
เมื่อ registry ของ hivemind load ขึ้นมา มันจะมองหา
descriptor file ที่อยู่ใน classpath ทั้งหมด แล้วประกอบขึ้นมาเป็น registry ให้โดยอัติโนมัติ

Related link from Roti

Sunday, August 13, 2006

Hivemind #1

ช่วงสองสามเดือนมานี่ ชีวิตขลุกอยู่กับ Tapestry
แน่นอนเมื่อโปรเจคใหญ่ขึ้น ซับซ้อนขึ้น
ก็หลีกเลี่ยงไม่ได้ที่จะต้องลงไปแตะ Architecture ที่เป็นฐานรากของ Tapestry
ซึ่งก็คือ Hivemind

Hivemind เรียกตัวเองว่า Service and Configuration MicroKernel
โดย Service ก็คือ POJO ที่สามารถร้อยเข้าหากันด้วยวิธี Dependency Injection เช่นเดียวกับ Spring
ส่วน Configuration ก็คือ การ define data ที่ service ต้องการใช้

ลองดูตัวอย่างการ configuration ของ hivemind

เริ่มด้วยการ define service

<service-point id="foo" interface="Foo">
...
</service-point>


เทียบกับ Spring ที่ใช้ concept เป็น Bean
<bean id="foo" class="test.impl.FooImpl">
...
</bean>

ความแตกต่างในเบื้องต้น จะเห็นได้ว่า bean ใน spring เราจะระบุชื่อที่รวม package name ด้วย
ส่วน hivemind ชื่อ package จะถูก define ไว้ที่ระดับ document root ของ xml

ส่วนประเด็นที่สอง hivemine นิยมประกาศให้ชัดไปเลยว่า
service ที่ expose นั้นมี interface เป็นอย่างไร
ซึ่ง client ที่ต้องการใช้ service สามารถ query ตามชื่อของ service ได้
IPreferenceService svr = (IPreferenceService) registry
.getService(IPreferenceService.class);


ทีนี้ลองไปดูวิธีร้อย service เข้าหากันบ้าง
เอาวิธีร้อยแบบ declarative (ไม่ได้ใช้ autowire)
ในส่วนของ Hivemind
<service-point id="dao" interface="IPreferenceDao">
<create-instance class="impl.PreferenceDapImpl"/>
</service-point>

<service-point id="preferenceService" interface="IPreferenceService">
<invoke-factory>
<construct class="impl.PreferenceServiceImpl">
<set-service property="dao" service-id="dao"/>
</construct>
</invoke-factory>
</service-point>

เทียบกับ spring
<bean id="dao" class="test.impl.PreferenceDaoImpl"/>

<bean id="preferenceService" class="test.impl.PreferenceServiceImpl">
<property name="">
<ref bean="dao"/>
</property>
</bean>


จะเห็นว่าถึงตอนนี้ hivemind กับ spring ก็ดูเหมือนๆกัน
ต่างกันตรงที่ใช้ wording กันคนละแบบ
แต่จริงๆแล้วพอถึงจุดนี้ มันเริ่มเกิดความแตกต่างในวิธี implement แล้ว
ใน Hivemind ถ้าเราลอง get service ขึ้นมา
object ที่เราได้จะมี class เป้น SingletonProxy
ส่วนของ Spring ถ้า get bean ขึ้นมา object ที่ได้
จะเป็น class ตรงตามที่ define ไว้
// hivemind
System.out.println(registry.getService(PreferenceServiceImpl.class);
// => <SingletonProxy for test.preferenceService(test.IPreferenceService)>

// spring
System.out.println(factory.getBean("preferenceService"));
// => test.impl.PreferenceServiceImpl

ดูอย่างนี้แล้ว Hivemind จะออกไปในแนว Lazy initialize มากกว่า
คือถ้า proxy ยังไม่ถูกใช้งาน object จริงๆ ก็จะยังไม่ถูกสร้างขึ้น
แต่ในกรณี spring bean ลูกๆที่ถูกอ้างถึง ต้องมีการ initialize สร้างเป็น object
ก่อนที่จะถูก set ให้กับ bean แม่

พักอยู่ที่จุดนี้ก่อน
ไว้ถ้ามีเวลา จะลงไปลึกถึงเรื่อง interceptor,
configuration point, service management

Related link from Roti