This Banner is For Sale !!
Get your ad here for a week in 20$ only and get upto 15k traffic Daily!!!

Writing and testing a custom RuboCop cop


Fixing an issue is nice — however conserving it from coming again is even higher. As we resolve points in our code base, we regularly think about preserve that classification of problem out of the code base solely. Typically we attain for RuboCop to assist us police sure patterns. This additionally helps to doc the originating problem and educates teammates on why these patterns are undesirable.

RuboCop is greater than only a linter. It’s extremely extensible and means that you can write custom cops to implement particular habits. These cops can be utilized to create higher code practices, forestall dangerous patterns from sneaking right into a legacy code base, and supply coaching for different engineers. However it may be difficult to know how to create a new cop and if it’s going to work long-term.

We are able to write unit checks to make sure the success of our customized cops, simply as we might with any utility code.
Let’s discover this with an instance to point out how testing might be performed.



Testing customized cops

With the Aha! engineering group, each mannequin has an account_id attribute current and for security reasons, we by no means need this to be set by way of mass-assignment. To keep away from this, we need to forestall sure attributes from being added to attr_accessible.

# dangerous
class Foo
  attr_accessible :title, :account_id
finish
Foo.create(account_id: 1, title: "foo")
# good
class Foo
  attr_accessible :title
finish
foo = Foo.new(title: "foo")
foo.account_id = 1
foo.save
Enter fullscreen mode

Exit fullscreen mode

We’ve a customized cop that analyzes the arguments to that methodology and can error if any protected attribute is current. The customized cop we’ve finally ends up trying one thing like this:

class RuboCop::Cop::ProtectedAttrAccessibleFields < RuboCop::Cop::Cop
  # We are able to outline an inventory of attributes we need to defend
  PROTECTED_ATTRIBUTES = [
    :account_id,
  ].freeze
  # We are able to outline an error message that's displayed when an offense is detected.
  # This may be useful to speak data again to different engineers
  ERROR_MESSAGE = <<~ERROR.freeze
    Solely allow attributes which might be protected to be fully person managed. Sometimes any *_id subject might be problematic.
    As an alternative carry out direct project of the sphere after doing a scoped lookup. That is the most secure approach to deal with person enter.
    Some fields equivalent to #{PROTECTED_ATTRIBUTES.examine} ought to by no means be used as a part of attr_accessible.
  ERROR
  # We need to look at methodology calls. Significantly these which might be calling the attr_accessible methodology
  # and now have arguments we care about
  def on_send(node)
    if receiver_attr_accessible?(node) && protected_arguments?(node)
      # If we do detect an attr_accessible name with arguments we care about, we will file an offense
      add_offense(node, message: ERROR_MESSAGE)
    finish
  finish
  non-public
  def receiver_attr_accessible?(node)
    node.method_name == :attr_accessible
  finish
  def protected_arguments?(node)
    node.arguments.any? do |argument|
      if argument.sym_type? || argument.str_type?
        PROTECTED_ATTRIBUTES.embody?(argument.worth.to_sym)
      finish
    finish
  finish
finish
Enter fullscreen mode

Exit fullscreen mode

This practice cop does the trick. Including a check for it ensures that it will not break sooner or later after we replace RuboCop or prolong the performance. With a purpose to write a check, we have to perceive how the customized cops are arrange and run.



Instantiate a customized cop

RuboCop::Cop::Cop inherits from RuboCop::Cop::Base and that permits the instantiation with out any arguments. So it seems this is not something particular — creating a brand new occasion of our cop is actually so simple as: RuboCop::Cop::ProtectedAttrAccessibleFields.new

If the cop requires some sort of configuration, it may be handed to the occasion by way of a RuboCop::Config object. The RuboCop::Config takes two arguments. RuboCop can provide configuration by way of YML recordsdata. You need to use the primary argument of RuboCop::Config to go this configuration with varied values from the check. The second argument is the trail of the loaded YML file, which will be ignored within the checks.

config = RuboCop::Config.new({ RuboCop::Cop::ProtectedAttrAccessibleFields.badge.to_s => {} }, "https://style-tricks.com/")
cop = RuboCop::Cop::ProtectedAttrAccessibleFields.new(config)
Enter fullscreen mode

Exit fullscreen mode



Course of, execute, look at

Because it seems, there’s a method obtainable, RuboCop::Cop::Base#parse , that accepts a string as enter and can return one thing the cop can course of.

This permits us to have one thing like:

supply = <<~CODE
  attr_accessible :account_id
CODE
processed_source = cop.parse(supply)
Enter fullscreen mode

Exit fullscreen mode

There’s a class from inside RuboCop, RuboCop::Cop::Commissioner , that’s answerable for taking a list of cops and utilizing these to investigate the processed supply code. With a purpose to run our cop, we will run this methodology.

commissioner = RuboCop::Cop::Commissioner.new([cop])
investigation_report = commissioner.examine(processed_source)
Enter fullscreen mode

Exit fullscreen mode

The RuboCop::Cop::Commissioner#examine methodology will return an occasion of RuboCop::Cop::Commissioner::InvestigationReport which is an easy struct class that has an inventory of offenses which were recorded.



Put all of it collectively

We find yourself with a check file that appears one thing like this:

describe RuboCop::Cop::ProtectedAttrAccessibleFields do
  let(:config) { RuboCop::Config.new({ described_class.badge.to_s => {} }, "https://style-tricks.com/") }
  let(:cop) { described_class.new(config) }
  let(:commissioner) { RuboCop::Cop::Commissioner.new([cop]) }
  it "data an offense if we use permit account_id as a string" do
    supply = <<~CODE
      attr_accessible :foo, 'account_id'
    CODE
    investigation_report = commissioner.examine(cop.parse(supply))
    anticipate(investigation_report.offenses).to_not be_blank
    anticipate(investigation_report.offenses.first.message).to eql described_class::ERROR_MESSAGE
  finish
  it "data an offense if we use permit account_id as image" do
    supply = <<~CODE
      attr_accessible :foo, :account_id
    CODE
    investigation_report = commissioner.examine(cop.parse(supply))
    anticipate(investigation_report.offenses).to_not be_blank
    anticipate(investigation_report.offenses.first.message).to eql described_class::ERROR_MESSAGE
  finish
  it "does not file an offense if no protected attribute is used" do
    supply = <<~CODE
      attr_accessible :foo
    CODE
    investigation_report = commissioner.examine(cop.parse(supply))
    anticipate(investigation_report.offenses).to be_blank
  finish
finish
Enter fullscreen mode

Exit fullscreen mode

Now that we all know write checks, we will use them as a place to begin for constructing new cops, extending current cops, and making certain that issues proceed to operate as our utility grows and evolves. These little investments into project-specific cops can find yourself being a big funding sooner or later well being of the tasks.

Join a free trial of Aha! Develop

Aha! Develop is a totally extendable agile growth instrument. Prioritize the backlog, estimate work, and plan sprints. If you’re excited about an built-in product development strategy, use Aha! Roadmaps and Aha! Develop together. Join a free 30-day trial or join a live demo to see why greater than 5,000 firms belief our software program to construct lovable merchandise and be joyful doing it.

The Article was Inspired from tech community site.
Contact us if this is inspired from your article and we will give you credit for it for serving the community.

This Banner is For Sale !!
Get your ad here for a week in 20$ only and get upto 10k Tech related traffic daily !!!

Leave a Reply

Your email address will not be published. Required fields are marked *

Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?