Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
17
Help us understand the problem. What is going on with this article?
@ryshinoz

RuboCopのCustom Copを作る

More than 5 years have passed since last update.

はじめに

rubocop-rspecを参考にCustom Copを作る方法です

今回はメソッド名がaaaであれば違反になるサンプルCopを作成します

# NG
def aaa
    1
end

Copの名前はBadMethodNameにして、gem化してみます

試したバージョン

  • Ruby - 2.1.1p76
  • RuboCop - 0.23.0
  • Bundler - 1.5.3

rubocop-sample

rubocop-sampleというGemを作成します

$ bundle gem rubocop-sample -t
      create  rubocop-sample/Gemfile
      create  rubocop-sample/Rakefile
      create  rubocop-sample/LICENSE.txt
      create  rubocop-sample/README.md
      create  rubocop-sample/.gitignore
      create  rubocop-sample/rubocop-sample.gemspec
      create  rubocop-sample/lib/rubocop/sample.rb
      create  rubocop-sample/lib/rubocop/sample/version.rb
      create  rubocop-sample/.rspec
      create  rubocop-sample/spec/spec_helper.rb
      create  rubocop-sample/spec/rubocop/sample_spec.rb
      create  rubocop-sample/.travis.yml
Initializing git repo in /Users/xxx/rubocop-sample

rubocop-sample.gemspec

RuboCopを追加しておきます

※ 0.19の時点でRubocopからRuboCopに変更した関係でバージョンを指定しているみたいです

spec.add_runtime_dependency('rubocop', '~> 0.19', '>= 0.19')

そのほかは適宜編集してください

編集後にbundle installで依存ライブラリをインストールしておきます

lib/rubocop-sample.rb

lib/rubocop/sample.rbをrequireするlib/rubocop-sample.rbを作成します

require 'rubocop/sample'

rubocop-rspecは実行時に--requireオプションを使って外部Copをrequireさせてます

その時にrubocop --require rubocop-sampleと実行できる為に作成しています、別にrubocop --require rubocop/sampleでもいい人ならいらないのかもしれません

※ Ruby歴数ヶ月だとこれ以上の意味がわかりませんでした

lib/rubocop/sample/inject.rb

RuboCopは外部Copを取り入れる仕組みがない?みたいなのでそれに対応するlib/rubocop/sample/inject.rbを作成します

RuboCopはCopリスト(rubocop/config/default.yml)をRuboCop::ConfigLoaderクラスの@default_configurationに保持しています

rubocop-rspecでは以下のような実装で@default_configurationを更新する事で対応していますのでそのまま実装します

module RuboCop
  module Sample
    module Inject
      DEFAULT_FILE = File.expand_path('../../../../config/default.yml'), __FILE__)

      def self.defaults!
        hash = YAML.load_file(DEFAULT_FILE)
        config = ConfigLoader.merge_with_default(hash, DEFAULT_FILE)
        ConfigLoader.instance_variable_set(:@default_configuration, config)
      end
    end
  end
end

config/default.yml

作成するCop名と説明(Description)と有効(Enabled)にしておきます

Style/BadMethodName:
  Description: 'aaaというメソッド名は利用してはいけない'
  Enabled: true

lib/rubocop/sample.rb

ruboco/sample.rbをrequireされた時点で作成したRuboCop::Sample::Inject.default!が実行されるようにします

require 'rubocop'

require 'rubocop/sample/version'
require 'rubocop/sample/inject'

RuboCop::Sample::Inject.defaults!

# 作成するCop
require 'rubocop/sample/cop/style/bad_method_name'

lib/rubocop/sample/cop/style/bad_method_name.rb

RuboCopはソースをASTに変換した後にParser::AST::Processoron_xxxを呼び出します

今回はメソッド名のチェックなのでon_def(インスタンスメソッド)を実装して名前をチェックします

module RuboCop
  module Cop
    module Style
      class BadMethodName < Cop
        def on_def(node)
          method_name, _other = *node
          if method_name.to_s == 'aaa'
            add_offense(node, :name, 'aaaは利用してはいけません!', :fatal)
          end
        end
      end
    end
  end
end

add_offenseに違反を追加します

※ 今回はサンプルの為そのまま実装しましたが、メソッドの違反チェック用にRuboCop::Cop::CheckMethodsが用意されているのでそれを利用した方がよいです

spec/spec_helper.rb

作成したgemとRuboCopのspec_helperをrequireしておきます

require File.join(Gem::Specification.find_by_name('rubocop').gem_dir, 'spec', 'spec_helper.rb')

spec/rubocop-sample.rb

そのままじゃエラーになるので修正しておきます(環境によって違うかも)

spec/rubocop/cop/style/bad_method_name_spec.rb

RuboCopのspec_helperをrequireした事でinspect_sourceが利用できるようになりますのでspecを書いていきます

require 'spec_helper'

describe RuboCop::Cop::Style::BadMethodName do
  subject(:cop) { RuboCop::Cop::Style::BadMethodName.new }

  it 'インスタンスメソッド名がaaaは利用できない' do
    inspect_source(cop, ['def aaa; 1;end'])
    expect(cop.offenses.size).to eq(1)
    expect(cop.messages).to eq(['aaaは利用してはいけません!'])
  end
end

rake spec

エラーが出なければOKです

$ rake spec
ruby -S rspec ./spec/rubocop/cop/style/bad_method_name_spec.rb ./spec/rubocop/sample_spec.rb

...
...

RuboCop::Cop::Style::BadMethodName
  インスタンスメソッド名がaaaは利用できない

Rubocop::Sample
  should have a version number

Finished in 0.00237 seconds
2 examples, 0 failures

rake install

作成したgemをローカルにインストールします

$ rake install
rubocop-sample 0.0.1 built to pkg/rubocop-sample-0.0.1.gem.
rubocop-sample (0.0.1) installed.

$ gem list rubocop-sample

*** LOCAL GEMS ***

rubocop-sample (0.0.1)

試す

src/sample.rb

class Sample
  def aaa
    1
  end
end

実行時には必ず--require rubocop-sampleオプションをつけて実行します

$ rubocop --require rubocop-sample --only BadMethodName sample.rb
Inspecting 1 file
F

Offenses:

test.rb:2:7: F: aaaは利用してはいけません!
  def aaa
      ^^^

1 file inspected, 1 offense detected

Links

17
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
17
Help us understand the problem. What is going on with this article?