[RUBY] Lassen Sie uns ein custom_cop erstellen, das auf das Schütteln des Namens hinweist

Einleitung: cat2:

Fanclub und fan_club sind im Code gemischt ... Ich denke, es ist üblich. Ich würde es gerne mit denen vereinen, die sagen, dass dies derjenige im Team ist, aber ich habe nicht genug Gedächtnis im Kopf, um mich an solche Regeln zu erinnern ... Es ist auch eine gute Idee, Leute darauf hinweisen zu lassen, und ich möchte es einem Computer überlassen, der viele Male geduldig darauf hinweist. Also habe ich ein custom_cop für [Rubocop] erstellt (https://github.com/rubocop-hq/rubocop).

Quelle hier.

Übrigens, auch wenn Sie keine so mühsame Arbeit verwenden, scheint es einfacher zu sein, mit dem Hauptzweig nach dem Diff zu suchen. Aber ich wollte custom_cop schreiben. Es ist ein Sonntagsprogramm, oder?

Einfache Spezifikationen: Kuchen:

Wenn Sie versuchen zu definieren, was Sie wollen, so weit Sie verstehen können ... Ist es so? Schlüsselwörter nehmen während der Anwendungsentwicklung um ein Vielfaches zu, daher möchte ich sie in einer externen Datei definieren.

Listen Sie die falschen Wörter und die Wörter, die Sie korrigieren möchten, in der YAML-Datei auf, weisen Sie auf falsche Wörter hin und korrigieren Sie sie.

Versuchen Sie es zu machen: Muskel:

Wie auch immer, es ist nicht interessant, wenn es nicht funktioniert, also werde ich mein Bestes geben, um Folgendes zu erreichen: point_down :.

Weisen Sie darauf hin, ob der Wert, den Sie in die Variable eingeben, das falsche Wort fanclub ist

Überprüfen Sie den Zielcode: dart:

Bereiten Sie Code vor, der überprüft werden kann, wenn rubocop ausgeführt wird. Ich denke, der Code der Anwendung, die Sie tatsächlich verwenden, ist in Ordnung.

target.rb


a = 'fanclub'

custom_cop :cop:

Ich habe es fast durch Kopieren geschafft. Wie auch immer, ich hoffe du kannst dich so bewegen.

lib/custom_cops/spell_inconsistency.rb


# frozen_string_literal: true

module CustomCops
  class SpellInconsistency < RuboCop::Cop::Cop
    WRONG_KEYWORD = 'fanclub'.freeze

    def on_str(node)
      add_offense(node, message: "Use 'fan_club' instead of 'fanclub'.") if node.source.include?(WRONG_KEYWORD)
    end
  end
end

.rubocop.yml :wrench:

Stellen Sie das erstellte custom_cop so ein, dass es von rubocop verwendet werden kann.

yaml:.rubocop.yml


require:
  - './lib/custom_cops/spell_inconsistency'

Run: raise_hands:

Bei Ausführung mit rubocop target.rb ...

image.png

Zusätzlich zum Hinweis "Ich verwende Variablen nicht nur zur Definition" oder "Da es sich um eine Konstante handelt, frieren Sie sie bitte ein" ... "CustomCops / SpellInconsistency: Verwenden Sie" fan_club "anstelle von" fanclub " !!

Andere Wörter werden ebenfalls unterstützt: zap:

Ich möchte es auf die in YAML registrierte erweitern.

Weisen Sie darauf hin, ob der Wert, den Sie in die Variable eingeben, das falsche Wort in der YAML-Datei ist

spell_inconsistency.yml :wrench:

Registrieren Sie hier "Fanclub", "Fanclub" und "FANCLUB". In Zukunft möchte ich sie in zwei Worten schreiben: "fan_club", "FanClub" und "FAN_CLUB".

lib/custom_cops/spell_inconsistency.yml


# Wrong: Correct
fanclub: fan_club
Fanclub: Fanclub
FANCLUB: FAN_CLUB

Überprüfen Sie den Zielcode: dart:

target.rb


a = 'fanclub'
b = 'Fanclub'
c = 'FANCLUB'

custom_cop :cop:

Die Datei, in der das Wort registriert ist, wird von "YAML.load_file" gelesen und durch Drehen um "each" überprüft.

lib/custom_cops/spell_inconsistency.rb


# frozen_string_literal: true

require 'yaml'

module CustomCops
  class SpellInconsistency < RuboCop::Cop::Cop
    MESSAGE_TEMPLATE = "Use '%s' instead of '%s'."
    SPELL_INCONSISTENCIES = YAML.load_file(Pathname(__dir__).join('spell_inconsistency.yml'))

    def on_str(node)
      SPELL_INCONSISTENCIES.each do |wrong_keyword, correct_keyword|
        add_offense(node, message: message(wrong_keyword, correct_keyword)) if node.source.include?(wrong_keyword)
      end
    end

    private

    def message(wrong_keyword, correct_keyword)
      MESSAGE_TEMPLATE % [correct_keyword, wrong_keyword]
    end
  end
end

Run: raise_hands:

Wenn Sie es mit "rubocop target.rb" ausführen, gibt es viele zusätzliche Nachrichten. Wenn Sie also nur Ihren eigenen custom_cop wie "rubocop --only CustomCops / SpellInconsistency target.rb" angeben und ausführen ...

image.png

Oh, es sieht gut aus: Daumen hoch:

Ersetzen Sie Symbole und Konstanten

Machen Sie dasselbe, wenn Sie Symbole oder Konstanten ersetzen, die einer Zeichenfolge sehr ähnlich sehen.

Überprüfen Sie den Zielcode: dart:

target.rb


a = 'Fanclub'
b = :fanclub
c = FANCLUB

custom_cop :cop:

Schauen Sie sich vor dem Schreiben [RuboCop :: AST :: Traversal] an (https://github.com/rubocop-hq/rubocop-ast/blob/master/lib/rubocop/ast/traversal.rb).

Unmittelbar nach dem Anzeigen von # walk Es sieht so aus, als würden Sie eine Methode mit diesem Namen aufrufen und den Knotentyp betrachten, den Sie überquert haben. Es scheint also, dass mein # on_str auch aufgerufen wurde.

lib/rubocop/ast/traversal.rb


def walk(node)
  return if node.nil?

  send(:"on_#{node.type}", node)
  nil
end

Ich habe die ganze Datei durchsucht und so "const" und "sym" gefunden, also habe ich beschlossen, auch "# on_sym" und "on_const" zu implementieren. Die Inspektionsmethode ist genau die gleiche, definieren Sie sie also mit define_method.

lib/custom_cops/spell_inconsistency.rb


# frozen_string_literal: true

require 'yaml'

module CustomCops
  class SpellInconsistency < RuboCop::Cop::Cop
    MESSAGE_TEMPLATE = "Use '%s' instead of '%s'."
    SPELL_INCONSISTENCIES = YAML.load_file(Pathname(__dir__).join('spell_inconsistency.yml'))

    NODE_TYPES = %I[str const sym].freeze
    NODE_TYPES.each do |node_type|
      define_method "on_#{node_type}" do |node|
        SPELL_INCONSISTENCIES.each do |wrong_keyword, correct_keyword|
          add_offense(node, message: message(wrong_keyword, correct_keyword)) if node.source.include?(wrong_keyword)
        end
      end
    end

    def message(wrong_keyword, correct_keyword)
      MESSAGE_TEMPLATE % [correct_keyword, wrong_keyword]
    end
  end
end

Run: raise_hands:

Bei Ausführung als rubocop - nur CustomCops / SpellInconsistency target.rb ...

image.png

Sieht gut aus: +1:

Weisen Sie darauf hin, wenn Sie im Variablennamen das falsche Wort verwenden

Nachdem wir nun Zeichenfolgen, Symbole und Konstanten haben, lassen Sie sie darauf hinweisen, wenn wir das falsche Wort für den Variablennamen verwenden.

Überprüfen Sie den Zielcode: dart:

target.rb


a = 'Fanclub'
b = :fanclub
c = FANCLUB
fanclub = 'a'

Ruby-Parse Teil 1

Apropos Variablen, "Variable" wäre "var" ... Also, [RuboCop :: AST :: Traversal](https://github.com/rubocop-hq/rubocop-ast/blob/master/lib Ich habe mir (/rubocop/ast/traversal.rb) angesehen, aber ... es gibt nicht viel davon ...

In Development Basic der offiziellen RuboCop-Dokumentation heißt es, "Ruby-Parse" in der Befehlszeile zu verwenden. Ich verstehe.

image.png

Es war "lvasgn". Zu NODE_TYPES hinzufügen ...

lib/custom_cops/spell_inconsistency.rb


(Kürzung)
module CustomCops
  class SpellInconsistency < RuboCop::Cop::Cop
    (Kürzung)
    NODE_TYPES = %I[str const sym lvasgn].freeze
    NODE_TYPES.each do |node_type|
      define_method "on_#{node_type}" do |node|
        SPELL_INCONSISTENCIES.each do |wrong_keyword, correct_keyword|
          add_offense(node, message: message(wrong_keyword, correct_keyword)) if node.source.include?(wrong_keyword)
        end
      end
    end
    (Kürzung)
  end
end

Wenn Sie rubocop ausführen - nur CustomCops / SpellInconsistency target.rb ...

image.png

fanclub = 'a' wurde erkannt ... a =' FanClub wurde ebenfalls erkannt ...

Rubinanalyse (2)

Schauen wir uns den Originalcode und die Ausgabe von Ruby-Parse genauer an.

(lvasgn :fanclub
  (str "a"))

Die Zuordnung zu einer Variablen lautet "Variablenname = Ausdruck", nicht wahr? Ich frage mich, ob der Ausdruck der Zeichenkette "a" entspricht. Wenn Sie es so betrachten, ist lvasgn der linke Wert ein Zeichen für die Zuordnung auf der linken Seite. (Da es vor langer Zeit eine Syntaxanalyse in einer Universitätsklasse gab, kann ich sie nicht erklären, aber ich kann sie nicht auf einer Ebene erklären, die ich verstehen kann ... Es tut mir leid.)

(Substitution auf der linken Seite:fanclub
  (String"a")) 

Der Grund, warum ich zweimal mit "fanclub =" a "feststeckte, war, dass es auf" fanclub "von" str "und auf" fanclub "von" str "in" lvasgn "reagierte. Wahrscheinlich von.

(lvasgn :a
  (str "fanclub"))

Es scheint, dass Sie es nur unmittelbar nach "lvasgn" einnehmen sollten.

Wenn Sie das offizielle Dokument Development Basic von RuboCop lesen, sehen Sie die Methoden, die häufig im Argument node of on_ ~ verwendet werden. War dort. Sie sollten "Kinder" verwenden und das erste Kind verwenden.

node.type # => :send
node.children # => [s(:send, s(:send, nil, :something), :empty?), :!]
node.source # => "!something.empty?"

custom_cop :cop: Abgesehen von "str", "const", "sym" habe ich "on_lvasgn" gemacht, um das erste Kind zu inspizieren.

lib/custom_cops/spell_inconsistency.rb


(Kürzung)
module CustomCops
  class SpellInconsistency < RuboCop::Cop::Cop
(Kürzung)
    NODE_TYPES = %I[str const sym].freeze
    NODE_TYPES.each do |node_type|
      define_method "on_#{node_type}" do |node|
        SPELL_INCONSISTENCIES.each do |wrong_keyword, correct_keyword|
          add_offense(node, message: message(wrong_keyword, correct_keyword)) if node.source.include?(wrong_keyword)
        end
      end
    end

    def on_lvasgn(node)
      target = node.children.first
      SPELL_INCONSISTENCIES.each do |wrong_keyword, correct_keyword|
        add_offense(node, message: message(wrong_keyword, correct_keyword)) if target.match?(/#{wrong_keyword}/)
      end
    end
(Kürzung)
  end
end

Run: raise_hands:

Bei Ausführung als rubocop - nur CustomCops / SpellInconsistency target.rb ...

image.png

Sieht gut aus: tada:

Test (RSpec): Bleistift:

Was wir bisher gemacht haben, ist ... Strings, Symbole, Konstanten und Variablennamen zu überprüfen. In Anbetracht der Syntax von Ruby ... Es gibt Methoden- und Klassennamen, um die Teile zu finden, die behandelt werden sollten. Sie werden danach etwas bemerken ... Dann war es mühsam, den Code einzeln zur Inspektion auszuführen, deshalb wollte ich einen Test schreiben.

Einstellungen: falsch:

Die Dateien unter "spec / support" sind "erforderlich". Es scheint, dass Personen, die versuchen, die Anwendung später aufzurufen, häufig bereits eingerichtet sind.

spec/spec_helper.rb


RSpec.configure do |config|
(Kürzung)
  Dir["#{__dir__}/support/**/*.rb"].sort.each { |f| require f }
end

Ich lade die RSpec-Unterstützung für Rubocop und lade das von mir hinzugefügte custom_cop.

spec/support/rubocop.rb


# frozen_string_literal: true

require 'rubocop'
require 'rubocop/rspec/support'
Dir["#{__dir__}/../../lib/**/*.rb"].sort.each { |f| require f }

RSpec.configure do |config|
  config.include(RuboCop::RSpec::ExpectOffense)
end

Test: Bleistift:

Der im Prüfzielcode geschriebene Code wird in den Test übertragen, und es wird auch beschrieben, wie die Ausgabe bei der Ausführung von Rubocop ausgeführt wird.

spec/lib/custom_cops/spell_inconstency_spec.rb


# frozen_string_literal: true

RSpec.describe CustomCops::SpellInconsistency do
  subject(:cop) { described_class.new }

  it 'Fehler in Zeichenketten erkennen können' do
    expect_offense(<<-RUBY)
      fan_club = 'fanclub'
                 ^^^^^^^^^ Use 'fan_club' instead of 'fanclub'.
    RUBY
  end

  it 'Fehler in Symbolen erkennen können' do
    expect_offense(<<-RUBY)
      fan_club = :fanclub
                 ^^^^^^^^ Use 'fan_club' instead of 'fanclub'.
    RUBY
  end

  it 'Ständige Fehler erkennen können' do
    expect_offense(<<-RUBY)
      fan_club = FANCLUB
                 ^^^^^^^ Use 'FAN_CLUB' instead of 'FANCLUB'.
    RUBY
  end

  it 'Fehler in Variablennamen erkennen können' do
    expect_offense(<<-RUBY)
      fanclub = 'fan_club'
      ^^^^^^^^^^^^^^^^^^^^ Use 'fan_club' instead of 'fanclub'.
    RUBY
  end
end

Run: raise_hands:

Bei Ausführung mit rspec spec / lib / custom_cops / Zauber_inconstency_spec.rb ...

image.png

Sieht gut aus: Daumen hoch:

Konstante Definition

Ich habe die Variablen definiert, aber ich habe die Konstanten noch nicht definiert, also werde ich es tun.

Überprüfen Sie den Zielcode: dart:

Funktionscode für Konstanten- und Methodendefinitionen.

target.rb


FANCLUB = 'a'

ruby-parse

Mit "Ruby-Parse" analysieren.

image.png

Die konstante Definition ist "casgn", ein anderes Format als "lvasgn". Es war der zweite.

custom_cop :cop: Ich habe eine Prüfmethode für "casgn" erstellt.

lib/custom_cops/spell_inconsistency.rb


(Kürzung)
module CustomCops
  class SpellInconsistency < RuboCop::Cop::Cop
(Kürzung)
    def on_casgn(node)
      target = node.children[1]
      SPELL_INCONSISTENCIES.each do |wrong_keyword, correct_keyword|
        add_offense(node, message: message(wrong_keyword, correct_keyword)) if target.match?(/#{wrong_keyword}/)
      end
    end
(Kürzung)
  end
end

Run: raise_hands:

Bei Ausführung als rubocop - nur CustomCops / SpellInconsistency target.rb ...

image.png

Sieht gut aus: Daumen hoch:

Test: Bleistift:

Fügen Sie es dem Test hinzu, damit Sie feststellen, ob es kaputt geht.

spec/lib/custom_cops/spell_inconstency_spec.rb


# frozen_string_literal: true

RSpec.describe CustomCops::SpellInconsistency do
  subject(:cop) { described_class.new }
(Kürzung)
  it 'Fehler in konstanten Namen erkennen können' do
    expect_offense(<<-RUBY)
      FANCLUB = 'fan_club'
      ^^^^^^^^^^^^^^^^^^^^ Use 'FAN_CLUB' instead of 'FANCLUB'.
    RUBY
  end
end

Auffrischen: funkelt:

Zu diesem Zeitpunkt habe ich das Verfahren irgendwie verstanden.

  1. Schreiben Sie das Codestück, das Sie erkennen möchten (= schreiben Sie den Test)
  2. Mit Ruby-Parse analysieren
  3. Implementieren

Wiederholen Sie diesen Vorgang, um die Erkennungsgenauigkeit zu verbessern.

Methodendefinition

Wir werden ihm bei der Definition der Methode einen Namen geben. Schauen wir uns das an. Da es viele Elemente in einer Zeile gibt und es viele Arten von Elementen gibt, werden wir sie in kleine Stücke schneiden.

Methodenname

In Anbetracht eines kurzen Codeteils sieht es aus wie "def set_fanclub; hoge; end".

Ruby-Parse betrachten ...

image.png

Das erste "Kind" von "def" ist so. Es ähnelt dem lvasgn, das beim Zuweisen zu einer Variablen verwendet wird. Verwenden Sie wie bei "str" und "const" "define_method" in einer Schleife, um sie zu gruppieren.

lib/custom_cops/spell_inconsistency.rb


(Kürzung)
module CustomCops
  class SpellInconsistency < RuboCop::Cop::Cop
(Kürzung)
    NODE_TYPES_ONE = %I[str const sym].freeze
    NODE_TYPES_FIRST_CHILD = %I[lvasgn def].freeze

    NODE_TYPES_ONE.each do |node_type|
      define_method "on_#{node_type}" do |node|
        SPELL_INCONSISTENCIES.each do |wrong_keyword, correct_keyword|
          add_offense(node, message: message(wrong_keyword, correct_keyword)) if node.source.include?(wrong_keyword)
        end
      end
    end

    NODE_TYPES_FIRST_CHILD.each do |node_type|
      define_method "on_#{node_type}" do |node|
        target = node.children.first
        SPELL_INCONSISTENCIES.each do |wrong_keyword, correct_keyword|
          add_offense(node, message: message(wrong_keyword, correct_keyword)) if target.match?(/#{wrong_keyword}/)
        end
      end
    end
(Kürzung)
  end
end

Streit

Das Argument ist "arg", das die gleiche Form wie "lvasgn" hat, also werden wir es zusammenfassen.

image.png

lib/custom_cops/spell_inconsistency.rb


(Kürzung)
module CustomCops
  class SpellInconsistency < RuboCop::Cop::Cop
(Kürzung)
    NODE_TYPES_FIRST_CHILD = %I[lvasgn def arg].freeze
(Kürzung)
  end
end

Standardwert des Arguments

Sie können den Argumenten Standardwerte zuweisen, aber da sie mit "str", "sym" bzw. "const" identisch sind, fügen Sie nichts hinzu.

image.png

Schlüsselwortargument

Das Schlüsselwortargument lautet "kwarg" und hat dieselbe Form wie "lvasgn". Wir werden es also zusammenfassen.

image.png

lib/custom_cops/spell_inconsistency.rb


(Kürzung)
module CustomCops
  class SpellInconsistency < RuboCop::Cop::Cop
(Kürzung)
    NODE_TYPES_FIRST_CHILD = %I[lvasgn def arg kwarg].freeze
(Kürzung)
  end
end

Argument für Schlüsselwortoption

Wenn der Standardwert für das Schlüsselwortargument angegeben wird, lautet der Argumentname "kwoptarg". Dies ist die gleiche Form wie "lvasgn", daher werden wir ihn zusammenfassen.

image.png

lib/custom_cops/spell_inconsistency.rb


(Kürzung)
module CustomCops
  class SpellInconsistency < RuboCop::Cop::Cop
(Kürzung)
    NODE_TYPES_FIRST_CHILD = %I[lvasgn def arg kwarg kwoptarg].freeze
(Kürzung)
  end
end

Hash

Ich erinnerte mich an das Schlüsselwortargument der Methodendefinition. Es wurde von demselben Symbol unterstützt.

image.png

Klassendefinition, Moduldefinition

Der Klassenname und der Modulname waren const.

image.png

image.png

Versuch es

Ich bin so weit gekommen ... Ich habe das Gefühl, dass es immer noch viele Auslassungen gibt. Da ich jedoch einen Test schreibe, habe ich das Gefühl, dass ich ihn basierend auf dem, was mir aufgefallen ist, schrittweise erweitern kann.

Aber ... es war super einfach, aber es war wahnsinnig schwierig. Auf keinen Fall werde ich die Syntaxanalyse beißen ... Ich habe mich jedoch mit [Rubocop] angefreundet (https://github.com/rubocop-hq/rubocop). Wenn Sie eine Regel erstellen möchten, an die Sie sich mit Ihrem Arbeitscode nicht erinnern können, werde ich es versuchen.

Referenz

Recommended Posts

Lassen Sie uns ein custom_cop erstellen, das auf das Schütteln des Namens hinweist
Machen Sie einen Rand links vom TextField
Lass uns einen Roboter bauen! "Eine einfache Demo von Java AWT Robot"
Erstellen wir eine Taschenrechner-App mit Java ~ Zeigen Sie das Anwendungsfenster an
Werfen wir einen Blick auf den Bildschirm von Quant Analyzer!
Lassen Sie uns die App verbessern
Machen Sie die Code-Rechtschreibprüfung mit der Ruby-Sprache kompatibel, die auf Rechtschreibfehler mit VS-Code hinweist.
Lassen Sie uns eine Kombination ohne Duplizierung erstellen. | Berechnen Sie zunächst die Gesamtzahl
Ein Memo, das die einfache Chat-Anwendung von Node.js + socket.io enthält
Eine Geschichte, die mit der Einführung von Web Apple Pay zu kämpfen hatte
So identifizieren Sie den Pfad, auf dem leicht Fehler gemacht werden können
Lassen Sie uns eine TODO-App in Java 5 erstellen. Schalten Sie die Anzeige von TODO um
Schreiben wir einen Code, der einfach zu pflegen ist (Teil 2)
Dies und das von JDK
Ein Memorandum über das FizzBuzz-Problem
Fälle, in denen Benutzer beim Bearbeiten einer App abgemeldet sind (Rails)
Ein Memo, das nüchtern von der Anfrage nach mehrteiligen / Formulardaten abhängig war
Eine Sammlung von Phrasen, die das "unterschiedliche Gefühl" von Java und JavaScript beeindruckt
Lassen Sie uns das Ergebnis der Analyse von Java-Bytecode in einem Klassendiagramm ausdrücken
So finden Sie heraus, welche Java-Version der Klassendatei kompiliert wurde
Beispielprogramm, das den Hashwert einer Datei in Java zurückgibt