ducalis 0.5.5 → 0.5.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/ducalis.svg)](https://badge.fury.io/rb/ducalis)
|
4
4
|
[![Build Status](https://travis-ci.org/ignat-z/ducalis.svg?branch=master)](https://travis-ci.org/ignat-z/ducalis)
|
5
|
-
[![
|
5
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/d03d4e567e8728d2c58b/maintainability)](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
|