How to deal with the requirement of certain attributes in models that include a Concern in Rails?

Asked

Viewed 43 times

1

I’m working on a project where I have several models who share the behavior of being approvable. After doing some research, I came to the conclusion that the best thing in this case is to use the Concerns with the help of Activesupport::Concern. I confess that I really could not find much difference in relation to what other languages already do with the use of interfaces, if anyone can clarify me better about this; thank you. Get to the point, man Concern baptized with Approvable is as follows:

module Approvable
    extend ActiveSupport::Concern

    included do
            #validations
            validates :approval_status,
                    presence: true,
                    inclusion: { :in => NixusValidation::ValidApprovalStatuses, :message => :inclusion, unless: 'approval_status.blank?' }
            #scopes:
            scope :approved, -> { where(approvalStatus: NixusValidation::ApprovalStatuses::APPROVED) }
            scope :pending, -> { where(approvalStatus: NixusValidation::ApprovalStatuses::PENDING) }
            scope :unapproved, -> { where(approvalStatus: NixusValidation::ApprovalStatuses::UNAPPROVED) }
    end

    #INSTANCE METHODS
    #methods:
    def approved?()
            self.approval_status == NixusValidation::ApprovalStatuses::APPROVED
    end
end

There are still some appearances pending, such as "approve" and "disapprove", but my doubt consists of the following, every approved model will need to own and persist an approval_status attribute. How do you model this without using inheritance? Is the "right" way to simply know that every approvable model should include this attribute? How to design tests? I create a simple object that includes the module?

1 answer

1

Simple, before Activesupport::Concerns you needed to include a module of a lib, in Activerecord to be able to make use of a behavior, for example:

User.my_custom_method

Concerns, came to help in this regard, because we focused on writing our modules and only included them in the Models that share the behaviors. The correct way to do this is precisely with Modules that have the concept of being something reusable that we inject into some client that uses it (in other languages we would do this after DI (Dependency Injection)). Inheritance conceptually is something that should be shared only for 'things' that are of the same type, which does not apply to your case as you are taking advantage of the approval_status behavior in different types.

To model this without using inheritance (which in this case is more correct) use the Concerns as you are doing and only include the Module in the Models that need to make use of the behavior you want to share:

class User
  include Approvable
end

And your User model will have the behavior:

User.approved?

To test you must create the files in the test directory as you created in the app folder. And test your module alone.

app path

app/models/concerns/aprovabble.rb

test path

spec/models/concerns/approvabble_spec.rb

And then test the modules that make use of the shared behavior, for example in: spec/models/user_spec.Rb

expect(User.approved?).to eq(true)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.