RSS là công nghệ hỗ trợ người dùng cập nhật nội dung từ website mà không cần phải vào trang web. Cách hoạt động thì đơn giản là website sẽ tạo ra một file có định dạng XML rồi trả về cho người dùng, người dùng sẽ dùng một phần mềm để đọc file đó.
RSS cũng có nhiều phiên bản khác nhau, phổ biến nhất là RSS 1.0, RSS 2.0 và Atom. Cái nào cũng có ưu/nhược điểm riêng và đều được sử dụng rộng rãi, có website dùng cả 3 phiên bản.
Riêng Ruby có thư viện hỗ trợ tạo file RSS ở 3 phiên bản trên và các phiên bản khác nhỏ lẻ khác. Rails cũng có hàm tạo RSS dựa trên thư viện của Ruby, và mặc định Rails chọn phiên bản Atom.
Chúng ta sẽ tạo một file Atom hiển thị những đơn hàng đã mua sản phẩm có id bất kỳ.
Định nghĩa Action
Đầu tiên chúng ta định nghĩa phương thức who_bought
trong lớp ProductsController
như sau:
class ProductsController < ApplicationController . . . def who_bought @product = Product.find(params[:id]) respond_to do |format| format.atom format.xml { render :xml => @product } end end . . . end
Phương thức này sẽ được gọi từ URL là /products/<id>/who_bought.atom
. Ở đây chúng ta lấy đối tượng Product
có id
tương ứng trong tham số gửi lên. Sau đó gọi hàm format.atom,
hàm này sẽ chạy đoạn code có trong file <tên_phương_thức>.atom.builder
nằm trong thư mục app/views/<tên model>
dùng để tạo dữ liệu Atom, tức là ở đây sẽ gọi tới file who_bought.atom.builder
trong thư mục app/views/products.
Chúng ta truyền vào file này biến @product
đã tạo ra ở trên trong phương thức format.xml
.
Tạo dữ liệu Atom
Tiếp theo chúng ta tạo file who_bought.atom.builder
trong thư mục app/views/products
có nội dung như sau:
atom_feed do |feed| feed.title "Who bought #{@product.title}" latest_order = @product.orders.sort_by(&:updated_at).last feed.updated(latest_order && latest_order.updated_at) @product.orders.each do |order| feed.entry(order) do |entry| entry.title "Order #{order.id}" entry.summary :type => 'xhtml' do |xhtml| xhtml.p "Shipped to #{order.address}" xhtml.table do xhtml.tr do xhtml.th 'Product' xhtml.th 'Quantity' xhtml.th 'Total Price' end order.line_items.each do |item| xhtml.tr do xhtml.td item.product.title xhtml.td item.quantity xhtml.td number_to_currency item.total_price end end xhtml.tr do xhtml.th 'Total', :colspan => 2 xhtml.th number_to_currency order.line_items.map(&:total_price).sum end end xhtml.p "Paid by #{order.pay_type}" end entry.author do |author| entry.name order.name entry.email order.email end end end end
Hàm atom_feed
là hàm helper tạo ra code XML theo định dạng Atom.
atom_feed do |feed| ... end
Hàm này tạo ra một đối tượng ActionView::Helpers::AtomFeedHelper::AtomFeedBuilder,
ở đây
là biến feed
.
feed.title "Who bought #{@product.title}" latest_order = @product.orders.sort_by(&:updated_at).last feed.updated(latest_order && latest_order.updated_at)
Đầu tiên chúng ta gọi phương thức title
và updated
để thiết lập tiêu đề và thời gian tạo gần đây nhất bằng cách lấy thời gian trong thuộc tính updated_at
của bản ghi Order
cuối cùng. Nếu không tìm thấy thì Rails sẽ lấy thời gian là giờ hiện tại trên máy. Hai phương thức này sẽ tạo thẻ <entry>
và <title>
trong file Atom.
@product.orders.each do |order| feed.entry(order) do |entry| ... end end
Tiếp theo chúng ta lặp từng bản ghi Order,
cứ mỗi lần lặp thì chúng ta gọi phương thức feed.entry
. Phương thức này nhận vào một đối tượng bất kỳ và tạo ra biến ActionView::Helpers::AtomFeedHelper::AtomBuilder,
ở đây là biến entry
.
Chúng ta dùng biến entry
này để tạo các thẻ html như bình thường. Bạn có thể dựa vào code là có thể tự đoán phương thức nào tạo thẻ nào rồi.
Thiết lập mối quan hệ
Tuy nhiên chúng ta chưa định nghĩa mối quan hệ giữa một đối tượng Product
và một đối tượng Order.
Nhưng một đối tượng Order
lại chứa nhiều đối tượng LineItem,
một đối tượng Product
cũng có nhiều đối tượng LineItem,
do đó
chúng ta có thể liên kết đối tượng Product
với đối tượng Order
thông qua đối tượng LineItem
như sau:
class Product < ActiveRecord::Base validates :title, :description, :image_url, :presence => true validates :price, :numericality => {:greater_than_or_equal_to => 1.0} validates :title, :uniqueness => true validates :image_url, :format => { :with => %r{\.(gif|jpg|png)\Z}i, :message => 'Chi nhan file GIF, JPG, PNG' } has_many :line_items has_many :orders, :through => :line_items before_destroy :check_if_has_line_item private def check_if_has_line_item if line_items.empty? return true else errors.add(:base, 'This product has a LineItem') return false end end end
Routing
Cuối cùng chúng ta điều hướng URL tới phương thức who_bought
như sau:
Rails.application.routes.draw do resources :orders resources :line_items resources :carts get 'store/index' resources :products do get :who_bought, :on => :member end root :to => 'store#index' end
Nếu bạn còn nhớ thì hàm resources
sẽ tự động định nghĩa 7 URL vào 7 phương thức CRUD cơ bản.
Rails cho phép chúng ta điều hướng thêm vào sau các URL có sẵn, bằng cách khai báo thêm khối lệnh do...end
sau phương thức resources
và khai báo các dòng routing trong đó.
resources :products do get :who_bought, :on => :member end
Dòng code trên sẽ gắn URL /who_bought
vào URL của các phương thức đã định nghĩa trước đó, tức là Rails sẽ hiểu các URL như /products/1/who_bought
. Và gửi tới phương thức who_bought
trong lớp ProductsController
.
Vậy là xong, bạn có thể chạy server và trỏ vào URL như /products/1/who_bought.atom
là sẽ có đoạn code XML hiển thị danh sách các đơn hàng có sản phẩm có product_id
là 1
. Lưu ý là vì URL /who_bought
trả về nội dung Atom chứ không phải HTML như bình thường nên khi trỏ thì chúng ta thêm đuôi .atom
ở cuối URL.
Nếu muốn bạn có thể dùng các phần mềm đọc tin RSS để đọc file Atom này cũng được. Dưới đây là hình khi dùng RSS Feed Reader, đây là một extension của trình duyệt Chrome.