rails on mysql

หลังจากลง mysql-server, mysql-client, libmysqlclient15-dev

$ gem install mysql
$ mysql -u root -p
GRANT ALL PRIVILEGES ON *.* TO 'dsin'@'localhost'

1. dump data from .sql using rake

lib/tasks/sample_data.rake

# Provides task to load sample data.

require 'active_record'
require 'active_record/fixtures'

namespace :db do
DATA_DIRECTORY = "#{RAILS_ROOT}/lib/tasks/sample_data"
namespace :sample_data do

desc "Load sample data"
task :load => :environment do |t|
chdir DATA_DIRECTORY
system 'mysql lectures_development < lectures.sql'
end
end
end
$ rake db:sample_data:load

2. ลง dojo-release-1.2.1.tar.gz ไปที่ public/javascript

/layouts/application.html.erb —

<%= stylesheet_link_tag "/javascripts/dojox/grid/resources/Grid.css" %>
<%= stylesheet_link_tag "/javascripts/dojox/grid/resources/tundraGrid.css" %>
<%= stylesheet_link_tag "/javascripts/dijit/themes/tundra/tundra" %>
<%= stylesheet_link_tag "/javascripts/dojo/resources/dojo.css" %>
<%= javascript_include_tag 'dojo/dojo', :djConfig => 'isDebug:false, parseOnLoad:true' %>

3. ถ้า javascript turn off จะไม่ทำใน noscript

<noscript>

<table> ... </table>

</noscript>

4.

dojo.data.ItemFileWriteStore

structure -> HTML table

—————————————-

<script type=”text/javascript”>
dojo.require(“dojo.data.ItemFileWriteStore”);

dojo.require(“dojox.grid.DataGrid”);

dojo.require(“dojox.grid.cells”);

var gridStore = new dojo.data.ItemFileWriteStore({
url: ‘/courses/<%= @course.id %>/lectures.json’,   # ajax ไปเอาข้อมูล JSON มาจาก URL นี้ แล้วโหลดเข้า structure ( Model )
id: ‘gridData’
});
var grid = null;

////////////////////////////////////////////////////////////////////////////

function saveGrid(){
gridStore.save();
}


// extend dojo.data.ItemFileWriteStore

dojo.declare(“CustomItemFileWriteStore”, dojo.data.ItemFileWriteStore, {
_saveCustom: saveCustom,
_sendUpdate: sendUpdate,
_formEncode: formEncode,
});


var saveCustom = function (saveCompleteCallback, saveFailedCallback) {
var success = true;
if (this.isDirty()) {
if (!this._isEmpty(this._pending._newItems)) {
success = this._sendCreate(this._pending._newItems);
}
if (success && !this._isEmpty(this._pending._deletedItems)) {
success = this._sendDelete(this._pending._deletedItems);
}
if (success && !this._isEmpty(this._pending._modifiedItems)) {
for (identity in this._pending._modifiedItems) {
var modifications = null;
// identity is the ORIGINAL value; we need the modified items too
if (this._itemsByIdentity) {
modifications = this._itemsByIdentity[identity];
} else {
modifications = this._arrayOfAllItems[identity];
}
success = this._sendUpdate(identity,modifications);
}
}
}
if(success) {
saveCompleteCallback();
} else {
saveFailedCallback();
}
}


function formEncode(item) {
var id = ‘lecture[id]=’ + item.id;
var dates = ‘&lecture[lecture_dates]=’ + escape(item.lecture_dates);
var number = ‘&lecture[lecture_number]=’ + escape(item.lecture_number);
var topics = ‘&lecture[topics]=’ + escape(item.topics);
var readings = ‘&lecture[readings]=’ + escape(item.readings);
var auth = ‘&’ + window._auth_token_name + ‘=’ + window._auth_token;
return id + dates + number + topics + readings + auth;
}


var sendUpdate = function(origItem, newItem){
var putData = this._formEncode(newItem);
return dojo.rawXhrPut({
preventCache: true,
url: “<%= course_lectures_path( @course ) %>/” + origItem + ‘.json’,
handleAs: “text”,
putData: putData,
sync: true,
timeout: 10000,
load: function(response, ioArgs){
return true;
},
error: function(response, ioArgs){
var errors = eval( ‘(‘ + response.responseText + ‘)’ );
alert(‘Error:’ + errors.base);
return false;
}
});
}

var gridStore = new CustomItemFileWriteStore({
url: ‘/courses/<%= @course.id %>/lectures.json’,   

id: ‘gridData’

});

///////////////////////////////////////////////////////////////////////////

dojo.addOnLoad( function() {
// Add style to the gridContainerOuter
var gridContainerOuterNode = dojo.byId(“gridContainerOuter”);
gridContainerOuterNode.setAttribute(“style”, “background:maroon; padding:0.5em; width:50em;”);
// Add style to the gridContainerInner
var gridContainerInnerNode = dojo.byId(“gridContainerInner”);
gridContainerInnerNode.setAttribute(“style”, “background:#fff; height:350px;”);
// Add style to the gridNode
var gridNode = dojo.byId(“gridNode”);
gridNode.setAttribute(“style”, “border: 1px solid #333;”);
// Create the column layout for the grid
var layout = [
{name: “Lecture number”, field: “lecture_number”, width: “3em”, editable: true, type: dojox.grid.cells.Editor },
{name: “Dates”,          field: “lecture_dates”,  width: “5em”, editable: true , type: dojox.grid.cells.Editor },
{name: “Topics”,         field: “topics”,         width: “35%”, editable: true , type: dojox.grid.cells.Editor },
{name: “Readings”,       field: “readings”,       width: “35%”, editable: true , type: dojox.grid.cells.Editor },
];

// editable: true — double click on cell and can edit

//  type: dojox.grid.cells.Editor — rich-text html editor

// ( dojo.require(“dojox.grid.cells”); )


// Create the grid
grid = new dojox.grid.DataGrid({
query: { id: ‘*’ },   #
store: gridStore, #
structure: layout
}, ‘gridNode‘);
grid.startup();

// Create the save button , callback to saveGrid()
saveButton = new dijit.form.Button({label:’Save changes’, onClick:saveGrid});
dojo.byId(‘scheduleGrid’).appendChild(saveButton.domNode);


});
</script>
<div id=”scheduleGrid”>
<div id=”gridContainerOuter”>
<div id=”gridContainerInner”>
<div id=”gridNode“></div>
</div>
</div>
</div>

5. extend ActiveRecord::Errors

application.rb —

class ActiveRecord::Errors
def to_json
@errors.to_json
end
end

how to update gem

หลังจากเจอปัญหา

Rails requires RubyGems >= 1.1.1 (you have 0.9.4). Please `gem update –system` and try again.

$ sudo gem update --system

$ sudo gem install -v=2.1.1 rails

$ gedit /usr/bin/gem
require 'rubygems'

require 'rubygems/gem_runner'  ## -- add this line in

ใน environment.rb แก้

RAILS_GEM_VERSION = '0.9.4'

เล่น 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/

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 หรือไม่ใช้ก็ได้

AJAX in Rails

ใน view ( จริงๆ ยัดเข้าไปใน view/layout/application.html.erb ได้ด้วย )

<%= javascript_include_tag :defaults %>
# include javascript [ Prototype and Scriptaculous ]

<script src="/javascripts/prototype.js?1223017864" type="text/javascript"></script>
<script src="/javascripts/effects.js?1223017864" type="text/javascript"></script>
<script src="/javascripts/dragdrop.js?1223017864" type="text/javascript"></script>
<script src="/javascripts/controls.js?1223017864" type="text/javascript"></script>
<script src="/javascripts/application.js?1223017864" type="text/javascript"></script>

Sample Prototype code

ใส่ลงไปใน public/javascripts/application.js ก็ได้

function serverSideAlert() {
new Ajax.Request( '/xhr_test/myresponse', {
method: 'get',
onSuccess: function( request ) {
alert( request.responseText );
}
})
}
<a href="#" onclick="serverSideAlert()">Server side alert()</a>

หรือ มันมี function ไว้ ส่ง XHR และ filling in a DOM element ให้เลยนะ ( .innerHTML )

new Ajax.Updater( 'HTMLid', '/xhr_test/myresponse', { method: 'get' })

หรือ ใส่ append แทนที่จะเซต .innerHTML

insertion: ‘bottom’

new Ajax.Updater( 'HTMLid', '/xhr_test/myresponse',
{ insertion: 'bottom', method: 'get' })

อีกตัวอย่างหนึ่ง

evalScripts:true

new Ajax.Request('/ajax_test/cityforzip', {method:'get', asynchronous:true, evalScripts:true, parameters:'zip='+$('postalcodeInput').value});

evalScripts คือ ให้ execute response เป็น script

Sample Rails code

link_to_remote คือ สร้าง a ไปเรียก ajax แทนที่จะ refresh page

# Ajax:Request()

<%= link_to_remote “Inline alert”, :url => { :action => :myresponse },
:success => “alert( request.responseText )” %>

# Ajax:Updater(), set innerHTML ของ HTMLid เป็น response

<%= link_to_remote “Inline alert”, :url => { :action => :myresponse },
:update => :HTMLid %>

<p id=”HTMLid”></p>

# append rather than .innerHTML

<%= link_to_remote “Inline alert”, :url => { :action => :myresponse },
:update => :HTMLid,
:position => ‘bottom’ %>

วิธีเขียน callback
:before, :complete (XHR state 4), :success, and :failure

<%= link_to_remote “Check time”, :url => { :action => :get_time },
:update => :current_time, :before =>”$(‘indicator’).show()“, :complete => “$(‘indicator’).hide()“, :success => “$(‘current_time’).visualEffect(‘highlight’);”, :failure => “alert(‘There was an error.’)” %>

NOTE .visualEffect คือ scriptaculous visual effect

<span id=”indicator” style=”display: none;”>Loading…</span>

<p id=”current_time“></p>

NOTE : XHR ย่อมาจาก XML HTTP request

rjs

เก็บไว้ที่ view/controllerName/actionName.rjs

page[:cityInput].value = ‘test’  แปลเป็น javascript ได้เป็น $(‘cityInput’).value = “test”

มันใช้ตัวแปรของ rails ได้ด้วย เหมือน view ทุกอย่าง

page[:cityInput].value = @zipcode.city

Ajax autocomplete

ลอง request ไป http://www.geonames.org

เราสามารถส่ง postalcode ไปยัง geonames.org เพื่อเอาค่ากลับมาได้ เช่น

http://ws.geonames.org/postalCodeLookupJSON?postalcode=12120&country=TH

ดู reference ได้ที่

http://www.geonames.org/export
1.

XMLHttpRequest -> only allows connections to original server

ถ้าจะเรียก ajax ข้ามเครื่อง ให้ใช้ JSONscriptRequest ของ Yahoo! นั่นเอง

( JSONscriptRequest add javascript tag to the DOM ไม่ได้ เรียก ajax ข้ามเครื่อง )

Download Levitt’s JSONscriptRequest class definition from http://developer.yahoo.com/javascript/samples/dynamic/jsr_class.js

2. Add <script> tags to import jsr_class.js and http://www.geonames.org/export/geonamesData.js

3. onblur = “postalCodeLookup”

// This function is called when the user leaves the postal code input field.
// It calls the geonames.org JSON Web service to fetch an array of places for
// the given postal code
function postalCodeLookup() {
var country = escape( $( ‘countrySelect’ ).value );
// Check if country is supported

## geonamesPostalCodeCountries เป็นตัวแปรอยู่ใน geonamesData.js

if (geonamesPostalCodeCountries.toString().search(country) == -1) {
alert(‘Sorry, geonames.org does not support lookups in country ‘ +
country + ‘.’);
return;
}
var postalcode = escape( $(‘postalcodeInput’).value );
// Display “loading” message in search status box
$( ‘searchStatusElement’ ).show;
$( ‘searchStatusElement’ ).innerHTML =
‘<small><i>searching for postal code ‘ + postalcode + ‘ in ‘ +
country + ‘…</i></small>’;
// Create a new script object
var request = ‘http://ws.geonames.org/postalCodeLookupJSON?postalcode=&#8217; +escape( postalcode ) + ‘&country=’ + escape( country ) +’&callback=extractLocation‘;

// callback ใช้โดย JSONscriptRequest

// มันจะเรียก extractLocation({“postalcodes”:[{“postalcode”:”12120″,”countryCode”:”TH”,”placeName”:”Khlong Luang”}]});
aObj = new JSONscriptRequest( request );
// Build the <script> tag point to that url

aObj.buildScriptTag();
// adds the new element to the DOM, the browser fetches the JavaScript code from the remote server and executes it.

aObj.addScriptTag();
}
////////////////////////////////////////////////////////////////////////////////////////////////

// This function will be called when the JSON response arrives from the
// server. The parameter jData will contain an array with postalcode
// objects.
function extractLocation( jData ) {
$( ‘searchStatusElement’ ).innerHTML = ”;
$( ‘searchStatusElement’ ).hide;
if (jData == null) {
alert( ‘There was a problem looking up your postal code.’ );
return;
}
if (jData.postalcodes.length >= 1) {
$(‘cityInput‘).value = jData.postalcodes[0].placeName;
$(‘stateInput‘).value = jData.postalcodes[0].adminName1;
} else {
$(‘cityInput’).value = ‘UNKNOWN’;
$(‘stateInput’).value = ‘UNKNOWN’;
}
}