Ruby on Rails :: RESTful on Rails(2)
Posted by PunNeng, Wed Jan 31 03:12:00 UTC 2007
หลังจากที่ Rails 1.2(1.2.1 แล้ว) ถูกปล่อยออกมา หนึ่งใน feature หลักๆ ก็คือ RESTful โดยอาศัย plugin(ในอดีต) ที่ชื่อ simply_restful ซึึ่งถูกเพิ่มเข้าไปใน rails-core ใน edgerails ตั้งนานแล้ว
ok มาดูกัน ว่ามันจะทำอะไรยังไง
อันดับแรก จัดการให้ rails ของเราเป็น version ล่าสุดก่อน โดยการพิมพ์ใน terminal ว่า
sudo gem install rails --source http://gems.rubyonrails.org -y
-y คือ --include-dependencies
ถ้าใน windows ก็พิมพ์ใน cmd เลย เพียงแต่เอา sudo ออก
หลังจาก update กันเสร็จแล้ว ก็สร้าง project กันเลย โดยพิมพ์ว่า
yourpath$ rails project_name
แล้วไปสร้าง db ก่อน ผมก็สร้างเอาใน phpmyadmin(ใน MAMP) เนี่ยแหละ ง่ายดี(ปกติผมใช้ migration ครับ ไว้คราวหน้า จะมาเล่าให้ฟัง) สร้างชื่อว่า "stock_development" แล้วใส่ sql command ว่า
CREATE TABLE products ( id int(11) NOT NULL, name varchar(255) NOT NULL, description text NOT NULL, PRIMARY KEY (id) ) |
แล้วก็ไปจัดการใน database.yml ให้เรียบร้อย(ตัวอย่าง)
จากนั้นก็เข้าไปใน project พิมพ์ต่อใน terminal ว่า
project_name$ ruby script/generate scaffold_resource product
มันก็จะสร้างชุด file มาชุดนึง ซึ่งเมื่อก่อนที่เคยใช้คือ generate scaffold เฉยๆ ซึ่งตัวใหม่นี้ จะทำการสร้างชุด CRUD มาให้พร้อมใช้เลย ลองไปไล่เปิด file ดูนะครับ โดยส่วนใหญ่จะอยู่ที่ controller
หลังจากน้ัน ไปที่ config/routes.rb ลองดูว่ามันทำการ map url ยังไง
map.resources :products |
มีแค่นี้เอง แค่ใส่ให้เป็น symbol แบบ plural แล้วมันก็จะมีชุด url ที่เคยให้ดูในครั้งที่แล้ว โดยสร้างจาก Net::HTTP หน้าตาก็จะประมาณนี้ (ติ๊งต่างว่า messages คือ products ละกันครับ ผมก็ copy มาอีกที)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | Net::HTTP.start("localhost", 3000) do |http| # retrieve all messages response = http.get("/messages") # return an HTML form for defining a new message response = http.get("/messages/new") # create a new message response = http.post("/messages", "...") # retrieve message #1 response = http.get("/messages/1") # return an HTML form for editing an existing message response = http.get("/messages/1;edit") # update an existing message response = http.put("/messages/1", "...") # delete an existing message response = http.delete("/messages/1") end |
แล้วก็จะมี options อีกต่างๆ นานา เช่น
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | map.resources :messages, :path_prefix => "/thread/:thread_id" # --> GET /thread/7/messages/1 map.resources :messages, :collection => { :rss => :get } # --> GET /messages;rss (maps to the #rss action) # also adds a url named "rss_messages" map.resources :messages, :member => { :mark => :post } # --> POST /messages/1;mark (maps to the #mark action) # also adds a url named "mark_message" map.resources :messages, :new => { :preview => :post } # --> POST /messages/new;preview (maps to the #preview action) # also adds a url named "preview_new_message" map.resources :messages, :new => { :new => :any, :preview => :post } # --> POST /messages/new;preview (maps to the #preview action) # also adds a url named "preview_new_message" # --> /messages/new can be invoked via any request method map.resources :messages, :controller => "categories", :path_prefix => "/categories/:category_id", :name_prefix => "categories_" # --> GET /categories/7/messages/1 # has named route "categories_messages" |
- :path_prefix ก็คือส่วนที่เราอยากจะให้อยู่ข้างหน้า url ดูตามตัวอย่างไปเลยนะครับ
- :collection ก็คือ action(method ใน controller) ที่เราอยากจะเพิ่มเข้าไป แต่ข้อสังเกตคือ ใช้กับ index เท่าน้ัน ซึ่งส่วนใหญ่ index จะทำหน้าที่ list all item ก็หมายความว่าเป็น action ที่ทำกับ resource รวมๆ โดยระบุ method เข้าไปได้ด้วย แล้ว named route ก็จะถูกสร้างเอาไว้ใช้ใน controller และ view1 โดยจะมีชื่อว่า "rss_messages" (option ต่อไปดู named route จากตัวอย่างละกันครับ)
- :member อันนี้ก็เหมือนกับ :collection เพียงแต่มันจะไปกระทำกับ resource ตัวใดตัวหนึ่ง เช่นในตัวอย่าง มันจะไปพิจารณาตัวที่มี id เป็น 1
- :new อันนี้เหมือนกับ :collection เหมือนกัน แต่ไว้จัดการกับ resource ของ new
- อีกรูปนึงของ new แต่ว่าอันนี้ จะทำการเซ็ตค่าเพิ่มหน่อย ตรงที่ new จะถูกเรียกจาก request อันไหนก็ได้(ปกติ new จะเรียกได้จาก get อย่างเดียว)
- :controller ใช้เซ็ตตัว controller ให้กับ resource ของเรา เช่น ถ้าเซ็ตให้
เวลาเรียก /categories ก็จะเสมือนว่าเรียก /productsresources :categories, :controller =>"products"
- :singular ไว้เซ็ตค่า singular กับ named route เช่น :singular => "pd" ตัว named route ของ product ที่เป็น singular จะถูกเปลี่ยนไปตามที่เซ็ตไว้ ถ้าเป็น new จะเป็น new_pd ถ้าไม่เซ็ตค่านี้ ค่า default ของมันจะเป็น new_product ซึ่งไว้ใช้ในกรณีที่รูปของ singular กับ plural มันตรงกัน เช่น sheep หรือ fish หรือ news
- :name_prefix ก็เอาไว้ใส่ prefix หน้า named route (ดูจากตัวอย่างได้เลย)
กลับมาที่ terminal กันต่อ สั่ง run server เลย
project_name$ ruby script/server
มาลองเริ่มต้นที่ /products ดูก่อน ลองจิ้มไปเรื่อยๆ จะพบว่า form มันหายไป อันนี้เป็นหน้าที่เราแล้ว ที่ต้องไปใส่เอง ไปที่ app/view/products/index.rhtml แล้วเพิ่ม
1 2 3 4 5 6 7 | ... <tr> <td><%= product.name %></td> <td><%= product.description %></td> <td><%= link_to 'Show', product_path(product) %></td> ... </tr> |
อันนี้เข้าไป ลองสังเกตดูนะครับ จะมี product_path อยู่ ซึ่งเป็น named route จะทำการสร้้าง url เป็น /products/:id
แล้วสร้าง _form.rhtml ในนี้แหละ
<p><label for="product_name">Name:</label><br /> <%= form.text_field "name" %></p> <p><label for="product_description">Description</label><br /> <%= form.text_area "description:" %></p> |
เนื่องจากมันถูกสร้างมาในฟอร์มของ FormBuilder เลยต้องสร้างในรูปของ FormBuilder ต่อไป โดยใช้ form.text_field และ form.text_area
จากนั้นต่อกันด้วย new.rthml และ edit.rhtml
<%= render :partial => 'form' , :object => f %>
|
โดยเพิ่มไประหว่าง form_for และ submit_tag ทั้ง new.rhtml และ edit.rhtml ใน edit.rhtml ลองดูใน form_for ดู จะเจอ :method => :put อยู่ เนื่องจากมันมีปัญหากับ browser เลยต้องใช้วิธีนี้เซ็ตเอา โดยมันจะเพิ่ม hidden tag เข้าไป
<input name="_method" type="hidden" value="put" />
|
พอ submit มันก็จะไปเพิ่ม ?_method=put เข้าไป แล้วมันจะมีกลไกต่อเอง โดยมันจะตรวจจาก _method นี้แล ไปต่อที่ show.rhtml
<%= @product.name %> <%= @product.description %> |
เพิ่ิมอันนี้เข้าไป
จากนั้นมาลอง test กันดูใหม่ ลองเพิ่มลอง update ดู
ย้อนกลับมาที่ index.rhtml ในส่วนของ delete จะเจอ
<td><%= link_to 'Destroy', product_path(product), :confirm => 'Are you sure?', :method => :delete %></td> |
เราจะเห็นได้ว่ามีการเซ็ต :method => :delete เพื่อเป็นการบอก method ถ้าเรา view source จะเจอ js อยู่ก้อนนึง เป็นตัวเซ็ต delete method ซึ่งจะทำการสร้าง form มาตัวนึง แล้วเซ็ต method เข้าไป เหมือนกับของ put แต่ว่าอันนี้ใช้ js เป็นตัวสร้าง
ถ้าต้องการเพิ่มเติม ไปที่ api ของ rails ได้เลย
ปล. ติดไว้หลายเรื่องแฮะ จดไว้ก่อน มี migration, route file, respond_to(ลองแงะเข้าไปดูใน controller ดูครับ)
1 เป็นลักษณะเหมือน alias ตัวที่ถูกสร้างขึ้นมา สามารถใช้ใน controller ได้ เช่น redirect_to rss_messages_path หรือฝั่ง view เช่น link_to "rss", rss_messages_path ซึ่ง path จะสามารถเปลี่ยนเป็น url ได้ ถ้า view source จะเห็นเป็น url เต็มๆ แปะเรื่อง route ไว้ก่อน แล้วจะมาเจอกันเต็มๆ
แก้ไขล่าสุด วันที่ 27 กรกฏาคม 2550 เวลา 3.32 น.