26
loading...
This website collects cookies to deliver better user experience
Using a concern lets you extract the common logic from different classes into a reusable module.
visible_to
attribute, is_visible
instance method, and count_all_visible
class method. There could be many other models that need to control their visibility in a similar way. It would be nice if there was a way to abstract this code.# post.rb
class Post < ApplicationRecord
belongs_to :author
has_many :comments, dependent: :delete_all
has_rich_text :content
validates :title, presence: true, length: { minimum: 2 }
validate :has_valid_content
attr_accessor :visible_to
def is_visible?
visible_to.present?
end
def has_valid_content
# some code
end
def self.count_all_visible
all.select { |item| item.is_visible? }
end
end
# comment.rb
class Comment < ApplicationRecord
belongs_to :post
validates :commenter, :body, presence: true
attr_accessor :visible_to
def is_visible?
visible_to.present?
end
def self.count_all_visible
all.select { |item| item.is_visible? }
end
end
A lot of concerns are simply Ruby modules that are mixed in, with no additional stuff added. And then for a few cases, like when your concern includes both class and instance methods, or you want to call class methods on mixin, you can use extend ActiveSupport::Concern. - DHH
ActiveSupport::Concern
module. Concerns allow us to include modules with methods (both instance and class) and constants into a class so that the including class can use them.included
Post
includes a concern, anything inside the included
block will be evaluated as if it was written inside Post
. class_methods
class_methods
block, you can create a nested module named ClassMethods
. module Taggable
extend ActiveSupport::Concern
included do
end
class_methods do
end
end
A concern is a module that you extract to split the implementation of a class or module in coherent chunks, instead of having one big class body. The API surface is the same one, they just help organize the code. A concern is a regular Ruby mixin, there’s nothing else, it is not a new concept. It is plain Ruby. - Xavier Noria
Visible
concern to extract the visibility-related code from the Post
and Comment
models. In general, the Visible
concern deals with an entity's visibility, concerning if it is visible or not.# visible.rb
module Visible
extend ActiveSupport::Concern
# The code inside the included block is evaluated
# in the context of the class that includes the Visible concern.
# You can write class macros here, and
# any methods become instance methods of the including class.
included do
attr_accessor :visible_to
def is_visible?
visible_to.present?
end
end
# The methods added inside the class_methods block (or, ClassMethods module)
# become the class methods on the including class.
class_methods do
def count_all_visible
all.select { |item| item.is_visible? }
end
end
end
Post
model wanted the visibility
functionality, it would include the Visible
concern.class Post
include Visible
end
visible_to
attribute, is_visible
instance method, and the count_all_visible
class method to the Post
class. require "test_helper"
class PostTest < ActiveSupport::TestCase
test "Post can include the visible concern" do
post = Post.new
assert_not post.is_visible?
post.visible_to = "reader"
assert_equal "reader", post.visible_to
assert post.is_visible?
assert_equal [], Post.count_all_visible
end
end
visibility_manager = Visibilty.new
visibility_manager.is_visible(post)
visibility_manager
to access the shared code. So that’s another way of putting this: It’s a writing style. Like using subheads to explain subservient ideas within a broader context. You could probably extract all those subheads out, and turn them into little essays of their own. But that’s often just not the right level of extraction. As you saw, the Accessor role starts life as literally just two methods! It doesn’t warrant being turned into it’s own class. It doesn’t have an independent center of gravity. - A May of WTFs
Original Post: How Rails Concerns Work and How to Use Them