Displaying articles with tag ruby on rails

Ruby on Rails :: Fragment Caching

Posted by PunNeng, Sun Nov 09 22:19:00 UTC 2008

fragment caching เป็น post ต่อจาก page caching กับ action caching

ชื่อมันก็บอกอยู่แล้วว่าทำ cache แบบเป็นชิ้นๆ สิ่งที่นิยมทำ cache แบบนี้คือการ render เป็นส่วนๆ ในฝั่งของ html template ตัวอย่าง

  1
  2
  3
  4
  5
<b><%= @title %></b>
<% cache do %>
  a lot of text or ruby expression hear and it is 
  <%= render :partial => "topic", :locals => {:time => Time.now } %>
<% end %>

ในที่นี้ มันจะ cache ใน block ที่กำหนดไว้ โดยค่า default ในการอ้างถึงจะเป็นชื่อ action และ controller เช่น ถ้าจะ clear cache ก้อนนี้ จะเรียกใช้

expire_fragment(:controller => "topics", :action => "list")

ในความเป็นจริงแล้ว เราอาจจะมี cache หลายที่ในหนึ่งหน้า เลยมี options เพิ่มมา สำหรับกำหนดชื่อให้มันอีก

  1
cache(:action => "list", :action_suffix => "time")

ตัว action_suffix จะเป็นสิ่งที่ทำให้เราสามารถเข้าถึง cache ก้อนนี้ได้ เช่น

expire_fragment(:controller => "topics", :action => "list", :action_suffix => "time")

แล้วมันเอาไปเก็บไว้ที่ไหน??
จริงๆ ผมเขียนไปในตอนที่แล้วแล้ว ว่ามันเก็บไว้เป็น hash โดยใช้ ActiveSupport::Cache::MemoryStore เป็นตัวบันทึก

รายละเอียดเพิ่มเติมดูได้ใน API ได้เลย

0 comments | Filed Under: Ruby on Rails | Tags: ruby on rails

Ruby on Rails :: Action Caching

Posted by PunNeng, Sun Oct 26 21:09:00 UTC 2008

ต่อจากคราวที่แล้ว

ใน API ของ rails เอง เขียนอธิบายไว้ว่า มันไม่ได้ต่างอะไรกับ page caching มาก แต่ผมว่ามันค่อนข้างต่างเลยแหละ ถ้าจะพิจารณาการบันทึกข้อความสำหรับ cache

เรื่อง configuration ก็เหมือนกับ page caching ครับ จะข้ามไป

แล้ว Action Caching ใช้ตอนไหน ??
ก็ใช้ตอนที่เราต้องใช้ filter เช่น before_filter หรือ after_filter
ตัวอย่างที่เขาใช้อธิบายใน API คือ เรือ่ง authentication ใน code หน้าตาจะประมาณนี้

  1
  2
  3
  4
  5
class ListsController < ApplicationController
    before_filter :authenticate, :except => :public
    caches_page   :public
    caches_action :index, :show, :feed
end

จากตัวอย่างข้างบน จะมีการ authentication ที่ action index, show และ feed ถ้าหากไม่ใช้ caches_action แต่ไปใช้ caches_page แทน มันจะเขียน file.html แล้วมันจะไม่วิ่งเข้า before_filter :authetication
ทางแก้ก็คือ ใช้ caches_action แทน

โดยการเก็บ cache มันจะไปใช้ความสามารถของ Fragment Cache แทน สำหรับ Fragment Cache ไว้ตอนหน้าครับ ซึ่งมันจะเก็บ cache ไว้ที่ Hash ไว้ใน ActiveSupport::Cache::MemoryStore ครับ ถ้าเรา restart server มันก็จะหายไป ไม่ได้เก็บถาวรเหมือน Page Caching

ในการ clear action caching ก็ไม่ได้ซับซ้อนอะไร เพียงแค่

  1
expire_action :controller =>"lists", :action => "index"

นอกจากนี้ยังใส่เงื่อนไขได้ด้วย เช่น

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
class ListsController < ApplicationController
    before_filter :authenticate, :except => :public
    caches_page   :public
    caches_action :index, :if => Proc.new { |c| !c.request.format.json? } # cache if is not a JSON request
    caches_action :show, :cache_path => { :project =&gt; 1 }
    caches_action :feed, :cache_path => Proc.new { |controller|
      controller.params[:user_id] ?
        controller.send(:user_list_url, c.params[:user_id], c.params[:id]) :
        controller.send(:list_url, c.params[:id]) }
end

ข้อมูลเพิ่มเติมดูได้ที่ API เลยครับ

0 comments | Filed Under: Ruby on Rails | Tags: ruby on rails

Ruby on Rails :: Page caching

Posted by PunNeng, Mon Oct 06 01:17:00 UTC 2008

รีบปั่นก่อน ก่อนที่ merb 1.0 จะออกมา เดี๋ยวจะไม่ได้เขียน blog สักเท่าไหร่ เพราะคงไปนั่งวุ่นอยู่กับ merb นี่แหละ

post อันนี้ คงจะอยู่ทั้ง blog ผม และ rails66.com ด้วย เขียนทีเดียว แปะมันทั้งสองที่

วิธีการทีจะลดการทำงานของ server ได้ หนึ่งวิธีในนั้นที่ช่วยได้เยอะเลยคือการทำ cache
ใน Rails เองมีการทำ cache อยู่ 4 ระดับ คือ

  • page caching
  • action caching
  • fragment caching
  • sql caching

และตัวควบคุมการลบ cache พวกนี้ ก็คือ

  • sweeper

อันแรกนี้ ขอเป็น page caching ก่อน

ซึ่ง page caching เนี่ย มันจะเก็บ output ไว้เป็น file โดย save ไว้เป็น html เลย โดยเก็บไว้ใน public/ ซึ่งสามารถเปลี่ยนที่เก็บได้ เดี๋ยวค่อยว่ากันอีกที เมื่อมันมี request มา มันจะวิ่งเข้าที่ file นี้แทน แทนที่จะวิ่งไปที่ controller แล้วก็ทำงานตามที่เราเขียนไว้

การทำ page caching นี้ เหมาะสำหรับอะไรที่มันอยู่นานๆ แล้วคงจะไม่เปลี่ยน เช่นระบบ blog ซึ่งปกติ จะไม่ค่อยแก้ไขอะไรเท่าไหร่ จะทำการลบ cache นี้ออก ก็ต่อเมื่อมีการ update หรือมีการเพิ่มข้อคิดเห็น(comment) เป็นต้น

เริ่มต้นด้วยการเปิด cache ก่อน โดยปกติ ถ้าเราทำงานใน development environment มันจะปิดระบบ cache ทั้งหมด แต่ถ้าสลับมาเป็น production environment มันจะทำการเปิด cache ทุกอย่าง ลองดูใน config/environments/production.rb

  1
config.action_controller.perform_caching = true

ไปตั้งค่าให้เป็น true ใน config/environments/development.rb มันก็จะไปเปิดตัวจัดการ cache ให้

สมมติว่าถ้าต้องการเปลี่ยน path ในการเก็บ cache ก็ใส่เพิ่มไปว่า

  1
config.action_controller.page_cache_directory = RAILS_ROOT + "/public/wherever/you/want/"

เวลาเข้าถึงก็ http://localhost:3000/wherever/you/want/file

วิธีเซ็ต page caching ก็ง่ายๆ เราใส่ไปใน controller ว่า

  1
  2
  3
  4
  5
  6
class PostsController < ApplicationController
  caches_page :show
  def show
    @post = Post.find params[:id]
  end
end

แค่นี้เอง

เวลาจะลบ cache สมมติว่าเราจะลบ หลังจากที่เรา update content ของเรา เราก็ใส่ใน controller ไปว่า

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
def update
  @post = Post.find params[:id]
  if @post.update_attributes params[:post]
    flash[:notice] = "Updated"
    expire_page :action => "show", :id => @post.id
    redirect_to :action => "index"
  else 
    render :action => "edit"
  end
end

expire_page จะไปทำการลบให้เองเลย สบายมากๆ (รออีกสักหน่อย เดี๋ยวจะเขียนเรือง sweeper มาจัดการตรงนี้ครับ)

แต่ก็ใช่ว่าจะไม่มีปัญหาเลย เพราะหลังจาก update เสร็จแล้ว เราควรที่จะแสดงข้อความอะไรบอกหน่อย ในที่นี้ผมใส่ไปใน flash[:notice] ปัญหามันคือ มันจะเก็บ cache ไว้ในขณะที่มีข้อความนี้อยู่บนหน้า เพราะมันจะเก็บตอนที่เราสั่ง redirect_to

วิธีซ่อมแบบไม่ลงแรงเลยคือ ใช้ plugin ที่ชื่อว่า Cacheable Flash

เอามาจาก นั่งเทียนเขียนข่าว#12 ครับ

โดยวิธีการมันก็ง่ายๆ ครับ คือเก็บข้อความไว้ใน cookie ก่อน แล้วก็ดึงมาแสดงผลด้วย javascript อีกที
แต่ผมไม่ค่อยชอบ น่าจะมีตรวจว่ามี flash หรือเปล่า ก็เลยลงมืองัดแงะปลุกปล้ำเสียหน่อยได้หน้าตาแบบนี้

  1
caches_page :show, :if => Proc.new{ |p| p.request.session.data["flash"].blank?}

คือ cache ถ้า flash message ว่าง
ดูเท่กว่าเยอะ

ปล. อาทิตย์หน้ามาต่อกับ action caching ครับ

ข้อมูลจาก rails api

1 comment | Filed Under: Ruby on Rails | Tags: ruby on rails

Modifying fieldWithErrors

Posted by PunNeng, Sat Jul 05 20:33:00 UTC 2008

fieldWithErrors เนี่ย บางทีมันก็มีประโยชน์ดีนะ แต่บางทีก็อยากจะเอาออก หรืออยากจะแก้ไข

เอาแบบนี้ดีกว่า

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|          
  if html_tag =~ /<label.*>.*<\/label>/
    "<div class=\"labelWithErrors\">#{html_tag}</div>" 
  elsif html_tag =~ /terms_and_conditions/   
    "<div class=\"tickWithErrors\">#{html_tag}</div>"
  elsif html_tag =~ /radio/
    "<div class=\"radioWithErrors\">#{html_tag}</div>"     
  else
    "<div class=\"fieldWithErrors\">#{html_tag}</div>"      
  end                            
end

ใส่ไปใน environment.rb
ใครมีความคิดอะไรก็ใส่ไปให้หมด สำหรับผมการ filter ง่ายๆ ก็ใช้ RegExp ครับ

ไม่มีกะจิตกะใจจะเขียนเท่าไหร่ กำลังทะเลาะกับ Merb อยู่ ทำไมมันติดตั้งยากเยี่ยงนี้ !!

0 comments | Filed Under: Ruby on Rails | Tags: ruby on rails

Ruby on Rails :: Customize Form helper

Posted by PunNeng, Sun Jun 29 03:00:00 UTC 2008

เพิ่งทำของเล่นเสร็จไปตัวนึง เลยไม่ได้เขียน blog

ไม่ต้องพล่ามมาก เอาเลยดีกว่า

ใน Rails มี Form helper อยู่ เราทำอะไรกับมันได้บ้าง?? ลองดูแบบนี้ก่อน

  1
  2
  3
  4
  5
  6
<% form_for :person, @person, :url => { :action => "update" } do |f| %>
  First name: <%= f.text_field :first_name %>
  Last name : <%= f.text_field :last_name %>
  Biography : <%= text_area :person, :biography %>
  Admin?    : <%= check_box_tag "person[admin]", @person.company.admin? %>
<% end %>

ปกติ ผมจะต้องใส่ label ให้มันด้วย
ผมไม่ค่อยชอบเลย ผมอยากได้

f.text_field :first_name

แล้วมันใส่ label ไปให้ผมเลย
มันทำได้โดยสร้าง module เพิ่ม ไปใว้ใน config/initializers/ (มันมาตอน rails 2.0 ออกมั้ง ทุก file ในนี้ จะถูกโหลดโดยอัติโนมัติ)

  1
  2
  3
  4
  5
  6
  7
module MyFormHelper
  class MyFormBuilder < ActionView::Helpers::FormBuilder
    def text_field(method, options = {})
      @template.content_tag('p', label(method) + <br /> + super)
    end
  end
end

ก็ extends ตามเรื่องไป(ต้องใช้พลังแงะนิดหน่อย)

ใน form ด้านบน ก็เพิ่ม :builder => MyFormHelper::MyFormBuilder
หน้าตาจะเป็นแบบนี้

  1
  2
  3
  4
  5
  6
<% form_for :person, @person, :url => { :action => "update" }, :builder => MyFormHelper::MyFormBuilder do |f| %>
  <%= f.text_field :first_name %>
  <%= f.text_field :last_name %>
  <%= text_area :person, :biography %>
  <%= check_box_tag "person[admin]", @person.company.admin? %>
<% end %>

แล้วผมก็จะได้ผลของการ render text_field เป็นแบบนี้

  1
  2
<p><label for="person_first_name">First name</label><br />
<input type="text" id="person_first_name" name="person[first_name]"</p>

อะไรประมาณนี้

0 comments | Filed Under: Ruby on Rails | Tags: ruby on rails

codegent: we're hiring