ducalis 0.5.5 → 0.5.6
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 +4 -4
- data/.codeclimate.yml +1 -1
- data/DOCUMENTATION.md +250 -198
- data/Gemfile.lock +5 -5
- data/README.md +13 -1
- data/lib/ducalis/cops/callbacks_activerecord.rb +12 -5
- data/lib/ducalis/cops/case_mapping.rb +9 -8
- data/lib/ducalis/cops/controllers_except.rb +6 -6
- data/lib/ducalis/cops/keyword_defaults.rb +4 -5
- data/lib/ducalis/cops/module_like_class.rb +4 -5
- data/lib/ducalis/cops/params_passing.rb +5 -5
- data/lib/ducalis/cops/possible_tap.rb +12 -11
- data/lib/ducalis/cops/preferable_methods.rb +11 -5
- data/lib/ducalis/cops/private_instance_assign.rb +11 -4
- data/lib/ducalis/cops/protected_scope_cop.rb +11 -11
- data/lib/ducalis/cops/raise_withour_error_class.rb +4 -5
- data/lib/ducalis/cops/regex_cop.rb +13 -8
- data/lib/ducalis/cops/rest_only_cop.rb +5 -4
- data/lib/ducalis/cops/rubocop_disable.rb +5 -4
- data/lib/ducalis/cops/strings_in_activerecords.rb +7 -6
- data/lib/ducalis/cops/uncommented_gem.rb +6 -5
- data/lib/ducalis/cops/useless_only.rb +16 -16
- data/lib/ducalis/documentation.rb +16 -7
- data/lib/ducalis/version.rb +1 -1
- metadata +2 -2
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ducalis (0.5.
|
4
|
+
ducalis (0.5.6)
|
5
5
|
git (~> 1.3, >= 1.3.0)
|
6
6
|
policial (= 0.0.4)
|
7
7
|
regexp-examples (~> 1.3, >= 1.3.2)
|
@@ -38,8 +38,8 @@ GEM
|
|
38
38
|
multipart-post (2.0.0)
|
39
39
|
octokit (4.7.0)
|
40
40
|
sawyer (~> 0.8.0, >= 0.5.3)
|
41
|
-
parser (2.4.0.
|
42
|
-
ast (~> 2.
|
41
|
+
parser (2.4.0.2)
|
42
|
+
ast (~> 2.3)
|
43
43
|
policial (0.0.4)
|
44
44
|
coffeelint (~> 1.14)
|
45
45
|
eslintrb (~> 2.0)
|
@@ -49,11 +49,11 @@ GEM
|
|
49
49
|
pry (0.11.2)
|
50
50
|
coderay (~> 1.1.0)
|
51
51
|
method_source (~> 0.9.0)
|
52
|
-
public_suffix (3.0.
|
52
|
+
public_suffix (3.0.1)
|
53
53
|
rainbow (2.2.2)
|
54
54
|
rake
|
55
55
|
rake (12.1.0)
|
56
|
-
regexp-examples (1.4.
|
56
|
+
regexp-examples (1.4.1)
|
57
57
|
rspec (3.7.0)
|
58
58
|
rspec-core (~> 3.7.0)
|
59
59
|
rspec-expectations (~> 3.7.0)
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/ducalis)
|
4
4
|
[](https://travis-ci.org/ignat-z/ducalis)
|
5
|
-
[](https://codeclimate.com/github/ignat-z/ducalis/maintainability)
|
6
6
|
|
7
7
|
__Ducalis__ is RuboCop-based static code analyzer for enterprise Rails applications.
|
8
8
|
As __Ducalis__ isn't style checker and could sometimes be false-positive it's not
|
@@ -58,3 +58,15 @@ rack application. All related files are located in the `client/` directory.
|
|
58
58
|
In CLI modes you can provide yours `.ducalis.yml` file based on
|
59
59
|
[default](https://github.com/ignat-z/ducalis/blob/master/config/.ducalis.yml) by
|
60
60
|
`-c` flag or simply putting it in your project directory.
|
61
|
+
|
62
|
+
## Contribution
|
63
|
+
|
64
|
+
To pass your code through the all checks you simply need to run:
|
65
|
+
|
66
|
+
```
|
67
|
+
bundle exec rake
|
68
|
+
```
|
69
|
+
|
70
|
+
Please, do not edit
|
71
|
+
[DOCUMENTATION.md](<https://github.com/ignat-z/ducalis/blob/master/DOCUMENTATION.md>),
|
72
|
+
this file is generating by `bundle exec rake documentation` command.
|
@@ -4,11 +4,18 @@ require 'rubocop'
|
|
4
4
|
|
5
5
|
module Ducalis
|
6
6
|
class CallbacksActiverecord < RuboCop::Cop::Cop
|
7
|
-
OFFENSE =
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
8
|
+
| Please, avoid using of callbacks for models. It's better to
|
9
|
+
| keep models small ("dumb") and instead use "builder" classes
|
10
|
+
| / services: to construct new objects. You can read more
|
11
|
+
| [here](https://medium.com/planet-arkency/a61fd75ab2d3).
|
12
|
+
MESSAGE
|
13
|
+
|
14
|
+
MODELS_CLASS_NAMES = [
|
15
|
+
'ApplicationRecord',
|
16
|
+
'ActiveRecord::Base'
|
17
|
+
].freeze
|
18
|
+
|
12
19
|
METHODS_BLACK_LIST = %i(
|
13
20
|
after_commit
|
14
21
|
after_create
|
@@ -4,14 +4,15 @@ require 'rubocop'
|
|
4
4
|
|
5
5
|
module Ducalis
|
6
6
|
class CaseMapping < RuboCop::Cop::Cop
|
7
|
-
OFFENSE =
|
8
|
-
Try to avoid `case when` statements. You can replace it with a sequence
|
9
|
-
`if... elsif... elsif... else`. For cases where you need to choose
|
10
|
-
large number of possibilities, you can create a dictionary
|
11
|
-
to functions to call by `call`. It's nice to have
|
12
|
-
names, i.e.: `visit_`.
|
13
|
-
|
14
|
-
|
7
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
8
|
+
| Try to avoid `case when` statements. You can replace it with a sequence
|
9
|
+
| of `if... elsif... elsif... else`. For cases where you need to choose
|
10
|
+
| from a large number of possibilities, you can create a dictionary
|
11
|
+
| mapping case values to functions to call by `call`. It's nice to have
|
12
|
+
| prefix for the method names, i.e.: `visit_`.
|
13
|
+
MESSAGE
|
14
|
+
|
15
|
+
DETAILS = %(
|
15
16
|
Usually `case when` statements are using for the next reasons:
|
16
17
|
|
17
18
|
I. Mapping between different values.
|
@@ -3,14 +3,14 @@
|
|
3
3
|
require 'rubocop'
|
4
4
|
|
5
5
|
module Ducalis
|
6
|
-
class ControllersExcept <
|
7
|
-
|
6
|
+
class ControllersExcept < RuboCop::Cop::Cop
|
7
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
8
|
+
| Prefer to use `:only` over `:except` in controllers because it's more
|
9
|
+
| explicit and will be easier to maintain for new developers.
|
10
|
+
MESSAGE
|
11
|
+
|
8
12
|
FILTERS = %i(before_filter after_filter around_filter
|
9
13
|
before_action after_action around_action).freeze
|
10
|
-
OFFENSE = %(
|
11
|
-
Prefer to use `:only` over `:except` in controllers because it's more explicit \
|
12
|
-
and will be easier to maintain for new developers.
|
13
|
-
).strip
|
14
14
|
|
15
15
|
def on_class(node)
|
16
16
|
_classdef_node, superclass, _body = *node
|
@@ -4,11 +4,10 @@ require 'rubocop'
|
|
4
4
|
|
5
5
|
module Ducalis
|
6
6
|
class KeywordDefaults < RuboCop::Cop::Cop
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
).strip
|
7
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
8
|
+
| Prefer to use keyword arguments for defaults. It increases readability
|
9
|
+
| and reduces ambiguities.
|
10
|
+
MESSAGE
|
12
11
|
|
13
12
|
def on_def(node)
|
14
13
|
args = node.type == :defs ? node.to_a[2] : node.to_a[1]
|
@@ -5,11 +5,10 @@ require 'rubocop'
|
|
5
5
|
module Ducalis
|
6
6
|
class ModuleLikeClass < RuboCop::Cop::Cop
|
7
7
|
include RuboCop::Cop::DefNode
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
).strip
|
8
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
9
|
+
| Seems like it will be better to define initialize and pass %<args>s
|
10
|
+
| there instead of each method.
|
11
|
+
MESSAGE
|
13
12
|
|
14
13
|
def on_class(node)
|
15
14
|
_name, inheritance, body = *node
|
@@ -4,12 +4,12 @@ require 'rubocop'
|
|
4
4
|
|
5
5
|
module Ducalis
|
6
6
|
class ParamsPassing < RuboCop::Cop::Cop
|
7
|
-
|
7
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
8
|
+
| It's better to pass already preprocessed params hash to services. Or
|
9
|
+
| you can use `arcane` gem.
|
10
|
+
MESSAGE
|
11
|
+
|
8
12
|
PARAMS_CALL = s(:send, nil, :params)
|
9
|
-
OFFENSE = %(
|
10
|
-
It's better to pass already preprocessed params hash to services. Or you can use
|
11
|
-
`arcane` gem
|
12
|
-
).strip
|
13
13
|
|
14
14
|
def on_send(node)
|
15
15
|
_who, _what, *args = *node
|
@@ -4,20 +4,19 @@ require 'rubocop'
|
|
4
4
|
|
5
5
|
module Ducalis
|
6
6
|
class PossibleTap < RuboCop::Cop::Cop
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
[Related article](<http://seejohncode.com/2012/01/02/ruby-tap-that/>).
|
15
|
-
).strip
|
7
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
8
|
+
| Consider of using `.tap`, default ruby
|
9
|
+
| [method](<https://apidock.com/ruby/Object/tap>)
|
10
|
+
| which allows to replace intermediate variables with block, by this you
|
11
|
+
| are limiting scope pollution and make scope more clear.
|
12
|
+
| [Related article](<http://seejohncode.com/2012/01/02/ruby-tap-that/>).
|
13
|
+
MESSAGE
|
16
14
|
|
17
15
|
PAIRS = {
|
18
16
|
lvar: :lvasgn,
|
19
17
|
ivar: :ivasgn
|
20
18
|
}.freeze
|
19
|
+
|
21
20
|
ASSIGNS = PAIRS.keys
|
22
21
|
|
23
22
|
def on_def(node)
|
@@ -49,11 +48,13 @@ and make scope more clear. \
|
|
49
48
|
|
50
49
|
def return_var_call?(body)
|
51
50
|
return unless body.children.last.respond_to?(:children)
|
52
|
-
subnodes(body.children.last).find
|
51
|
+
subnodes(body.children.last.to_a.first).find do |node|
|
52
|
+
ASSIGNS.include?(node.type)
|
53
|
+
end
|
53
54
|
end
|
54
55
|
|
55
56
|
def subnodes(node)
|
56
|
-
node.children.select { |child| child.respond_to?(:type) }
|
57
|
+
([node] + node.children).select { |child| child.respond_to?(:type) }
|
57
58
|
end
|
58
59
|
end
|
59
60
|
end
|
@@ -3,21 +3,27 @@ require 'rubocop'
|
|
3
3
|
|
4
4
|
module Ducalis
|
5
5
|
class PreferableMethods < RuboCop::Cop::Cop
|
6
|
-
OFFENSE =
|
7
|
-
Prefer to use %<alternative>s method instead of %<original>s because of
|
8
|
-
%<reason>s.
|
9
|
-
|
6
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
7
|
+
| Prefer to use %<alternative>s method instead of %<original>s because of
|
8
|
+
| %<reason>s.
|
9
|
+
MESSAGE
|
10
|
+
|
10
11
|
ALWAYS_TRUE = ->(_who, _what, _args) { true }
|
12
|
+
|
11
13
|
DELETE_CHECK = lambda do |who, _what, args|
|
12
|
-
!%i(sym str).include?(args.first
|
14
|
+
!%i(sym str).include?(args.first && args.first.type) &&
|
13
15
|
args.count <= 1 && who.to_s !~ /file/
|
14
16
|
end
|
17
|
+
|
15
18
|
DESCRIPTION = {
|
16
19
|
# Method => [Alternative, Reason, Callable condition]
|
17
20
|
delete_all: [:destroy_all, 'it is not invoking callbacks', ALWAYS_TRUE],
|
18
21
|
delete: [:destroy, 'it is not invoking callbacks', DELETE_CHECK]
|
19
22
|
}.freeze
|
20
23
|
|
24
|
+
DETAILS = "Dangerous methods are:
|
25
|
+
#{DESCRIPTION.keys.map { |name| "`#{name}`" }.join(', ')}."
|
26
|
+
|
21
27
|
def on_send(node)
|
22
28
|
who, what, *args = *node
|
23
29
|
return unless DESCRIPTION.keys.include?(what)
|
@@ -5,15 +5,22 @@ require 'rubocop'
|
|
5
5
|
module Ducalis
|
6
6
|
class PrivateInstanceAssign < RuboCop::Cop::Cop
|
7
7
|
include RuboCop::Cop::DefNode
|
8
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
9
|
+
| Don't use filters for setting instance variables, use them only for
|
10
|
+
| changing application flow, such as redirecting if a user is not
|
11
|
+
| authenticated. Controller instance variables are forming contract
|
12
|
+
| between controller and view. Keeping instance variables defined in one
|
13
|
+
| place makes it easier to: reason, refactor and remove old views, test
|
14
|
+
| controllers and views, extract actions to new controllers, etc.
|
15
|
+
MESSAGE
|
8
16
|
|
9
|
-
OFFENSE = %(
|
10
|
-
Please, don't assign instance variables in controller's private methods. It's \
|
11
|
-
make hard to understand what variables are available in views.
|
12
|
-
).strip
|
13
17
|
ADD_OFFENSE = %(
|
14
18
|
If you want to memoize variable, please, add underscore to the variable name \
|
15
19
|
start: `@_name`.
|
16
20
|
).strip
|
21
|
+
|
22
|
+
DETAILS = ADD_OFFENSE
|
23
|
+
|
17
24
|
def on_class(node)
|
18
25
|
_classdef_node, superclass, _body = *node
|
19
26
|
return if superclass.nil?
|
@@ -4,17 +4,17 @@ require 'rubocop'
|
|
4
4
|
|
5
5
|
module Ducalis
|
6
6
|
class ProtectedScopeCop < RuboCop::Cop::Cop
|
7
|
-
OFFENSE =
|
8
|
-
Seems like you are using `find` on non-protected scope. Potentially it
|
9
|
-
lead to unauthorized access. It's better to call `find` on
|
10
|
-
scopes. Example:
|
11
|
-
|
12
|
-
```ruby
|
13
|
-
current_group.employees.find(params[:id])
|
14
|
-
# better then
|
15
|
-
Employee.find(params[:id])
|
16
|
-
```
|
17
|
-
|
7
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
8
|
+
| Seems like you are using `find` on non-protected scope. Potentially it
|
9
|
+
| could lead to unauthorized access. It's better to call `find` on
|
10
|
+
| authorized resources scopes. Example:
|
11
|
+
|
|
12
|
+
| ```ruby
|
13
|
+
| current_group.employees.find(params[:id])
|
14
|
+
| # better then
|
15
|
+
| Employee.find(params[:id])
|
16
|
+
| ```
|
17
|
+
MESSAGE
|
18
18
|
|
19
19
|
def on_send(node)
|
20
20
|
_, method_name, *args = *node
|
@@ -4,11 +4,10 @@ require 'rubocop'
|
|
4
4
|
|
5
5
|
module Ducalis
|
6
6
|
class RaiseWithourErrorClass < RuboCop::Cop::Cop
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
).strip
|
7
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
8
|
+
| It's better to add exception class as raise argument. It will make
|
9
|
+
| easier to catch and process it later.
|
10
|
+
MESSAGE
|
12
11
|
|
13
12
|
def on_send(node)
|
14
13
|
_who, what, *args = *node
|
@@ -5,15 +5,17 @@ require 'regexp-examples'
|
|
5
5
|
|
6
6
|
module Ducalis
|
7
7
|
class RegexCop < RuboCop::Cop::Cop
|
8
|
-
OFFENSE =
|
9
|
-
It's better to move regex to constants with example instead of direct
|
10
|
-
It will allow you to reuse this regex and provide instructions
|
8
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
9
|
+
| It's better to move regex to constants with example instead of direct
|
10
|
+
| using it. It will allow you to reuse this regex and provide instructions
|
11
|
+
| for others.
|
12
|
+
|
|
13
|
+
|```ruby
|
14
|
+
|CONST_NAME = %<constant>s # "%<example>s"
|
15
|
+
|%<fixed_string>s
|
16
|
+
|```
|
17
|
+
MESSAGE
|
11
18
|
|
12
|
-
```ruby
|
13
|
-
CONST_NAME = %<constant>s # "%<example>s"
|
14
|
-
%<fixed_string>s
|
15
|
-
```
|
16
|
-
).strip
|
17
19
|
SELF_DESCRIPTIVE = %w(
|
18
20
|
/[[:alnum:]]/
|
19
21
|
/[[:alpha:]]/
|
@@ -31,6 +33,9 @@ CONST_NAME = %<constant>s # "%<example>s"
|
|
31
33
|
/[[:ascii:]]/
|
32
34
|
).freeze
|
33
35
|
|
36
|
+
DETAILS = "Available regexes are:
|
37
|
+
#{SELF_DESCRIPTIVE.map { |name| "`#{name}`" }.join(', ')}"
|
38
|
+
|
34
39
|
def on_regexp(node)
|
35
40
|
return if node.parent.type == :casgn
|
36
41
|
return if SELF_DESCRIPTIVE.include?(node.source)
|
@@ -5,11 +5,12 @@ require 'rubocop'
|
|
5
5
|
module Ducalis
|
6
6
|
class RestOnlyCop < RuboCop::Cop::Cop
|
7
7
|
include RuboCop::Cop::DefNode
|
8
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
9
|
+
| It's better for controllers to stay adherent to REST:
|
10
|
+
| http://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/
|
11
|
+
MESSAGE
|
12
|
+
|
8
13
|
WHITELIST = %i(index show new edit create update destroy).freeze
|
9
|
-
OFFENSE = %(
|
10
|
-
It's better for controllers to stay adherent to REST:
|
11
|
-
http://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/
|
12
|
-
).strip
|
13
14
|
|
14
15
|
def on_class(node)
|
15
16
|
_classdef_node, superclass, _body = *node
|
@@ -4,10 +4,11 @@ require 'rubocop'
|
|
4
4
|
|
5
5
|
module Ducalis
|
6
6
|
class RubocopDisable < RuboCop::Cop::Cop
|
7
|
-
OFFENSE =
|
8
|
-
Please, do not suppress RuboCop metrics, may be you can introduce some
|
9
|
-
refactoring or another concept.
|
10
|
-
|
7
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
8
|
+
| Please, do not suppress RuboCop metrics, may be you can introduce some
|
9
|
+
| refactoring or another concept.
|
10
|
+
MESSAGE
|
11
|
+
|
11
12
|
def investigate(processed_source)
|
12
13
|
return unless processed_source.ast
|
13
14
|
processed_source.comments.each do |comment_node|
|
@@ -4,12 +4,13 @@ require 'rubocop'
|
|
4
4
|
require_relative './callbacks_activerecord'
|
5
5
|
|
6
6
|
module Ducalis
|
7
|
-
class StringsInActiverecords <
|
8
|
-
OFFENSE =
|
9
|
-
Please, do not use strings as arguments for %<method_name>s argument.
|
10
|
-
It's hard to test, grep sources, code highlighting and so on.
|
11
|
-
Consider using of symbols or lambdas for complex expressions.
|
12
|
-
|
7
|
+
class StringsInActiverecords < RuboCop::Cop::Cop
|
8
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
9
|
+
| Please, do not use strings as arguments for %<method_name>s argument.
|
10
|
+
| It's hard to test, grep sources, code highlighting and so on.
|
11
|
+
| Consider using of symbols or lambdas for complex expressions.
|
12
|
+
MESSAGE
|
13
|
+
|
13
14
|
VALIDATEBLE_METHODS =
|
14
15
|
::Ducalis::CallbacksActiverecord::METHODS_BLACK_LIST + %i(
|
15
16
|
validates
|
@@ -3,11 +3,12 @@
|
|
3
3
|
require 'rubocop'
|
4
4
|
|
5
5
|
module Ducalis
|
6
|
-
class UncommentedGem <
|
7
|
-
OFFENSE =
|
8
|
-
Please, add comment why are you including non-realized gem version for
|
9
|
-
It will increase
|
10
|
-
|
6
|
+
class UncommentedGem < RuboCop::Cop::Cop
|
7
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
|
8
|
+
| Please, add comment why are you including non-realized gem version for
|
9
|
+
| %<gem>s. It will increase
|
10
|
+
| [bus-factor](<https://en.wikipedia.org/wiki/Bus_factor>).
|
11
|
+
MESSAGE
|
11
12
|
|
12
13
|
def investigate(processed_source)
|
13
14
|
return unless processed_source.ast
|