rubocop-rhino-project 0.20.0.alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/config/default.yml +21 -0
- data/lib/rubocop/cop/rhino_project/duplicate_rhino_references.rb +49 -0
- data/lib/rubocop/cop/rhino_project_cops.rb +3 -0
- data/lib/rubocop/rhino_project/inject.rb +18 -0
- data/lib/rubocop/rhino_project/schema_loader/schema.rb +188 -0
- data/lib/rubocop/rhino_project/schema_loader.rb +50 -0
- data/lib/rubocop/rhino_project/version.rb +17 -0
- data/lib/rubocop/rhino_project.rb +12 -0
- data/lib/rubocop-rhino-project.rb +18 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8d1e3753b313df9f9f1890d4af12074b96c0f7fe843e52d71e252f351e5ede79
|
4
|
+
data.tar.gz: 55693f13ed9852996e5fd026d76fe4d5bbdfcf24e9d551d3691d41ca8b33fcb8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fb134e396dd3ff56d92c27e6e2212f991abb34b9fa3abaa583b25b4d1722ba9d8662da8f633619526422872dfb85e9fe53be208a7c71621e3cdf81bd4234ebca
|
7
|
+
data.tar.gz: 22a5209ac3c8171e456574d02476a0e4be7b3f1377816bf5ec42ec554e6fe5c9dcd403ea1352e8295131d10715d045dfda9979aef35f387614d273d07f1e10e0
|
data/config/default.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Common configuration.
|
2
|
+
|
3
|
+
inherit_mode:
|
4
|
+
merge:
|
5
|
+
- Exclude
|
6
|
+
|
7
|
+
AllCops:
|
8
|
+
Exclude:
|
9
|
+
- app/assets/**/*
|
10
|
+
- bin/*
|
11
|
+
# Exclude db/schema.rb and db/[CONFIGURATION_NAMESPACE]_schema.rb by default.
|
12
|
+
# See: https://guides.rubyonrails.org/active_record_multiple_databases.html#setting-up-your-application
|
13
|
+
- db/*schema.rb
|
14
|
+
- log/**/*
|
15
|
+
- public/**/*
|
16
|
+
- storage/**/*
|
17
|
+
|
18
|
+
RhinoProject/DuplicateRhinoReferences:
|
19
|
+
Description: ''
|
20
|
+
Enabled: true
|
21
|
+
SafeAutoCorrect: false
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RhinoProject
|
6
|
+
# Don't call `rhino_references` method more than once in the same class.
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# class Blog < ApplicationRecord
|
12
|
+
# belongs_to :user
|
13
|
+
# belongs_to :category
|
14
|
+
#
|
15
|
+
# rhino_references %i[user]
|
16
|
+
# rhino_references %i[user category]
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# class Blog < ApplicationRecord
|
21
|
+
# belongs_to :user
|
22
|
+
# belongs_to :category
|
23
|
+
#
|
24
|
+
# rhino_references %i[user category]
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
class DuplicateRhinoReferences < Base
|
28
|
+
MSG = "Avoid calling `rhino_references` method more than once in the same class."
|
29
|
+
|
30
|
+
def_node_search :rhino_references_call, "(send nil? :rhino_references (array ...))"
|
31
|
+
|
32
|
+
def on_class(node)
|
33
|
+
check_for_multiple_calls(node)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def check_for_multiple_calls(node)
|
38
|
+
rhino_references_calls = rhino_references_call(node)
|
39
|
+
|
40
|
+
return if rhino_references_calls.count <= 1
|
41
|
+
|
42
|
+
rhino_references_calls.each do |offending_call|
|
43
|
+
add_offense(offending_call, message: MSG)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module RhinoProject
|
5
|
+
# Because RuboCop doesn't yet support plugins, we have to monkey patch in a
|
6
|
+
# bit of our configuration.
|
7
|
+
module Inject
|
8
|
+
def self.defaults!
|
9
|
+
path = CONFIG_DEFAULT.to_s
|
10
|
+
hash = ConfigLoader.send(:load_yaml_configuration, path)
|
11
|
+
config = Config.new(hash, path).tap(&:make_excludes_absolute)
|
12
|
+
puts "configuration from #{path}" if ConfigLoader.debug?
|
13
|
+
config = ConfigLoader.merge_with_default(config, path, unset_nil: false)
|
14
|
+
ConfigLoader.instance_variable_set(:@default_configuration, config)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module RhinoProject
|
5
|
+
module SchemaLoader
|
6
|
+
# Represent db/schema.rb
|
7
|
+
class Schema
|
8
|
+
attr_reader :tables, :add_indices
|
9
|
+
|
10
|
+
def initialize(ast)
|
11
|
+
@tables = []
|
12
|
+
@add_indices = []
|
13
|
+
|
14
|
+
build!(ast)
|
15
|
+
end
|
16
|
+
|
17
|
+
def table_by(name:)
|
18
|
+
tables.find do |table|
|
19
|
+
table.name == name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_indices_by(table_name:)
|
24
|
+
add_indices.select do |add_index|
|
25
|
+
add_index.table_name == table_name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def build!(ast)
|
31
|
+
raise "Unexpected type: #{ast.type}" unless ast.block_type?
|
32
|
+
return unless ast.body
|
33
|
+
|
34
|
+
each_table(ast) do |table_def|
|
35
|
+
next unless table_def.method?(:create_table)
|
36
|
+
|
37
|
+
@tables << Table.new(table_def)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Compatibility for Rails 4.2.
|
41
|
+
each_add_index(ast) do |add_index_def|
|
42
|
+
@add_indices << AddIndex.new(add_index_def)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def each_table(ast)
|
47
|
+
case ast.body.type
|
48
|
+
when :begin
|
49
|
+
ast.body.children.each do |node|
|
50
|
+
next unless node.block_type? && node.method?(:create_table)
|
51
|
+
|
52
|
+
yield(node)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
yield ast.body
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def each_add_index(ast)
|
60
|
+
ast.body.children.each do |node|
|
61
|
+
next unless node.respond_to?(:send_type?)
|
62
|
+
next if !node&.send_type? || !node.method?(:add_index)
|
63
|
+
|
64
|
+
yield(node)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Represent a table
|
70
|
+
class Table
|
71
|
+
attr_reader :name, :columns, :indices
|
72
|
+
|
73
|
+
def initialize(node)
|
74
|
+
@name = node.send_node.first_argument.value
|
75
|
+
@columns = build_columns(node)
|
76
|
+
@indices = build_indices(node)
|
77
|
+
end
|
78
|
+
|
79
|
+
def with_column?(name:)
|
80
|
+
@columns.any? { |c| c.name == name }
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
def build_columns(node)
|
85
|
+
each_content(node).filter_map do |child|
|
86
|
+
next unless child&.send_type?
|
87
|
+
next if child.method?(:index)
|
88
|
+
|
89
|
+
Column.new(child)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def build_indices(node)
|
94
|
+
each_content(node).filter_map do |child|
|
95
|
+
next unless child&.send_type?
|
96
|
+
next unless child.method?(:index)
|
97
|
+
|
98
|
+
Index.new(child)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def each_content(node, &block)
|
103
|
+
return enum_for(__method__, node) unless block
|
104
|
+
|
105
|
+
case node.body&.type
|
106
|
+
when :begin
|
107
|
+
node.body.children.each(&block)
|
108
|
+
else
|
109
|
+
yield(node.body)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Represent a column
|
115
|
+
class Column
|
116
|
+
attr_reader :name, :type, :not_null
|
117
|
+
|
118
|
+
def initialize(node)
|
119
|
+
@name = node.first_argument.str_content
|
120
|
+
@type = node.method_name
|
121
|
+
@not_null = nil
|
122
|
+
|
123
|
+
analyze_keywords!(node)
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
def analyze_keywords!(node)
|
128
|
+
pairs = node.last_argument
|
129
|
+
return unless pairs.hash_type?
|
130
|
+
|
131
|
+
pairs.each_pair do |k, v|
|
132
|
+
@not_null = !v.true_type? if k.value == :null
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Represent an index
|
138
|
+
class Index
|
139
|
+
attr_reader :name, :columns, :expression, :unique
|
140
|
+
|
141
|
+
def initialize(node)
|
142
|
+
@columns, @expression = build_columns_or_expr(node.first_argument)
|
143
|
+
@unique = nil
|
144
|
+
|
145
|
+
analyze_keywords!(node)
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
def build_columns_or_expr(columns)
|
150
|
+
if columns.array_type?
|
151
|
+
[columns.values.map(&:value), nil]
|
152
|
+
else
|
153
|
+
[[], columns.value]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def analyze_keywords!(node)
|
158
|
+
pairs = node.last_argument
|
159
|
+
return unless pairs.hash_type?
|
160
|
+
|
161
|
+
pairs.each_pair do |k, v|
|
162
|
+
case k.value
|
163
|
+
when :name
|
164
|
+
@name = v.value
|
165
|
+
when :unique
|
166
|
+
@unique = true
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Represent an `add_index`
|
173
|
+
class AddIndex < Index
|
174
|
+
attr_reader :table_name
|
175
|
+
|
176
|
+
def initialize(node)
|
177
|
+
super(node)
|
178
|
+
|
179
|
+
@table_name = node.first_argument.value
|
180
|
+
@columns, @expression = build_columns_or_expr(node.arguments[1])
|
181
|
+
@unique = nil
|
182
|
+
|
183
|
+
analyze_keywords!(node)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module RhinoProject
|
5
|
+
# It loads db/schema.rb and return Schema object.
|
6
|
+
# Cops refers database schema information with this module.
|
7
|
+
module SchemaLoader
|
8
|
+
extend self
|
9
|
+
|
10
|
+
# It parses `db/schema.rb` and return it.
|
11
|
+
# It returns `nil` if it can't find `db/schema.rb`.
|
12
|
+
# So a cop that uses the loader should handle `nil` properly.
|
13
|
+
#
|
14
|
+
# @return [Schema, nil]
|
15
|
+
def load(target_ruby_version, parser_engine)
|
16
|
+
return @load if defined?(@load)
|
17
|
+
|
18
|
+
@load = load!(target_ruby_version, parser_engine)
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset!
|
22
|
+
return unless instance_variable_defined?(:@load)
|
23
|
+
|
24
|
+
remove_instance_variable(:@load)
|
25
|
+
end
|
26
|
+
|
27
|
+
def db_schema_path
|
28
|
+
path = Pathname.pwd
|
29
|
+
until path.root?
|
30
|
+
schema_path = path.join("db/schema.rb")
|
31
|
+
return schema_path if schema_path.exist?
|
32
|
+
|
33
|
+
path = path.join("../").cleanpath
|
34
|
+
end
|
35
|
+
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def load!(target_ruby_version, parser_engine)
|
41
|
+
path = db_schema_path
|
42
|
+
return unless path
|
43
|
+
|
44
|
+
ast = RuboCop::ProcessedSource.new(File.read(path), target_ruby_version, path, parser_engine:).ast
|
45
|
+
|
46
|
+
Schema.new(ast) if ast
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubocopRhinoProject
|
4
|
+
# Returns the currently loaded version of Rhino core as a +Gem::Version+.
|
5
|
+
def self.gem_version
|
6
|
+
Gem::Version.new VERSION::STRING
|
7
|
+
end
|
8
|
+
|
9
|
+
module VERSION
|
10
|
+
MAJOR = 0
|
11
|
+
MINOR = 20
|
12
|
+
TINY = 0
|
13
|
+
PRE = "alpha.0"
|
14
|
+
|
15
|
+
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
# RuboCop Rails project namespace
|
5
|
+
module RhinoProject
|
6
|
+
PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
|
7
|
+
CONFIG_DEFAULT = PROJECT_ROOT.join("config", "default.yml").freeze
|
8
|
+
CONFIG = YAML.safe_load(CONFIG_DEFAULT.read, permitted_classes: [Regexp, Symbol]).freeze
|
9
|
+
|
10
|
+
private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rubocop"
|
4
|
+
require "rack/utils"
|
5
|
+
require "active_support/inflector"
|
6
|
+
require "active_support/core_ext/object/blank"
|
7
|
+
|
8
|
+
require_relative "rubocop/rhino_project"
|
9
|
+
require_relative "rubocop/rhino_project/version"
|
10
|
+
require_relative "rubocop/rhino_project/inject"
|
11
|
+
require_relative "rubocop/rhino_project/schema_loader"
|
12
|
+
require_relative "rubocop/rhino_project/schema_loader/schema"
|
13
|
+
|
14
|
+
RuboCop::RhinoProject::Inject.defaults!
|
15
|
+
|
16
|
+
require_relative "rubocop/cop/rhino_project_cops"
|
17
|
+
|
18
|
+
RuboCop::Cop::Style::HashExcept.minimum_target_ruby_version(2.0)
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubocop-rhino-project
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.20.0.alpha.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- JP Rosevear
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-08-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.24.1
|
48
|
+
- - "<"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '2.0'
|
51
|
+
type: :runtime
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 1.24.1
|
58
|
+
- - "<"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '2.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rubocop-ast
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 1.21.0
|
68
|
+
- - "<"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '2.0'
|
71
|
+
type: :runtime
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.21.0
|
78
|
+
- - "<"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '2.0'
|
81
|
+
description: ''
|
82
|
+
email:
|
83
|
+
- jp@codalio.com
|
84
|
+
executables: []
|
85
|
+
extensions: []
|
86
|
+
extra_rdoc_files: []
|
87
|
+
files:
|
88
|
+
- config/default.yml
|
89
|
+
- lib/rubocop-rhino-project.rb
|
90
|
+
- lib/rubocop/cop/rhino_project/duplicate_rhino_references.rb
|
91
|
+
- lib/rubocop/cop/rhino_project_cops.rb
|
92
|
+
- lib/rubocop/rhino_project.rb
|
93
|
+
- lib/rubocop/rhino_project/inject.rb
|
94
|
+
- lib/rubocop/rhino_project/schema_loader.rb
|
95
|
+
- lib/rubocop/rhino_project/schema_loader/schema.rb
|
96
|
+
- lib/rubocop/rhino_project/version.rb
|
97
|
+
homepage: ''
|
98
|
+
licenses:
|
99
|
+
- MIT
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubygems_version: 3.5.11
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: ''
|
120
|
+
test_files: []
|