เล่น comet บน rails ด้วย shooting star (2)

เอาล่ะ ได้เวลาสนุกแล้ว

พอเรามี chat แล้ว

มาทำ เครื่องดักฟังกันเถอะ ฟังได้อย่างเดียว ห้ามพูด

สร้าง controller เป็นของตัวเองแมร่งเลย

newchat_controller.rb—-

class NewchatController < ApplicationController

skip_before_filter :verify_authenticity_token # ปิด verify authen ของ rail ไปซะ

def index

session[:name] ||= 'guest'

end

# เพราะว่าใน meteor_strike มันมี javascript ที่ไปเรียก action event เราเลยต้องสร้าง action event มาด้วย



def event

message = case params[:event]

when 'init'; "connection established by #{params[:type]}."

when 'enter'; "#{params[:uid]} joined."

when 'leave'; "#{params[:uid]} left."

end

@chat = Chat.new(

:name => '(* system *)',

:created_at => Time.now,

:message => message)

render :action => 'show'  # นี่มัน render show เราเลยต้องสร้าง </strong>show.html.erb

end

end

index.html.erb—

<%= javascript_include_tag :defaults %>

<ul id='chat-list'>

</ul>

<%= meteor_strike 'chat', :uid => session[:name], :event => %Q{

new Ajax.Updater('chat-list', #{url_for(:action => 'event').to_json}, {

insertion: Insertion.Top, parameters: params})}, :noflash => true

%>

show.html.erb—

<li>
<span><%=h @chat.name %></span>
<span><%=h @chat.message %></span>
</li>

ไหนลองปรับๆหน่อยเด๊ะ

จาก vendor/plugins/meteor_strike/README

 user_identifier,
:tag => [tag1, tag2],
:event => %Q{
switch(params.event){ # พบว่ามันเลือก event ทำได้นี่หว่า
case 'enter':
/* params.uid is entered to the channel with params.tag */
break;
case 'leave':
/* params.uid is left from the channel with params.tag */
break;
case 'update':
/* params.uid is updated status in the channel */
break;
}
}
%>

งั้นลองเปลี่ยน event เป็นตามนี้เลยละกัน

<%= meteor_strike 'chat', :uid => session[:name], :event => %Q{
switch(params.event){ # พบว่ามันเลือก event ทำได้นี่หว่า
case 'enter':
/* params.uid is entered to the channel with params.tag */
#
new Ajax.Updater('chat-list', #{url_for(:action => 'event').to_json}, {
insertion: Insertion.Top, parameters: params})
break;
case 'leave':
/* params.uid is left from the channel with params.tag */
# 
new Ajax.Updater('chat-list', #{url_for(:action => 'event').to_json}, {
insertion: Insertion.Top, parameters: params})
break;
case 'update':
/* params.uid is updated status in the channel */
# new Ajax.Updater('chat-list', #{url_for(:action => 'event').to_json}, {
#  insertion: Insertion.Top, parameters: params})
break;
}
}, :noflash => true
%>

แตกมาเปง 3 อัน แล้ว comment อัน update ไว้

นั่นแน่ ไม่อัพเดตจริงๆด้วย

นี่คือ XHR เมื่อมีคน อัพเดตอะไรมา

http://localhost:8080/%5B channel name ]

screenshot-mozilla-firefox-1

ถ้ามี event มันจะเรียกไปที่ http://localhost:3000/newchat/event

=================================================

ทีนี้ ลอง พูดอย่างเดียว ห้ามฟัง บ้าง

จะเห็นว่า มันจะเป็น ajax ไปเรียก talk

talkative_controller.rb —-

class TalkativeController < ApplicationController

skip_before_filter :verify_authenticity_token

def index

session[:name] ||= 'guest'

end

def show

@chat = Chat.find(params[:id])

end

def talk

@chat = Chat.new(

:name => session[:name], :message => params[:message])

if @chat.save

content = render_component_as_string :action => 'show', :id => @chat.id # render_component_as_string : อันนี้มัน render มาทั้ง action ได้ string มาเลย

javascript = render_to_string :update do |page|

page.insert_html :top, 'chat-list', content

end

Meteor.shoot 'chat', javascript

render :update do |page|

page[:message].clear

page[:message].focus

end

else

render :nothing => true

end

end

end

index.html.erb —-

<%= javascript_include_tag :defaults %>

<% form_remote_tag(:url => {:action => 'talk'}) do |f| %>

<%= text_field_tag :message %>

<%= submit_tag 'Talk' %><br />

<% end %>

show.html.erb —-

<li>

<span><%=h @chat.name %></span>

<span><%=h @chat.message %></span>

</li>

พอจับดู พบว่า พอ submit form แล้วมันเรียกไปที่

http://localhost:3000/talkative/talk เท่านั้นเอง

ajax comet basic tutorial

เพราะว่า server ไม่สามารถเปิด connection ไปยัง browser ได้
ทางเดียวที่เป็นไปได้คือ ให้ browser hold connection ไว้

เช่น ถ้าเป็น php ก็ใช้ sleep

แล้วไปแก้ปัญหา server timeout โดยการเปิด connection ใหม่ ( resetConnection )

resetConnection คร่าวๆ คือ ใข้ heartbeat

ก็คือ call function heartbeat ทุกครั้งที่ response กลับมา เพื่อกันไม่ให้ heartbeat มันไป resetConnection

var timeoutId = null;

function heartbeat(){

clearTimeout(timeoutId);

timeoutId = setTimeout(resetConnection, 10000); // 10 sec.

}

ถ้า connection มันขาดหายไป ( timeout ไปแล้ว ) มันก็จะ resetConnection ใหม่ให้เอง

ซึ่งปกติเขาใช้ iframe เป็นตัว connector กัน โดยเซต src ( location ) ของ iframe เป็นไฟล์ response เลย

แต่มันด้วยการที่มันเป็น iframe เลยมีปัญหานิดหน่อย คือ browser จะขึ้นสถานะเป็น Loading ตลอดเวลา เพราะว่า connection ไปยัง server ในหน้า iframe ไม่ได้ถูกปิดไป

วิธีแก้สำหรับ IE คือ

แทนที่จะใส่ iframe ตรงๆ ก็ไปใส่ iframe ไว้ใน ActiveRecord(“htmlfile”); ซึ่งเป็น object ของ IE

ซึ่ง htmlfile ของ IE มันแยกจากหน้าเวป ดังนั้น จะไม่มีสถานะ loading ใน browser มาให้วุ่นวายใจ

เช่น

oPage = new ActiveRecord("htmlfile");

oPage.open();

oPage.write("<html><body></body></html>");

oPage.close("");

# โดยอย่าลืมใส่ reference ไปที่หน้าปัจจุบันด้วย เพราะว่า htmlfile ของ IE มันแยกเด็ดขาดจากหน้าเพจจริงๆ ( ใช้ parent หรือ top เรียกไม่ได้ )

oPage.parentWindow._parent = self;  # คือ เซตให้เพจลูกเรียก parent._parent แล้วมาเจอ object นี้นั่นเอง
# ดังนั้นในเพจ response ใน iframe จึงเรียก parent._parent.heartbeat() ได้

# หมายเหตุ <em>_parent</em> เป็นชื่อเฉยๆ นะ จะเปลี่ยนเป็นอย่างอื่นก็ได้แหละ</strong>

oPage.body.innerHTML = "<iframe src=\"http://dsin.blogspot.com\"></iframe>"

วิธีแก้สำหรับ firefox คือ

firefox มีวิธีจัดการกับ HTTP streaming ได้ดีกว่า IE

แทนที่จะใช้ iframe ก็ให้ไปใช้ XMLHttpRequest แทน โดยดักจับ สถานะ .readyState เป็น 3 ( คือ ส่งแล้ว ยังไม่ complete )

ref : comet and reverse ajax book ( Apress )

เล่น comet บน rails ด้วย shooting star (1)

ลองเข้าไปดูตามลิงก์นี้ แล้วทำตาม ได้ดังนี้

# เริ่มแรก ลง shooting star plugin ด้วย gem

$ gem install shooting_star

————————————————————————–

# create your own project

$ rails real_chat

# change the direction

$ cd real_chat

# initialize the shooting star, this command reacts nothing but don’t worry 🙂

$ shooting_star init

# มันแอบไปสร้าง config file ของ shooting_star ( config/shooting_star.yml )

# แล้วก็ vendor/plugins/meteor_strike/

[ meteor_strike เป็น plugin ของ rails ซึ่งทำหน้าที่เป็น client ของ shooting_star ]
# generate the bottom technology of comet with the generator given by shooting star

$ ruby script/generate meteor

exists  app/controllers/
exists  test/functional/
create  app/views/meteor
exists  app/models/
exists  test/unit/
create  app/controllers/meteor_controller.rb
create  test/functional/meteor_controller_test.rb
create  app/helpers/meteor_helper.rb
create  app/views/meteor/strike.rhtml
create  app/models/meteor.rb
create  test/unit/meteor_test.rb
create  public/meteor_strike.swf
create  db/migrate
create  db/migrate/20081028162753_create_meteors.rb
# Also you can get the chat scaffold type the following command

$ ruby script/generate chat

exists  app/controllers/
exists  test/functional/
create  app/views/chat
exists  app/models/
exists  test/unit/
create  app/controllers/chat_controller.rb
create  test/functional/chat_controller_test.rb
create  app/helpers/chat_helper.rb
create  app/views/layouts/chat.rhtml
create  app/views/chat/index.rhtml
create  app/views/chat/show.rhtml
create  app/models/chat.rb
create  test/unit/chat_test.rb
exists  db/migrate
create  db/migrate/20081028162839_create_chats.rb

# Launch your web server!!

$ ruby script/server

# Shooting star also needs to be launched

$ shooting_star start

shooting_star service started.

ลองเด๊ะ http://localhost:3000/chat

จะได้มาประมาณนี้

เหมือน มันจะต่อโดยใช้ flash เป็นอันดับแรก

  • (* system *) connection established by flash.

ถ้าใช้ flash ไม่ได้ มันถึงต่อโดยใช้ XMLHttpRequest

  • (* system *) connection established by xhr.

วิธี disable flash คือ ใส่คำว่า :noflash => true เข้าไปให้ meteor_strike helper เช่น

<%= meteor_strike ‘chat’, :uid => session[:name], :event => %Q{
new Ajax.Updater(‘chat-list’, #{url_for(:action => ‘event’).to_json}, {
insertion: Insertion.Top, parameters: params})}, :noflash => true
%>

มาแงะโค้ดกันเถอะ !!

ส่วน view ก่อนละกัน หน้า /chat นะ

มันมี layout อยู่

app/views/layouts/chat.rhtml

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en” lang=”en”>
<head>
<meta http-equiv=”content-type” content=”text/html;charset=UTF-8″ />
<title>Chat: <%= controller.action_name %></title>
<%= javascript_include_tag :defaults %>
</head>
<body>
<%= yield %>
</body>
</html>

พอ view source  แล้ว

พบว่า <%= javascript_include_tag :defaults %> ที่ render จริงๆ มี vbscript ด้วย อูฮู คนทำ มันขยันทำจริงๆ

แล้วก็นี่ app/views/chat/index.rhtml

<% form_remote_tag(:url => {:action => ‘listen’}) do |f| %>
<%= text_field_tag :name, session[:name] %>
<%= submit_tag ‘Listen‘ %><br />
<% end %>

<% form_remote_tag(:url => {:action => ‘talk’}) do |f| %>
<%= text_field_tag :message %>
<%= submit_tag ‘Talk‘ %><br />
<% end %>
<ul id=”chat-list”>
<% for chat in @chats %>
<%= render_component :action => ‘show’, :id => chat.id %>
<% end %>
</ul>
<%= meteor_strike ‘chat’, :uid => session[:name], :event => %Q{
new Ajax.Updater(‘chat-list’, #{url_for(:action => ‘event’).to_json}, {
insertion: Insertion.Top, parameters: params})}
%>

ปุ่ม Listen กับ Talk แยก form กัน

Listen เรียกไปที่ http://localhost:3000/chat/listen

Talk เรียกไปที่ http://localhost:3000/chat/talk

ถ้า database มีการ update มันจะส่งมาทาง

http://localhost:3000/meteor/strike/16?channel=chat&#1

http://localhost:3000/meteor/strike/17?channel=chat&#2

ซึ่งมันเป็น javascript ประมาณนี้

  (function(){
    var channel = "chat";
    var javascript = "try {\nnew Insertion.Top(\"chat-list\", \"<li>\\n  <span>guest</span>\\n  <span
>kkk</span>\\n</li>\\n\");\n} catch (e) { alert('RJS error:\\n\\n' + e.toString()); alert('new Insertion
.Top(\\\"chat-list\\\", \\\"<li>\\\\n  <span>guest</span>\\\\n  <span>kkk</span>\\\\n</li>\\\\n\\\")
;'); throw e }";
    var execute = function(){
      var ms = null;
      try{if(parent.meteorStrike_installed) ms = parent.meteorStrike}catch(e){}
      ms = (ms || parent.parent.meteorStrike)[channel];
      if(ms) ms.evaluate(javascript, location.hash.slice(1));
      else setTimeout(execute, 0);
    };
    execute();

ul chat-list เป็นที่แสดง chat ธรรมดา

ส่วน tag meteor_strike

<%= meteor_strike ‘chat’, :uid => session[:name], :event => %Q{
new Ajax.Updater(‘chat-list’, #{url_for(:action => ‘event’).to_json}, {
insertion: Insertion.Top, parameters: params}
)}
%>

สังเกต ว่า เราสามารถใส่ event ได้ให้มันไปอัพเดตที่ไหน

ซึ่ง ruby code ตะกี้

พอเรนเดอร์แล้วจะได้ดังนี้

  <iframe id="meteor-strike-1" name="meteor-strike-1"></iframe>
  <form id="meteor-strike-1-form" target="meteor-strike-1" method="POST"
    action="http://localhost:8080/chat">
    <input name="execute" value="http://localhost:3000/meteor/strike" />
    <input name="tag" /><input name="uid" /><input name="sig" />
    <input name="heartbeat" value="" />

  </form>
  <div id="meteor-strike-1-flash"></div>
  <script type="text/javascript">
//<![CDATA[
    window.meteorStrike_installed = true;
    window.meteorStrike = window.meteorStrike || new Object;
    meteorStrike.getFlashVersion = function(){
      ...
      return version
    };
    (function(){
      var channel = "chat";
      var UID = "haha", TAGS = [];
      var encodeTags = function(tags){
        var encode = function(i){return encodeURIComponent(i)};
        return $A(tags).uniq().map(encode).join(',');
      };
      var ms = meteorStrike[channel] = meteorStrike[channel] || new Object;
      ms.getTags = function(){return TAGS};
      ms.getUid = function(){return UID};
      ms.executionQueue = {};
      ms.executionCounter = null;
      ms.evaluate = function(js, serialId){
        if(ms.executionCounter == null){
          ms.executionCounter = serialId;
        }
        ms.executionQueue[serialId] = js;
        if(serialId == ms.executionCounter){
          while(js = ms.executionQueue[ms.executionCounter]){
            eval(js);
            delete ms.executionQueue[ms.executionCounter];
            ++ms.executionCounter;
          }
        }else{
          setTimeout(function(){
            if(js = ms.executionQueue[this]){
              eval(js);
              delete ms.executionQueue[this];
              ms.executionCounter = null;
            }
          }.bind(serialId), 3000);
        }
      };
      ms.event = function(params){
        if(params.event == 'init'){
          if(ms.connection) return;
          if(ms.connecting && ms.connecting != params.type) return;
          ms.connection = params.type;
        }
# request ไปที่ http://localhost:3000/chat/event
# นี่คึอผลลัพธ์ที่ได้มา
#
# <li>
#  <span>(* system *)</span>
# <span>connection established by flash.</span>
# </li>
 (function(){
  new Ajax.Updater('chat-list', "/chat/event", {
    insertion: Insertion.Top, parameters: params})})();
      };
      ms.update = function(uid, tags){
        new Ajax.Request("http://localhost:3000/meteor/update", {postBody: $H({
          channel: channel, uid: uid || UID,
          tag: encodeTags(tags || TAGS), sig: "1225212975634282"
        }).toQueryString(), asynchronous: true});
        UID = uid || UID, TAGS = tags || TAGS;
      };
      ms.tuneIn = function(tags){
        ms.update(UID, TAGS.concat(tags || []).uniq());
      };
      ms.tuneOut = function(tags){
        ms.update(UID, Array.prototype.without.apply(TAGS, tags));
      };
      ms.tuneInOut = function(tagsIn, tagsOut){
        var tags = TAGS.concat(tagsIn || []).uniq();
        ms.update(UID, Array.prototype.without.apply(tags, tagsOut));
      };
      ms.tuneOutIn = function(tagsOut, tagsIn){
        var tags = Array.prototype.without.apply(TAGS, tagsOut);
        ms.update(UID, tags.concat(tagsIn || []).uniq());
      };
# Event.observe ของ prototype ( ดู ดอก ที่ http://www.prototypejs.org/api/event/observe )
# อันนี้คือ window.onload นั่นเอง
      Event.observe(window, 'load', function(){
        setTimeout(ms.connector = function(){
          if(ms.connection) return;
          if(ms.connecting && ms.connecting != 'xhr') return;
          var form = $("meteor-strike-1-form");
          form.uid.value = "haha";
          form.tag.value = "";
          form.sig.value = "1225212975634282";
          var timerId = setTimeout(ms.connector, 3000);
          $('meteor-strike-1').onload = function(){clearTimeout(timerId)};
          form.submit();
        }, meteorStrike.getFlashVersion() >= 8 ? 3000 : 0);
      });
    })();

    function meteor_strike_1_DoFSCommand(command, args){
      var ms = meteorStrike["chat"];
      args = unescape(args);
      switch(command){
      case 'execute':
        if(ms.connection == 'flash' || ms.connecting == 'flash') eval(args);
        else if($('meteor-strike-1-flash')) Element.remove('meteor-strike-1-flash');
        break;
      case 'event':
        switch(args){
        case 'connect': // intercept xhr connection
          ms.connecting = 'flash';
          if(ms.connection != 'flash') ms.connection = null;
          if($('meteor-strike-1')) Element.remove('meteor-strike-1');
          break;
        }
        break;
      }
    }

    if(meteorStrike.getFlashVersion() >= 8){
      $('meteor-strike-1-flash').innerHTML = "\n<object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\"\n codebase=\"http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0\" width=\"300\" height=\"300\"\n id=\"meteor_strike_1\">\n  <param name=\"allowScriptAccess\" value=\"sameDomain\" />\n  <param name=\"FlashVars\" value=\"channel=chat&tag=&uid=haha&sig=1225212975634282&base_uri=http://localhost:3000&server=localhost:8080&heartbeat=&debug=null&meteor_strike_id=1\" />\n  <param name=\"movie\" value=\"/meteor_strike.swf?1225034522\" />\n  <param name=\"menu\" value=\"false\" />\n  <param name=\"quality\" value=\"high\" />\n  <param name=\"devicefont\" value=\"true\" />\n  <param name=\"bgcolor\" value=\"#ffffff\" />\n  <embed src=\"/meteor_strike.swf?1225034522\" menu=\"false\"\n   quality=\"high\" devicefont=\"true\" bgcolor=\"#ffffff\" width=\"300\" height=\"300\"\n   swLiveConnect=\"true\" id=\"meteor_strike_1\"\n   name=\"meteor_strike_1\" flashvars=\"channel=chat&tag=&uid=haha&sig=1225212975634282&base_uri=http://localhost:3000&server=localhost:8080&heartbeat=&debug=null&meteor_strike_id=1\"\n   allowScriptAccess=\"sameDomain\" type=\"application/x-shockwave-flash\"\n   pluginspage=\"http://www.macromedia.com/go/getflashplayer\" />\n</object>\n";
    }
//]]>
</script>

น่าจะเป็นส่วนที่สำคัญที่สุด เพราะว่าเป็นตัวรับข้อความ chat กลับมาโชว์ เมื่อ database มีการ update แล้ว

ถ้าสังเกตดูจะเจอ ฟังก์ชั่นหน้าตาประหลาด เลยลองเขียนโค้ดเทสดูดังนี้

<script language=”javascript”>
(function(){
alert(‘test’)
})();
</script>

ปรากฏว่ามัน alert test มาแหะ

เลยเดาๆ ว่า เป็น execute code ธรรมดาละกันนะ

ต่อไปมาดู model ว่ามันเรียกอะไรกันยังไง

หรือ จะเล่นแบบหลายๆอัน

จะเห็นว่ามันใช้ druby ( Distributed Ruby ) เพื่อไปเรียก port อื่น

ดูมาจากไฟล์ config/shooting_star.yml

server:
host: 0.0.0.0
port: 8080
shooter:
uri: druby://0.0.0.0:7123

จริงๆ แล้วมันมี shooting_star รันเป็น server อยู่ที่ port 8080 นะ

ซึ่ง binary อยู่ที่ /var/lib/gems/1.8/bin/shooting_star

เวลา talk ปุ๊ปมันจะส่ง request ไปที่ chat_controller.rb

action talk ซึ่งมัน insert chat log ลง database แล้วแอบไปเรียก Meteor.shoot

—-

referenece

Drb : http://www.chadfowler.com/ruby/drb.html

ทางเลือกอื่นของการทำ chat บน rails : http://abhijat.name/crossroads/2008/04/01/rails-chat-demystified/

chat ตัวนี้ก็น่าสนใจนะ : http://home.gna.org/xmpp4r/

gedit highlight mode

ด้วยความรำคาญที่จะต้องไปเซต view > highlight mode ทุกที

$ sudo gedit /usr/share/mime/packages/freedesktop.org.xml

ของเดิมมันมีประมาณนี้อยู่

<glob pattern="*.html"/>
<glob pattern="*.htm"/>

เพิ่มนี่ลงไป

<glob pattern="*.tpl"/>
<glob pattern="*.rhtml"/>
<glob pattern="*.erb.html"/>

จากนั้น

$ update-mime-database /usr/share/mime

ถ้าต้องการให้ อ่าน yaml ได้ ให้ไปโหลด language spec มีคนเขียนเอาไว้แล้ว มาจาก ที่นี่

หรือถ้าจะเขียน Language Spec สำหรับ gtksourceview ดูได้จาก
Language Definition v2.0 Tutorial
Language Definition v2.0 Reference

ref : http://wiki.linuxcnc.org/cgi-bin/emcinfo.pl?action=browse&diff=3&id=Highlighting_In_Gedit

scrum lecture note

scrum -> sprint

daily meeting = = =
accomplish ?
going to do ?
help ?

evaluation

100 points = 100 hours

how many points accomplish ?

how many points move to next sprint ? or put another point

XP – focus more about coding

scrum


XP

planning

– user stories

– release planning

– iteration planning ( reflect )

– project velocity : evaluate progress in project

– retrospectives

testing

– programmer testing ( code code code )

— test-driven dev

– unit testing

— boundary testing

— error handling tests

use test driver ( test suite or set of test ) with stubs

– story testing / acceptance testing ( specify input, estimating output )

– integration testing


testing

– errors

– requirement conformance

– preformance

– indication of quality

testing strategy

testing-in-the-small : unit test

testing-in-the-large : integrate test

guidelines

product req

testing objectives

users

rapid cycle testing

formal technical review

continuous improvement

Testing

top-down

Test parent before write child by simulate child.

bottom-up

do not have concept of overall sys.

test different cluster

sandwich testing

top-down and bottom-up

Server-push connections with only Rails and Mongrel

เนื่องจาก Controller ของ rails มันมี timeout

มีอีกทางเลือกหนึ่งคือ ใช้ Mongrel HttpHandler ( Suggest โดยคุณ adam )

ตัวอย่าง

require 'active_record'

class StatusHandler < Mongrel::HttpHandler
   def process(request, response)
      id = request.params['PATH_INFO'].slice(1, 20)
      current = request.params['QUERY_STRING']

      while status(id) == current do // ถ้าค่าเหมือนเดิมก็ hold connection ไปเรื่อยๆ
         sleep 0.2
      end

      response.start(200) do |head, out| // ถ้าค่าไม่เหมือนเดิม complete ไป
         head["Content-Type"] = "text/html"
         out.write status(id)
      end
   end

   # http://localhost:3000/status/1
   # auction id = 1
   # return status

   # http://localhost:3000/status/1?100
   # เปิด connection ค้างไว้และรอจนกว่าค่าจะเปลี่ยน
   # (จะมี database queries ใน development.log แต่ไม่มี web hits )
   # ลองเปิด sql shell แล้วสั่ง “update auctions set status=101″ หลังจากนั้น connection will resolve immediately
, printing out the new value.
   def status(id)
      connection.select_value("select status from auctions where id=#{id.to_i}")
      # สมมติว่า มีตาราง auctions( status:integer ) อยู่แล้วนะ
   end

   def connection
      ActiveRecord::Base.connection
   end
end

uri "/status", :handler => StatusHandler.new, :in_front => true

เรายังสามารถเขียน ajax ให้เรียก

http://localhost:3000/status/1?100

ได้ ตามนี้

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
   <%= javascript_include_tag :defaults %>
</head>
<body>
   Status is now: <span id="status"></span>

   <script language="javascript">
      function respawn()
      {
         new Ajax.Updater('status', '/status/1?' + $('status').textContent, { onComplete: respawn }); // พอ complete แล้ว ให้เปิด connection ไปที่ server ใหม่แล้วรอค่าเปลี่ยนอีกครั้ง
      }

      respawn();
   </script>
</body>
</html>

เรียกได้ว่าเป็น comet แบบแปลกๆ แทนที่จะไป handle timeout ที่ client กลับไปทำให้ server ไม่มี timeout ซะงั้น

แล้ววิธีที่ส่ง request เป็น long polling ไม่ใช่ comet นะผมว่า

นอกจากนี้ rails ยังมีทางเลือกอื่นๆ ( ที่น่าจะดีกว่า ) โดยมี plugin ที่น่าสนใจ คือ

orbited (python), shooting_star(Ruby), Juggernaut (flash-base)

ref :

http://adamac.blogspot.com/2007/11/comet-on-rails.html

SSL on RAILS

ก่อนอื่นอย่าลืมลง SSL ก่อน ตามนี้

$ script/plugin install ssl_requirement

application.rb —

include SslRequirement

def ssl_required?
  return false if RAILS_ENV == 'development' || RAILS_ENV == 'test'
  super
end

user_controller.rb —

ssl_required :login, :register # :all ก็ได้นะ
ssl_allowed :index # คือจะใช้ ssl ในหน้า index หรือไม่ใช้ก็ได้

XP

>> Values ( why ? )

communication : knowledge sharing, effective cooperation

simplicity : defer req. from today concern, reduce communication

feedback : reduce gap customer, developer; the sooner you know, the sooner you can adapt; Slow down released until u can handle defect reports; feedback is critical part of commu and contributes to simplicity;

courage : increase meaningful commu, discard failing sol and seek for new one -> simplicity

respect : no one can intrincically worth more than anyone else.

>> Principles ( How ? generally )

Humanity : XP meets personal needs; commu meets human needs for connection; rest, exercise, socialization;

Economics : produce biz value

Mutual benefit Activities should benefit all concerned;

doc –> automated test test-first programming
not benefit me now

refactor –> simplicity
coherent name

self similarity : reuse the sol that work, to Simplify design, less stress, more feedback

i.e. list the themes ( address stories ) -> list the stories ( address test ) -> list the tests

improvement : do your best today, better tomorrow; perfect(v) your process, design, stories; refine result overtime

diversity : team needs variety of skill, views; difference is opportunity, not a problem; ‘two ways to solve this’

reflection : how they are working/fail ? why they are working/fail ?; learn, analyze from errors.

flow : = deliver steady flow

opportunity : problem -> opportunity to change; learning + improvement, growth + deepening relationship;

redundancy : solve problem in multiple ways; defects cannot be solved with single process

failure : failure -> valuable knowledge

quality : is not an excuse for inaction; quality is not economic factor.

baby steps : small, safe steps; In term of change, overhead less than recovery from large step

accepted responsibility solving problem, not just complete the list of task

responsibility accepted, not assigned; with responsibility comes authority.
reduce misalignment between actions and consequence

>> Practices ( How ? specifically )

> Planning
weekly cycle : reduce time spent on planning, planning is a form of necessary waste.

; task create sense of ownership

; take task top of stack –> variety of task

quarterly cycle : bottleneck repairs, theme
— sync with other biz activity

slack ( เฉื่อย ) : include slack time; maintain trust for commitment

stories : customer-visible unit of functionality;

estimate time to implement;

priority req

> Programming and integration
energized work : work reasonable hours. prevent burnout;
remove distraction for a period. i.e. email, phone, chat

incremental design
design system every day
small, safe steps
keep cost of change low

test-first programming
keep test small but meaningful
avoid scope creep
loosely coupled, highly cohesive code is easy to test

maintain trust in code when pass to another person

continuous testing

ten minute build
– automatically build and run test in 10 minutes.
– easier, less stressful because it is automatically

continuous integration
– integrate + test change – hourly
– synchronous(integrate after each pair-programming), asynchronous(daily build)
– if there are problem, notify by email

> Team work
pair programming :
– rotate parter frequenly _ every hour to ส่งต่อ knowledge
– respect personal space
– maintain pace when one person stuck
– if wanna work with own idea(not code) alone, do it then come back and check with team.

sit together :
– open space i.e. conference room
– most problems resolved by interaction

whole team :
– maintain cross-functional team ( all skill & perspective )
– build sense of team ( we belong; we support each other, growth and learn
– expert in team when needed

– 12 is no. of ppl who can interact in a day
– fracture –> team of team

informative workspace :
– stories on a wall
– big visible chart
– external memory
– observer get general idea in 15 s.
– active information –> stop getting update -> take it down

>> getting start
— one step at a time

>> Corollary Practices

> Programming

single code base : you can develop temporary branch, but never let it live longer than an hour.

shared code : Anyone can improve any part of system any time. collective ownership.

code & test : as a permanent artifact. generate other docs from the code and test.

>Biz

negotiated scope contact : To reduce risk, sign short contact instead of long one

pay-per-use : the team know the number of customer that continue to subscribe.

Pay-per-release reduce communication and feedback.

daily deployment : put new SW into production every night. Deployment tools must be automate.( roll out and roll back in case of failure)

> Team

team continuity : work with those they know and trust

shrinking(ทำให้เล็กลง) teams : The fifth person might work 30% of the time, so he can think about how to improve their work process.

until some of the team member are idle, then shrink the team and continue.

root cause analysis : the team will never make same mistake again. five why. ( almost always a people problem )

process for responding to defect

1. automated system level test

2. unit test

3. fix system so the unit test work

4. figure out why the defect was created and wasn’t caught. Initiate the necessary changes to prevent this kind of defect in the future.

real customer involvement : reduce waste effort

incremental deployment : Big deployments have a high risk, high human and economic costs


>> The Whole XP team

Testers : write automated test.

The role shift earlier, help define and specify what will constitue acceptible function before implemented.

Once tests for the week are written and failing, testers continue to write new test.

Interaction Designer : work with customers, help to write and clarify stories.

Architect : system-level tests that stress architecture. improve stress test until system broke. partition system.

Project Manager : coordinating communication with customer, supplier, executive and the rest of org.

Increase cohesiveness and confident. remind the team how much progress it has made.

Product Manager : pick themes and stories in the quarterly cycle. make sure that customer concerns are acted on the team.

Executive : encouraging, face of criticism, prepared the firm

Technical Writer : 1. provide early feedback about features.  2. create closer relationships with users. address user confusion.

This week stories can be next week’s documentation tasks.

If user never look at doc, stop writing it.

document usage added to tracking.

Users : help write and pick stories.

strong relationships with the larger user community

keep in mind that they are speaking for an entire community.

Programmers : estimate stories and tasks, break stories into task

HR : 1. reviews

individual goals, individual performance

valueable employee

– act respectful

– play well with others

– take initiative

– deliver on their commitment

2. Hiring more social candidate.

>> The theory of constraint

find the constraint : make sure it is working full speed.

Work is pulled based on actual demand, not pushed based on project demand.

>> Managing Scope

Native model

time, quality (sponsor)

, price(team)

>> Testing Early, often, and automated

– Pair programming — reduce defect

– two principle to increase cost-effective

1. double-checking two distinct thought processes at the same ans

One set is written from the perspective of programmer, another set is written from the perspective of customer.

2. Defect Cost increase ( DCI ) the sooner you find a defect, the cheaper it is to fix it.

>> Designing: The value of time

design up-front instinct

when design is not clear – thought

incremental design – experience

————–

thought create more value — design sooner

exp create more value — design just enough for today

Agile

>> Manifesto

Individual and Interaction over process and tools
Working SW over comprehensive doc
Customer Collaboration over contact negotiation
Responding to change over following a plan

>> Principle

Incremental delivery : satisfy customer early, frequently and continuous delivery valuable SW

Embrace change : Welcome changing requirements, even late in development.  Agile processes harness change for the customer’s competitive advantage.

Customer involvemnet : Business people and developers must work together daily ( face-to-face conversation )

People not process

– Give them environment, support what they need, and trust them to get the job done. ( Self-organizing teams )

– Continuous attention to technical excellence and good design.

Simplicity–the art of maximizing the amount of work not done–is essential.

>> Key trait of team member

Competence, Common focus, Collaboration

Dicision-making ability, Fuzzy(เป็นฝอยๆ) problem-solving ability

Mutual trust and respect, Self-organization