Tìm hiểu về ActiveSupport::Concerns
Bài đăng này đã không được cập nhật trong 6 năm
Mở đầu
Trong quá trình build 1 Rails app, nếu để ý chúng ta sẽ thấy xuất hiện 1 folder có tên là concerns
, nằm trong đường dẫn app/controllers
và app/models
. Nếu trước giờ chưa dùng đến nó thì chúng ta thường không quan tâm đến, cũng không rõ nó có tác dụng gì, hoặc là có thể nó để làm 1 thứ gì đó phức tạp mà mình sẽ không cần đến,.. cho đến khi bước vào làm dự án thực tế lớn, đụng chạm đến nhiều method phức tạp thì concerns
đúng là vô cùng hữu dụng. Vậy concerns
ở đây là gì?
Nói đơn giản thì concerns
là 1 thư mục chứa các module bao gồm các method được sử dụng cho nhiều class hay các module include chúng.
Concerns in Controllers
Chúng ta muốn code được gọn gàng, đồng nghĩa với việc phải hạn chế trùng lặp code càng nhiều càng tốt. Giả sử như trong app của chúng ta phân quyền cho cả admin và user đều có thể tạo post với 7 action cơ bản (show, new, index, create, edit, update, destroy) thì để đạt được mong muốn tối ưu số dòng code, tránh trùng lặp ở controller thì đây là lúc concerns
phát huy tác dụng của mình.
Tạo file post_action.rb
trong đường dẫn app/controllers/concerns
:
module PostAction
included do
before_action :load_post, only: [:show, :edit, :destroy, :update]
end
def index
@posts = Post.select(:id, :content)
end
def new
@post = Post.new
end
def show
end
def create
@post = Post.new(post_params)
if @post.save
flash[:notice] = "Successfully created post."
redirect_to @post
else
flash[:alert] = "Error creating post."
render :new
end
end
def edit
end
def update
if @post.update_attributes(post_params)
flash[:notice] = "Successfully updated post."
redirect_to posts_path
else
flash[:alert] = "Error creating post."
render :edit
end
end
def destroy
if @post.destroy
flash[:notice] = "Successfully deleted post."
redirect_to posts_path
else
flash[:alert] = "Error deleting post."
end
end
private
def post_params
params.require(:post).permit(:content)
end
def load_post
@post = Post.find_by id: params[:id]
end
end
Ở đây before_action
được viết bên trong block included
để bảo đảm cho việc before_action
sẽ luôn luôn được thực thi ở bất cứ đâu mà module PostAction
được include.
Và chúng ta chỉ việc include module này vào controller nào cần dùng:
class PostsController < ApplicationController
include PostAction
end
class Admin::PostsController < ApplicationController
include PostAction
end
Concerns in Models
ActiveSupport::Concern
cũng hoạt động ở phía model, tương tự như những gì ta thấy ở bên controller. Giả sử như chúng ta có 3 model Post, Comment, Like có ràng buộc quan hệ như sau:
class Post < ActiveRecord::Base
has_many :likes, as: likable
has_many :comments
def like
likes.create
end
end
class Comment < ActiveRecord::Base
has_many :likes, as: likable
belongs_to :post
def like
likes.create
end
end
class Like < ActiveRecord::Base
belongs_to :likable, polymorphic: true
end
Với sự hỗ trợ của concerns
chúng ta có thể rút gọn lại như sau:
Tạo module Likable
trong đường dẫn app/models/concerns/likable.rb
:
module Likable
extend ActiveSupport::Concern
included do
has_many :likes, as: :likable
end
def like
likes.create
end
end
và include vào model:
class Post < ActiveRecord::Base
include Likable
has_many :comments
end
class Comment < ActiveRecord::Base
include Likable
belongs_to :post
end
1 số lưu ý
Khi một module được include vào một class, class đó sẽ được sử dụng các instance method được khai báo trong module đó, tuy nhiên, class này lại không truy cập được đến class method:
module PostAction
def put_a
puts "A"
end
class << self
def class_method
puts "Class Method"
end
end
end
class Post < ActiveRecord::Base
include PostAction
end
Post.new.put_a #=> "A"
Post.new.class_method #=> NoMethodError
Để khắc phục thì chúng ta có thể sử dụng included
:
module A
def A.included(mod)
puts "#{self} is included in #{mod}"
end
end
module B
include A
end
#=> "A is included in B"
Tổng kết
Trên đây là 1 chút chia sẻ của mình về ActiveSupport::Concerns
, bài viết còn nhiều thiếu sót, mong được góp ý, cảm ơn bạn đã dành thời gian đọc bài viết.
Nguồn tham khảo:
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
https://www.sitepoint.com/dry-off-your-rails-code-with-activesupportconcerns/
All rights reserved