dark_finger 0.3.1 → 0.6.1
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 +5 -5
- data/.github/dark_finger.jpg +0 -0
- data/.gitignore +1 -0
- data/README.md +8 -56
- data/docs/migration_constants.md +62 -0
- data/docs/model_structure.md +145 -0
- data/lib/dark_finger.rb +2 -0
- data/lib/dark_finger/version.rb +1 -1
- data/lib/rubocop/cop/dark_finger/active_model_node_decorator.rb +7 -0
- data/lib/rubocop/cop/dark_finger/migration_constants.rb +69 -0
- data/lib/rubocop/cop/dark_finger/model_structure.rb +11 -5
- data/lib/rubocop/cop/dark_finger/module_ancestor_chain_extractor.rb +25 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c387170753bd389d4df0457821fa9e56bf06f1d609e73ea2b4864753e9e09816
|
4
|
+
data.tar.gz: 12946aef0ce96a285a69714a8bb5f830335b47c4509b69151734a8523df76fea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0db6984e3639dd26fd5db716e94055dcbe481fb89fc947778f8b34a250b2b0fd042ab710ba5b6bba1351b1c5fde2e4dcb3d73234c4eae77ab5dc87d407b9b2c0
|
7
|
+
data.tar.gz: 334320e100727d04675b0cdc3cbf92d9ce597783090fe78d4e2ab08d379567412e8667d93981408c66c87edf86df416d42e17a4f66d5c2461688226c69044fbb
|
Binary file
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,23 +1,13 @@
|
|
1
|
-
|
1
|
+

|
2
2
|
|
3
|
-
A Rubocop extension
|
4
|
-
check that elements appear together, in the right order, and commented as
|
5
|
-
desired.
|
3
|
+
A Rubocop extension containing two cops:
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
* associations
|
10
|
-
* attributes
|
11
|
-
* callbacks
|
12
|
-
* constants
|
13
|
-
* enums
|
14
|
-
* includes
|
15
|
-
* modules
|
16
|
-
* scopes
|
17
|
-
* validations
|
18
|
-
* class_methods
|
19
|
-
* instance_methods
|
5
|
+
1. [Model Structure](docs/model_structure.md). Keep those "macro" methods at the top
|
6
|
+
of your model files clean and consistent across the project.
|
20
7
|
|
8
|
+
2. [Migration Constants](docs/migration_constants.md). Prevent a common source of
|
9
|
+
migration breakage - dependencies on things that are likely to change by the
|
10
|
+
time the migration is actually run.
|
21
11
|
|
22
12
|
## Installation
|
23
13
|
|
@@ -37,45 +27,7 @@ Or install it yourself as:
|
|
37
27
|
|
38
28
|
## Usage
|
39
29
|
|
40
|
-
|
41
|
-
add your desired config.
|
42
|
-
|
43
|
-
For example, here is the default config:
|
44
|
-
|
45
|
-
```ruby
|
46
|
-
# in .rubocop.yml
|
47
|
-
|
48
|
-
|
49
|
-
# this is required
|
50
|
-
require: dark_finger
|
51
|
-
|
52
|
-
DarkFinger/ModelStructure:
|
53
|
-
Include:
|
54
|
-
- 'app/models/*'
|
55
|
-
required_order:
|
56
|
-
- module
|
57
|
-
- include
|
58
|
-
- enum
|
59
|
-
- constant
|
60
|
-
- association
|
61
|
-
- validation
|
62
|
-
- scope
|
63
|
-
- attributes
|
64
|
-
- callback
|
65
|
-
- class_method
|
66
|
-
- instance_method
|
67
|
-
required_comments:
|
68
|
-
association: '# Relationships'
|
69
|
-
attribute: '# Attributes'
|
70
|
-
callback: '# Callbacks'
|
71
|
-
constant: '# Constants'
|
72
|
-
enum: '# Enums'
|
73
|
-
include: '# Includes'
|
74
|
-
module: '# Modules'
|
75
|
-
scope: '# Scopes'
|
76
|
-
validation: '# Validations'
|
77
|
-
|
78
|
-
```
|
30
|
+
See the guides for each of the Cops for details.
|
79
31
|
|
80
32
|
## License
|
81
33
|
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# The Migration Constants Cop
|
2
|
+
|
3
|
+
In our rails migration files we don't want dependencies on, for example,
|
4
|
+
ActiveRecord model files.
|
5
|
+
|
6
|
+
This is because migration files should be "timeless" and able to run at any
|
7
|
+
point in the future. Our model files change very frequently - and therefore
|
8
|
+
cannot be depended on directly. We must redeclare the model inside the
|
9
|
+
migration file.
|
10
|
+
|
11
|
+
For example:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
# BAD :'(
|
15
|
+
#
|
16
|
+
# This migration depends on `SomeModel` and `.some_scope`. When this
|
17
|
+
# migration is actually run, either of those things could have changed name,
|
18
|
+
# or perhaps `some_scope` might behave differently by then or have been
|
19
|
+
# deleted.
|
20
|
+
class FooMigration < ActiveRecord::Migration[5.1]
|
21
|
+
def up
|
22
|
+
SomeModel.some_scope.each do
|
23
|
+
# stuff
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# GOOD :-D
|
29
|
+
#
|
30
|
+
# This migration has no external dependencies on our app. It is unlikely to
|
31
|
+
# break in the future as our app changes.
|
32
|
+
class FooMigration < ActiveRecord::Migration[5.1]
|
33
|
+
class SomeModel < ActiveRecord::Base
|
34
|
+
scope :some_scope, -> { ... }
|
35
|
+
end
|
36
|
+
|
37
|
+
def up
|
38
|
+
SomeModel.some_scope.each do
|
39
|
+
# stuff
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
This cop will issue warnings if a migration file depends on certain constants
|
46
|
+
(like model files) that it doesn't declare.
|
47
|
+
|
48
|
+
## Usage
|
49
|
+
|
50
|
+
Install the gem and then add this to your `.rubocop.yml` file:
|
51
|
+
|
52
|
+
```yaml
|
53
|
+
# this is required
|
54
|
+
require: dark_finger
|
55
|
+
|
56
|
+
DarkFinger/MigrationConstants:
|
57
|
+
Include:
|
58
|
+
- 'db/migrate/*.rb'
|
59
|
+
whitelisted_constants:
|
60
|
+
- 'MyConstant'
|
61
|
+
- 'MyOtherConstant'
|
62
|
+
```
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# The Model Structure Cop
|
2
|
+
|
3
|
+
At work we've found that, as our model files grow in size, there are many
|
4
|
+
"macro" methods (scopes, validations, etc) at the top that become messy and
|
5
|
+
inconsistent across files.
|
6
|
+
|
7
|
+
This cop will issue warnings if the various model elements:
|
8
|
+
|
9
|
+
1. Aren't grouped together
|
10
|
+
2. The groups don't appear in the right order
|
11
|
+
3. The groups aren't commented properly
|
12
|
+
|
13
|
+
This is the kind of model file that we like. Notice how all the model elements
|
14
|
+
are grouped, commented, and ordered (although ordering is not visible from just
|
15
|
+
one file):
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
class Horse < ApplicationRecord
|
19
|
+
## Includes ##
|
20
|
+
include GallopingMagicPowers
|
21
|
+
include LazerEyes
|
22
|
+
|
23
|
+
## Enums ##
|
24
|
+
enum breed: %i[thoroughbred
|
25
|
+
arabian
|
26
|
+
american quarter horse
|
27
|
+
clydesdale
|
28
|
+
mustang]
|
29
|
+
|
30
|
+
## Associations ##
|
31
|
+
has_many :legs
|
32
|
+
belongs_to :brain
|
33
|
+
belongs_to :saddle
|
34
|
+
|
35
|
+
## Validations ##
|
36
|
+
validates_presence_of :breed
|
37
|
+
validates_presence_of :age
|
38
|
+
|
39
|
+
## Scopes ##
|
40
|
+
scope :dead, -> { where(state: 'dead') }
|
41
|
+
scope :alive, -> { where(state: 'alive') }
|
42
|
+
|
43
|
+
## Attributes ##
|
44
|
+
attr_accessor :promote_to_demon_horse
|
45
|
+
|
46
|
+
## Callbacks ##
|
47
|
+
after_save :callbacks_are_evil_you_should_be_ashamed
|
48
|
+
|
49
|
+
## Misc ##
|
50
|
+
serialize :nose_hairs, Array
|
51
|
+
acts_as_taggable
|
52
|
+
|
53
|
+
def self.foobario
|
54
|
+
# foo
|
55
|
+
end
|
56
|
+
|
57
|
+
def gallop_hard
|
58
|
+
# ...
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
```
|
63
|
+
|
64
|
+
## Usage and Configuration
|
65
|
+
|
66
|
+
Install the gem. Then, in your `.rubycop.yml` file, require `dark_finger` and
|
67
|
+
add your desired config.
|
68
|
+
|
69
|
+
For example:
|
70
|
+
|
71
|
+
```yaml
|
72
|
+
# in .rubocop.yml
|
73
|
+
|
74
|
+
# this is required
|
75
|
+
require: dark_finger
|
76
|
+
|
77
|
+
DarkFinger/ModelStructure:
|
78
|
+
|
79
|
+
# this is also required
|
80
|
+
Include:
|
81
|
+
- 'app/models/*'
|
82
|
+
|
83
|
+
# specify the order that the model elements must appear in
|
84
|
+
required_order:
|
85
|
+
- module
|
86
|
+
- include
|
87
|
+
- enum
|
88
|
+
- constant
|
89
|
+
- association
|
90
|
+
- validation
|
91
|
+
- scope
|
92
|
+
- attributes
|
93
|
+
- callback
|
94
|
+
- misc
|
95
|
+
- constructor
|
96
|
+
- class_method
|
97
|
+
- instance_method
|
98
|
+
|
99
|
+
# specify the comments that must appear above each group of model elements
|
100
|
+
required_comments:
|
101
|
+
association: '## Relationships ##'
|
102
|
+
attribute: '## Attributes ##'
|
103
|
+
callback: '## Callbacks ##'
|
104
|
+
constant: '## Constants ##'
|
105
|
+
enum: '## Enums ##'
|
106
|
+
include: '## Includes ##'
|
107
|
+
misc: '## Misc ##'
|
108
|
+
module: '## Modules ##'
|
109
|
+
scope: '## Scopes ##'
|
110
|
+
validation: '## Validations ##'
|
111
|
+
|
112
|
+
# specify the methods that are categorized as "misc"
|
113
|
+
misc_method_names:
|
114
|
+
- acts_as_list
|
115
|
+
- acts_as_taggable
|
116
|
+
- friendly_id
|
117
|
+
- serialize
|
118
|
+
- workflow
|
119
|
+
```
|
120
|
+
|
121
|
+
Supported model elements:
|
122
|
+
|
123
|
+
|
124
|
+
| Config key | Description (when not obvious) |
|
125
|
+
|-----------------|--------------------------------------------|
|
126
|
+
| association | |
|
127
|
+
| attribute | `attr_reader` and friends |
|
128
|
+
| callback | `after_save` et al. |
|
129
|
+
| class_method | |
|
130
|
+
| constant | |
|
131
|
+
| constructor | |
|
132
|
+
| enum | |
|
133
|
+
| include | |
|
134
|
+
| instance_method | |
|
135
|
+
| misc | This is a configurable set of method calls |
|
136
|
+
| module | Any `module Foo; ...; end` declarations |
|
137
|
+
| scope | Any `scope` or `default_scope` calls |
|
138
|
+
| validation | |
|
139
|
+
|
140
|
+
|
141
|
+
Any model elements that are not included in "required order" will be ignored.
|
142
|
+
|
143
|
+
Dark Finger ignores everything that appears after `private`. This may change in
|
144
|
+
future, but for now we have just agreed that anything goes in the private
|
145
|
+
section of our models.
|
data/lib/dark_finger.rb
CHANGED
@@ -2,3 +2,5 @@ require 'rubocop'
|
|
2
2
|
require "dark_finger/version"
|
3
3
|
require File.dirname(__FILE__) + "/rubocop/cop/dark_finger/model_structure"
|
4
4
|
require File.dirname(__FILE__) + "/rubocop/cop/dark_finger/active_model_node_decorator"
|
5
|
+
require File.dirname(__FILE__) + "/rubocop/cop/dark_finger/migration_constants"
|
6
|
+
require File.dirname(__FILE__) + "/rubocop/cop/dark_finger/module_ancestor_chain_extractor"
|
data/lib/dark_finger/version.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
1
3
|
module RuboCop
|
2
4
|
module Cop
|
3
5
|
module DarkFinger
|
@@ -60,6 +62,11 @@ module RuboCop
|
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
65
|
+
def private_declaration?
|
66
|
+
# need to check respond_to since this may not be called only "on_send"
|
67
|
+
respond_to?(:method_name) && method_name == :private && receiver.nil?
|
68
|
+
end
|
69
|
+
|
63
70
|
private
|
64
71
|
|
65
72
|
def nested_in_with_options?
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cop
|
3
|
+
module DarkFinger
|
4
|
+
class MigrationConstants < ::RuboCop::Cop::Cop
|
5
|
+
DEFAULT_ALLOWED_CONSTANTS = [
|
6
|
+
'Migration',
|
7
|
+
'ActiveRecord',
|
8
|
+
'ActiveRecord::Migration',
|
9
|
+
'ActiveRecord::Base',
|
10
|
+
'ActiveRecord::IrreversibleMigration'
|
11
|
+
]
|
12
|
+
|
13
|
+
attr_reader :allowed_constants
|
14
|
+
|
15
|
+
def initialize(*args, options)
|
16
|
+
super(*args)
|
17
|
+
@whitelisted_constants = options[:whitelisted_constants] || cop_config['whitelisted_constants'] || []
|
18
|
+
@allowed_constants =
|
19
|
+
DEFAULT_ALLOWED_CONSTANTS +
|
20
|
+
allowed_top_level_constants +
|
21
|
+
@whitelisted_constants
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_const(node)
|
25
|
+
return if allowed_constants.include?(node.const_name)
|
26
|
+
add_offense(node, message: %Q(Undeclared constant: "#{node.const_name}"))
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_casgn(node)
|
30
|
+
add_allowed_constant(node.children[1])
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_class(node)
|
34
|
+
add_allowed_constant(node.children.first.const_name)
|
35
|
+
add_module_parent_chain_for(node)
|
36
|
+
end
|
37
|
+
|
38
|
+
def on_module(node)
|
39
|
+
add_allowed_constant(node.children.first.const_name)
|
40
|
+
add_module_parent_chain_for(node)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def allowed_top_level_constants
|
46
|
+
Module.constants.map(&:to_s) - top_level_model_classes_and_containing_modules
|
47
|
+
end
|
48
|
+
|
49
|
+
def top_level_model_classes_and_containing_modules
|
50
|
+
return [] unless Object.const_defined?('ActiveRecord::Base')
|
51
|
+
|
52
|
+
::ActiveRecord::Base.descendants.map do |klass|
|
53
|
+
klass.name.sub(/::.*/, '').to_s
|
54
|
+
end.uniq
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_allowed_constant(constant)
|
58
|
+
@allowed_constants << constant.to_s
|
59
|
+
@allowed_constants.uniq!
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_module_parent_chain_for(node)
|
63
|
+
chain = ModuleAncestorChainExtractor.new(node).perform
|
64
|
+
add_allowed_constant(chain)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -68,12 +68,12 @@ module RuboCop
|
|
68
68
|
|
69
69
|
attr_reader :required_order, :required_comments, :misc_method_names
|
70
70
|
|
71
|
-
def initialize(*args,
|
71
|
+
def initialize(*args, options)
|
72
72
|
super
|
73
73
|
@class_elements_seen = []
|
74
|
-
@required_order = required_order || cop_config['required_order'] || DEFAULT_REQUIRED_ORDER
|
75
|
-
@required_comments = required_comments || cop_config['required_comments'] || DEFAULT_REQUIRED_COMMENTS
|
76
|
-
@misc_method_names = misc_method_names || cop_config['misc_method_names'] || DEFAULT_MISC_METHOD_NAMES
|
74
|
+
@required_order = options[:required_order] || cop_config['required_order'] || DEFAULT_REQUIRED_ORDER
|
75
|
+
@required_comments = options[:required_comments] || cop_config['required_comments'] || DEFAULT_REQUIRED_COMMENTS
|
76
|
+
@misc_method_names = options[:misc_method_names] || cop_config['misc_method_names'] || DEFAULT_MISC_METHOD_NAMES
|
77
77
|
|
78
78
|
# symbolize configs
|
79
79
|
@required_order.map!(&:to_sym)
|
@@ -114,10 +114,16 @@ module RuboCop
|
|
114
114
|
|
115
115
|
def process_node(node, seen_element: nil)
|
116
116
|
return if @order_violation_reported
|
117
|
+
return if @seen_private_declaration
|
117
118
|
|
118
119
|
node = ActiveModelNodeDecorator.new(node, misc_method_names: misc_method_names)
|
119
|
-
seen_element ||= node.node_type
|
120
120
|
|
121
|
+
if node.private_declaration?
|
122
|
+
@seen_private_declaration = true
|
123
|
+
return
|
124
|
+
end
|
125
|
+
|
126
|
+
seen_element ||= node.node_type
|
121
127
|
return unless seen_element
|
122
128
|
|
123
129
|
return if node.ignore_due_to_nesting?
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cop
|
3
|
+
module DarkFinger
|
4
|
+
class ModuleAncestorChainExtractor
|
5
|
+
attr_reader :node
|
6
|
+
|
7
|
+
def initialize(node)
|
8
|
+
@node = node
|
9
|
+
end
|
10
|
+
|
11
|
+
def perform
|
12
|
+
module_chain = [node.children.first.const_name]
|
13
|
+
|
14
|
+
current_node = node
|
15
|
+
while current_node.parent && current_node.parent.module_type?
|
16
|
+
module_chain << current_node.parent.children.first.const_name
|
17
|
+
current_node = current_node.parent
|
18
|
+
end
|
19
|
+
|
20
|
+
module_chain.reverse.join("::")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dark_finger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Professor Wang Matrix PhD
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2020-07-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -89,6 +89,7 @@ executables: []
|
|
89
89
|
extensions: []
|
90
90
|
extra_rdoc_files: []
|
91
91
|
files:
|
92
|
+
- ".github/dark_finger.jpg"
|
92
93
|
- ".gitignore"
|
93
94
|
- ".rspec"
|
94
95
|
- ".travis.yml"
|
@@ -99,10 +100,14 @@ files:
|
|
99
100
|
- bin/console
|
100
101
|
- bin/setup
|
101
102
|
- dark_finger.gemspec
|
103
|
+
- docs/migration_constants.md
|
104
|
+
- docs/model_structure.md
|
102
105
|
- lib/dark_finger.rb
|
103
106
|
- lib/dark_finger/version.rb
|
104
107
|
- lib/rubocop/cop/dark_finger/active_model_node_decorator.rb
|
108
|
+
- lib/rubocop/cop/dark_finger/migration_constants.rb
|
105
109
|
- lib/rubocop/cop/dark_finger/model_structure.rb
|
110
|
+
- lib/rubocop/cop/dark_finger/module_ancestor_chain_extractor.rb
|
106
111
|
homepage: https://github.com/the-suBLAM-executive-council/dark_finger
|
107
112
|
licenses:
|
108
113
|
- MIT
|
@@ -122,8 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
127
|
- !ruby/object:Gem::Version
|
123
128
|
version: '0'
|
124
129
|
requirements: []
|
125
|
-
|
126
|
-
rubygems_version: 2.5.2.1
|
130
|
+
rubygems_version: 3.1.2
|
127
131
|
signing_key:
|
128
132
|
specification_version: 4
|
129
133
|
summary: ActiveModel layout cop for Rubocop
|