Advanced routing, layouts, and a brief introduction to metaprogramming

Khi làm việc với Rails routes, đôi lúc chúng ta sẽ bắt gặp những câu hỏi như khi nào dùng singular routes, khi nào dùng regular routes, tại sao phải sử dụng nested routes, giữa member routes và collection giống và khác nhau như thế nào hay làm sao để thiết lập redirect route đính kèm với params . Bài viết dưới đây sẽ phần nào giúp bạn giải đáp được những thắc mặc này.

Advanced Routing

Hẳn là ai trong chúng ta cũng thấy quen thuộc với việc sử dụng resource method hay khai báo các get, post method cụ thể ở trong routes - chiếm 90% những gì bạn sẽ sử dụng trong file routes , thế nhưng 10% còn lại cung cấp cho bạn những tùy chọn rất hữu ích như redirect trực tiếp từ routes , nesting routes ...

Nested Routing

Chúng ta hoàn toàn có thể viết các resource lồng nhau, ví dụ như muốn liệt kê danh sách các học sinh trong một khóa học với URL là http://demosite.com/courses/1/students/3 ta làm như sau

    # Config routes
    DemoApp::Application.routes.draw do
    	resources :courses do
        	resources :students
        end
    end

Lúc này URL sẽ có dạng:

   course_student  GET  /courses/:course_id/students/:id(.:format)  student#show

Để truy cập được URL này, cần xác định được params id của cả 2 đối tượng course và student.

Member and Collection Routes

Có đôi lúc chúng ta muốn thêm vào một non-RESTful route vào trong một resources. Để làm được điều này chúng ta sử dụng "member" method

   #config/routes.rb
   DemoApp::Application.routes.draw do
    	resources :courses do
          member do
            get"review"
          end
        end
    end

Route sẽ ánh xạ đến action courses#review tương ứng trong controller. Và đương nhiên có thể thêm nhiều route khác nếu bạn muốn. Trong trường hợp muốn thêm một non-RESTful route có chức năng tương đương với action index, ta làm như sau:

  # config/routes.rb
    DemoApp::Application.routes.draw do
      resources :courses do
        member do
          get "review"  # review một khóa học (yêu cầu ID)
        end
        collection do
          get "upcoming"  # Liệt kê tất cả các khóa học sắp khai giảng
        end
      end
    end

Redirects và Wildcard Routes

Bạn muốn cung cấp cho người dùng URL ngắn gọn và dễ nhớ dễ dàng chia sẻ nhưng vẫn đảm bảo gửi đi những thông số cần thiết

 # config/routes.rb
    DemoApp::Application.routes.draw do
      get 'courses/:course_name' => redirect('/courses/%{course_name}/students'), :as => "course"
    end

URLs ban đầu

	/courses/demo01
	/courses/demo02
	/courses/demo03

Khi người dùng click vào các link này, chúng sẽ bị redirect sang các URLs tương ứng sau

	/courses/demo01/students
	/courses/demo02/students
	/courses/demo03/students

Với việc sử dụng redirect routes, các đường dẫn (URL) sẽ thân thiện hơn với người dùng, dễ dàng thao tác cũng như chia sẻ chúng.

Advanced Layouts

Rails cho chúng ta những chức năng rất hữu ích để quản lý layout. Việc sử dụng layout sẽ làm giảm thiểu việc lặp lại codes trong các view layer.Chúng ta có thể chia nhỏ các page thành các khối riêng biệt như: Header, footer, sidebar, body, etc.

Có thể sử dụng nhiều layout cho một page bằng cách từ layout hiện tại render layout khác sử dụng method

	render template: "your_layout.html.erb"

Hoặc cũng có thể sử dùng content_for method để đưa thông tin từ layout đầu tiên đến một layout khác.

Ví dụ bạn tạo một file layout cho các static pages: app/views/layouts/static_pages.html.erb, file này sẽ được render mặc định ra các trang view của StaticPagesController. Trong trường hợp bạn muốn render layout application.html.erb nhưng bỏ qua một số CSS đặc biệt, ta có thể dùng content_for method

	# app/views/layouts/static_pages.html.erb
    <% content_for :stylesheets do %>
      #navbar {display: none}
    <% end %>
    <%= render :template => "layouts/application" %>

Sau đó trong application layout cần thiết lập để lọc lấy nội dung đó và sử dụng chúng

  # app/views/layouts/application.html.erb
    ...
    <head>
      ...
      <style><%= yield :stylesheets %></style>
    </head>
    ...
    render :template => "static_pages.html.erb"
    ...

Khi yield đến block content :stylesheet nó sẽ chèn nội dung bên trong content_for đến nơi mà yield method hướng tới. Cụ thể trong ví dụ trên chúng ta chèn một số css style vào application layout bằng cách: đầu tiên là render một layout riêng static_pages.html.erb, sau đó sử dụng content_for để chèn style sang application layout. Kết quả thu được như sau:

     # app/views/layouts/application.html.erb
    ...
    <head>
      ...
      <style> #navbar {display: none} </style>
    </head>
    ...

Metaprogramming Rails

Metaprogramming được hiểu là lập trình trong lập trình, nó được sử dùng để thêm, chỉnh sửa hoặc thay đổi codes của một chương trình(ứng dụng) khi chương trình(ứng dụng) đó đang chạy. Với việc sử dụng metaprogramming, chúng ta có thể tạo mới hoặc xóa các phương thức hiện có trên object, class, tránh được việc lặp codes. Ví dụ về metaprogramming trong action Rails: Khi ứng dụng Rails được chạy lần đầu tiên, nó sẽ load file config/routes.rb. Bên trong file này chứa các resource paths như get "home" => static_pages#home và người dùng có thể nhập URL "http://www.demosite.com/home" để vào trang homepage. Sau đó Rails sẽ tự tạo ra một cặp helper method home_path và home_url. Đây chính là một phần của metaprogramming. Thế nhưng tự động tạo ra là một chuyện, vậy khi nào các method đó được thực thi? Ruby cung cấp #send method để làm điều này. Khi chúng ta muốn gọi một method của một object, ta chỉ việc gửi nó với các đối số mà mình muốn.

  > 1 + 2
  => 3
  > 1.send(:+, 2)
  => 3

Trong trường hợp method tự định nghĩa

    class Rubyist
      define_method :hello do |my_arg|
        my_arg
      end
    end
    obj = Rubyist.new
    puts(obj.hello('User')) # => User

Metaprogramming thực sự là một công cụ rất tiện lợi, có vô vàn những điều thú vị khi dùng nó. Tuy vậy không có nghĩa là ta phải nắm vững nó trước khi làm Rails, vì vậy hãy chỉ nên đào sâu về nó khi đã thực sự nắm được và vận dụng được Rails.