ducalis 0.5.10 → 0.5.11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '04397f76396f26b3580241dec8c514e605122819'
4
- data.tar.gz: dca5ae9744fdc0d42f5e590d87898802668f1f25
3
+ metadata.gz: 0e9545be92ee4417db0ef82c0becd1ccb5c7a8cd
4
+ data.tar.gz: a11c6683e3364627615fcaec02444e21100aadb8
5
5
  SHA512:
6
- metadata.gz: '0982cd88ca55631580c818f5447ae185eb4b43977ae8b1ad395782c944e412548c814406176b6816b35f0260a214b3b50932c2c85b166e3ed4bad583aed30097'
7
- data.tar.gz: 9cea3e6216bc80ef22458c066548dcfb42531c73d95fadab116bcd8395c25be746c13e04565983451a08c491634b69533913b70c832e7154464ee1134a8dbe55
6
+ metadata.gz: 21529fb2e38466c9c011bf2310811db2bc35b197d178e8c87ab2067a67a35ef2987a1920dcc6f54bc41945c774c84d90fdbf4ca1561defc1d3f55dc1fe089356
7
+ data.tar.gz: 6d8d1be1aeda6b342d594f87bb9a68037712aea33b13802b7637d8c5055c608a9686e5d44647e41f3595ece8f9d6e2a2387307b21f9975c175657cd2bd7dbf84
data/DOCUMENTATION.md CHANGED
@@ -1,11 +1,9 @@
1
1
  ## Ducalis::BlackListSuffix
2
2
 
3
- Please, avoid using of class suffixes like `Meneger`, `Client`
4
- and so on. If it has no parts, change the name of the class to what
5
- each object is managing. It's ok to use Manager as subclass of Person,
6
- which is there to refine a type of personal that has management
7
- behavior to it.
8
- Related [article](<http://www.carlopescio.com/2011/04/your-coding-conventions-are-hurting-you.html>)
3
+ Please, avoid using of class suffixes like `Meneger`, `Client` and so on. If it has no parts, change the name of the class to what each object is managing.
4
+
5
+ It's ok to use Manager as subclass of Person, which is there to refine a type of personal that has management behavior to it.
6
+ Related [article](<http://www.carlopescio.com/2011/04/your-coding-conventions-are-hurting-you.html>)
9
7
 
10
8
  ![](https://placehold.it/10/f03c15/000000?text=+) raises on classes with suffixes from black list
11
9
  ```ruby
@@ -32,10 +30,8 @@ end
32
30
  ```
33
31
  ## Ducalis::CallbacksActiverecord
34
32
 
35
- Please, avoid using of callbacks for models. It's better to
36
- keep models small ("dumb") and instead use "builder" classes
37
- / services: to construct new objects. You can read more
38
- [here](https://medium.com/planet-arkency/a61fd75ab2d3).
33
+ Please, avoid using of callbacks for models. It's better to keep models small ("dumb") and instead use "builder" classes/services: to construct new objects.
34
+ You can read more [here](https://medium.com/planet-arkency/a61fd75ab2d3).
39
35
 
40
36
  ![](https://placehold.it/10/f03c15/000000?text=+) raises on ActiveRecord classes which contains callbacks
41
37
  ```ruby
@@ -56,20 +52,14 @@ end
56
52
  ```
57
53
  ## Ducalis::CaseMapping
58
54
 
59
- Try to avoid `case when` statements. You can replace it with a sequence
60
- of `if... elsif... elsif... else`. For cases where you need to choose
61
- from a large number of possibilities, you can create a dictionary
62
- mapping case values to functions to call by `call`. It's nice to have
63
- prefix for the method names, i.e.: `visit_`.
55
+ Try to avoid `case when` statements. You can replace it with a sequence of `if... elsif... elsif... else`.
56
+ For cases where you need to choose from a large number of possibilities, you can create a dictionary mapping case values to functions to call by `call`. It's nice to have prefix for the method names, i.e.: `visit_`.
64
57
  Usually `case when` statements are using for the next reasons:
65
58
 
66
59
  I. Mapping between different values.
67
60
  ("A" => 1, "B" => 2, ...)
68
61
 
69
- This case is all about data representing. If you do not need to execute any code
70
- it's better to use data structure which represents it. This way you are
71
- separating concepts: code returns corresponding value and you have config-like
72
- data structure which describes your data.
62
+ This case is all about data representing. If you do not need to execute any code it's better to use data structure which represents it. This way you are separating concepts: code returns corresponding value and you have config-like data structure which describes your data.
73
63
 
74
64
  ```ruby
75
65
  %w[A B ...].index("A") + 1
@@ -82,19 +72,16 @@ II. Code execution depending of parameter or type:
82
72
  - a. (:attack => attack, :defend => defend)
83
73
  - b. (Feet => value * 0.348, Meters => `value`)
84
74
 
85
- In this case code violates OOP and S[O]LID principle. Code shouldn't know about
86
- object type and classes should be open for extension, but closed for
87
- modification (but you can't do it with case-statements).
88
- This is a signal that you have some problems with architecture.
75
+ In this case code violates OOP and S[O]LID principle. Code shouldn't know about object type and classes should be open for extension, but closed for modification (but you can't do it with case-statements). This is a signal that you have some problems with architecture.
89
76
 
90
- a.
77
+ a.
91
78
  ```ruby
92
79
  attack: -> { execute_attack }, defend: -> { execute_defend }
93
80
  # or
94
81
  call(:"execute_#{action}")
95
82
  ```
96
83
 
97
- b.
84
+ b.
98
85
  ```ruby
99
86
  class Meters; def to_metters; value; end
100
87
  class Feet; def to_metters; value * 0.348; end
@@ -103,17 +90,16 @@ class Feet; def to_metters; value * 0.348; end
103
90
  III. Code execution depending on some statement.
104
91
  (`a > 0` => 1, `a == 0` => 0, `a < 0` => -1)
105
92
 
106
- This case is combination of I and II -- high code complexity and unit-tests
107
- complexity. There are variants how to solve it:
93
+ This case is combination of I and II -- high code complexity and unit-tests complexity. There are variants how to solve it:
108
94
 
109
- a. Rewrite to simple if statement
95
+ a. Rewrite to simple if statement
110
96
 
111
97
  ```ruby
112
98
  return 0 if a == 0
113
99
  a > 0 ? 1 : -1
114
100
  ```
115
101
 
116
- b. Move statements to lambdas:
102
+ b. Move statements to lambdas:
117
103
 
118
104
  ```ruby
119
105
  ->(a) { a > 0 } => 1,
@@ -121,17 +107,9 @@ a > 0 ? 1 : -1
121
107
  ->(a) { a < 0 } => -1
122
108
  ```
123
109
 
124
- This way decreases code complexity by delegating it to lambdas and makes it easy
125
- to unit-testing because it's easy to test pure lambdas.
110
+ This way decreases code complexity by delegating it to lambdas and makes it easy to unit-testing because it's easy to test pure lambdas.
126
111
 
127
- Such approach is named
128
- [table-driven design](<https://www.d.umn.edu/~gshute/softeng/table-driven.html>)
129
- . Table-driven methods are schemes that allow you to look up information in a
130
- table rather than using logic statements (i.e. case, if). In simple cases,
131
- it's quicker and easier to use logic statements, but as the logic chain becomes
132
- more complex, table-driven code is simpler than complicated logic, easier to
133
- modify and more efficient.
134
- </details>
112
+ Such approach is named [table-driven design](<https://www.d.umn.edu/~gshute/softeng/table-driven.html>). Table-driven methods are schemes that allow you to look up information in a table rather than using logic statements (i.e. case, if). In simple cases, it's quicker and easier to use logic statements, but as the logic chain becomes more complex, table-driven code is simpler than complicated logic, easier to modify and more efficient.
135
113
 
136
114
  ![](https://placehold.it/10/f03c15/000000?text=+) raises on case statements
137
115
  ```ruby
@@ -150,8 +128,7 @@ end
150
128
  ```
151
129
  ## Ducalis::ControllersExcept
152
130
 
153
- Prefer to use `:only` over `:except` in controllers because it's more
154
- explicit and will be easier to maintain for new developers.
131
+ Prefer to use `:only` over `:except` in controllers because it's more explicit and will be easier to maintain for new developers.
155
132
 
156
133
  ![](https://placehold.it/10/f03c15/000000?text=+) raises for `before_filters` with `except` method as array
157
134
  ```ruby
@@ -202,8 +179,7 @@ end
202
179
  ```
203
180
  ## Ducalis::KeywordDefaults
204
181
 
205
- Prefer to use keyword arguments for defaults. It increases readability
206
- and reduces ambiguities.
182
+ Prefer to use keyword arguments for defaults. It increases readability and reduces ambiguities.
207
183
 
208
184
  ![](https://placehold.it/10/f03c15/000000?text=+) raises if method definition contains default values
209
185
  ```ruby
@@ -231,8 +207,7 @@ def self.calculate_amount; end
231
207
  ```
232
208
  ## Ducalis::ModuleLikeClass
233
209
 
234
- Seems like it will be better to define initialize and pass %<args>s
235
- there instead of each method.
210
+ Seems like it will be better to define initialize and pass %<args>s there instead of each method.
236
211
 
237
212
  ![](https://placehold.it/10/f03c15/000000?text=+) raises if class doesn't contain constructor but accept the same args
238
213
  ```ruby
@@ -319,11 +294,62 @@ class TaskJournal
319
294
  end
320
295
  end
321
296
 
297
+ ```
298
+ ## Ducalis::OptionsArgument
299
+
300
+ Default options argument isn't good idea. It's better to explicitly pass which keys are you interested in as keyword arguments. You can use split operator to support hash arguments.
301
+
302
+ Compare:
303
+
304
+ ```ruby
305
+ def generate_1(document, options = {})
306
+ format = options.delete(:format)
307
+ limit = options.delete(:limit) || 20
308
+ # ...
309
+ [format, limit, options]
310
+ end
311
+ generate_1(1, format: 'csv', limit: 5, useless_arg: :value)
312
+
313
+ # vs
314
+
315
+ def generate_2(document, format:, limit: 20, **options)
316
+ # ...
317
+ [format, limit, options]
318
+ end
319
+ generate_2(1, format: 'csv', limit: 5, useless_arg: :value)
320
+ ```
321
+
322
+ ![](https://placehold.it/10/f03c15/000000?text=+) raises if method accepts default options argument
323
+ ```ruby
324
+
325
+ def generate(document, options = {})
326
+ format = options.delete(:format)
327
+ limit = options.delete(:limit) || 20
328
+ [format, limit, options]
329
+ end
330
+
331
+ ```
332
+
333
+ ![](https://placehold.it/10/f03c15/000000?text=+) raises if method accepts options argument
334
+ ```ruby
335
+
336
+ def log(record, options)
337
+ # ...
338
+ end
339
+
340
+ ```
341
+
342
+ ![](https://placehold.it/10/2cbe4e/000000?text=+) ignores passing options with split operator
343
+ ```ruby
344
+
345
+ def generate(document, format:, limit: 20, **options)
346
+ [format, limit, options]
347
+ end
348
+
322
349
  ```
323
350
  ## Ducalis::ParamsPassing
324
351
 
325
- It's better to pass already preprocessed params hash to services. Or
326
- you can use `arcane` gem.
352
+ It's better to pass already preprocessed params hash to services. Or you can use `arcane` gem.
327
353
 
328
354
  ![](https://placehold.it/10/f03c15/000000?text=+) raises if user pass `params` as argument from controller
329
355
  ```ruby
@@ -392,12 +418,9 @@ end
392
418
  ```
393
419
  ## Ducalis::PossibleTap
394
420
 
395
- Consider of using `.tap`, default ruby
396
- [method](<https://apidock.com/ruby/Object/tap>)
397
- which allows to replace intermediate variables with block, by this you
398
- are limiting scope pollution and make method scope more clear. If it isn't
399
- possible, consider of moving it to method or even inline it.
400
- [Related article](<http://seejohncode.com/2012/01/02/ruby-tap-that/>).
421
+ Consider of using `.tap`, default ruby [method](<https://apidock.com/ruby/Object/tap>) which allows to replace intermediate variables with block, by this you are limiting scope pollution and make method scope more clear.
422
+ If it isn't possible, consider of moving it to method or even inline it.
423
+ [Related article](<http://seejohncode.com/2012/01/02/ruby-tap-that/>).
401
424
 
402
425
  ![](https://placehold.it/10/f03c15/000000?text=+) raises for methods with scope variable return
403
426
  ```ruby
@@ -493,16 +516,35 @@ end
493
516
  ```
494
517
  ## Ducalis::PreferableMethods
495
518
 
496
- Prefer to use %<alternative>s method instead of %<original>s because of
497
- %<reason>s.
519
+ Prefer to use %<alternative>s method instead of %<original>s because of %<reason>s.
498
520
  Dangerous methods are:
499
- `delete_all`, `delete`.
521
+ `toggle!`, `save`, `delete`, `delete_all`, `update_attribute`, `update_column`, `update_columns`.
500
522
 
501
523
  ![](https://placehold.it/10/f03c15/000000?text=+) raises for `delete` method calling
502
524
  ```ruby
503
525
  User.where(id: 7).delete
504
526
  ```
505
527
 
528
+ ![](https://placehold.it/10/f03c15/000000?text=+) raises `save` method calling with validate: false
529
+ ```ruby
530
+ User.where(id: 7).save(validate: false)
531
+ ```
532
+
533
+ ![](https://placehold.it/10/f03c15/000000?text=+) raises `toggle!` method calling
534
+ ```ruby
535
+ User.where(id: 7).toggle!
536
+ ```
537
+
538
+ ![](https://placehold.it/10/2cbe4e/000000?text=+) ignores `save` method calling without validate: false
539
+ ```ruby
540
+ User.where(id: 7).save
541
+ ```
542
+
543
+ ![](https://placehold.it/10/2cbe4e/000000?text=+) ignores `save` method calling without validate: false
544
+ ```ruby
545
+ User.where(id: 7).save(some_arg: true)
546
+ ```
547
+
506
548
  ![](https://placehold.it/10/2cbe4e/000000?text=+) ignores calling `delete` with symbol
507
549
  ```ruby
508
550
  params.delete(:code)
@@ -524,12 +566,7 @@ tempfile.delete
524
566
  ```
525
567
  ## Ducalis::PrivateInstanceAssign
526
568
 
527
- Don't use controller's filter methods for setting instance variables, use
528
- them only for changing application flow, such as redirecting if a user
529
- is not authenticated. Controller instance variables are forming contract
530
- between controller and view. Keeping instance variables defined in one
531
- place makes it easier to: reason, refactor and remove old views, test
532
- controllers and views, extract actions to new controllers, etc.
569
+ Don't use controller's filter methods for setting instance variables, use them only for changing application flow, such as redirecting if a user is not authenticated. Controller instance variables are forming contract between controller and view. Keeping instance variables defined in one place makes it easier to: reason, refactor and remove old views, test controllers and views, extract actions to new controllers, etc.
533
570
  If you want to memoize variable, please, add underscore to the variable name start: `@_name`.
534
571
 
535
572
  ![](https://placehold.it/10/f03c15/000000?text=+) raises for assigning instance variables in controllers private methods
@@ -589,15 +626,14 @@ end
589
626
  ```
590
627
  ## Ducalis::ProtectedScopeCop
591
628
 
592
- Seems like you are using `find` on non-protected scope. Potentially it
593
- could lead to unauthorized access. It's better to call `find` on
594
- authorized resources scopes. Example:
629
+ Seems like you are using `find` on non-protected scope. Potentially it could lead to unauthorized access. It's better to call `find` on authorized resources scopes.
630
+ Example:
595
631
 
596
- ```ruby
597
- current_group.employees.find(params[:id])
598
- # better then
599
- Employee.find(params[:id])
600
- ```
632
+ ```ruby
633
+ current_group.employees.find(params[:id])
634
+ # better then
635
+ Employee.find(params[:id])
636
+ ```
601
637
 
602
638
  ![](https://placehold.it/10/f03c15/000000?text=+) raises if somewhere AR search was called on not protected scope
603
639
  ```ruby
@@ -629,8 +665,7 @@ end
629
665
  ```
630
666
  ## Ducalis::RaiseWithoutErrorClass
631
667
 
632
- It's better to add exception class as raise argument. It will make
633
- easier to catch and process it later.
668
+ It's better to add exception class as raise argument. It will make easier to catch and process it later.
634
669
 
635
670
  ![](https://placehold.it/10/f03c15/000000?text=+) raises when `raise` called without exception class
636
671
  ```ruby
@@ -653,9 +688,7 @@ raise StandardError.new("Something went wrong")
653
688
  ```
654
689
  ## Ducalis::RegexCop
655
690
 
656
- It's better to move regex to constants with example instead of direct
657
- using it. It will allow you to reuse this regex and provide instructions
658
- for others.
691
+ It's better to move regex to constants with example instead of direct using it. It will allow you to reuse this regex and provide instructions for others.
659
692
 
660
693
  ```ruby
661
694
  CONST_NAME = %<constant>s # "%<example>s"
@@ -699,7 +732,7 @@ puts "hi" if name =~ /.{#{name.length}}/
699
732
  ## Ducalis::RestOnlyCop
700
733
 
701
734
  It's better for controllers to stay adherent to REST:
702
- http://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/.
735
+ http://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/.
703
736
  [About RESTful architecture](<https://confreaks.tv/videos/railsconf2017-in-relentless-pursuit-of-rest>)
704
737
 
705
738
  ![](https://placehold.it/10/f03c15/000000?text=+) raises for controllers with non-REST methods
@@ -751,8 +784,7 @@ end
751
784
  ```
752
785
  ## Ducalis::RubocopDisable
753
786
 
754
- Please, do not suppress RuboCop metrics, may be you can introduce some
755
- refactoring or another concept.
787
+ Please, do not suppress RuboCop metrics, may be you can introduce some refactoring or another concept.
756
788
 
757
789
  ![](https://placehold.it/10/f03c15/000000?text=+) raises on RuboCop disable comments
758
790
  ```ruby
@@ -768,12 +800,32 @@ def calculate(five, args, at, one, list); end
768
800
  # some meaningful comment
769
801
  def calculate(five, args, at, one, list); end
770
802
 
803
+ ```
804
+ ## Ducalis::StandardMethods
805
+
806
+ Please, be sure that you really want to redefine standard ruby methods.
807
+ You should know what are you doing and all consequences.
808
+
809
+ ![](https://placehold.it/10/f03c15/000000?text=+) raises if use redefines default ruby methods
810
+ ```ruby
811
+
812
+ def to_s
813
+ "my version"
814
+ end
815
+
816
+ ```
817
+
818
+ ![](https://placehold.it/10/2cbe4e/000000?text=+) ignores if use defines simple ruby methods
819
+ ```ruby
820
+
821
+ def present
822
+ "my version"
823
+ end
824
+
771
825
  ```
772
826
  ## Ducalis::StringsInActiverecords
773
827
 
774
- Please, do not use strings as arguments for %<method_name>s argument.
775
- It's hard to test, grep sources, code highlighting and so on.
776
- Consider using of symbols or lambdas for complex expressions.
828
+ Please, do not use strings as arguments for %<method_name>s argument. It's hard to test, grep sources, code highlighting and so on. Consider using of symbols or lambdas for complex expressions.
777
829
 
778
830
  ![](https://placehold.it/10/f03c15/000000?text=+) raises for string if argument
779
831
  ```ruby
@@ -789,11 +841,9 @@ validates :file, if: -> { remote_url.blank? }
789
841
  ```
790
842
  ## Ducalis::TooLongWorkers
791
843
 
792
- Seems like your worker is doing too much work, consider of moving business
793
- logic to service object. As rule, workers should have only two responsibilities:
794
- - __Model materialization__: As async jobs working with serialized attributes
795
- it's nescessary to cast them into actual objects.
796
- - __Errors handling__: Rescue errors and figure out what to do with them.
844
+ Seems like your worker is doing too much work, consider of moving business logic to service object. As rule, workers should have only two responsibilities:
845
+ - __Model materialization__: As async jobs working with serialized attributes it's nescessary to cast them into actual objects.
846
+ - __Errors handling__: Rescue errors and figure out what to do with them.
797
847
 
798
848
  ![](https://placehold.it/10/f03c15/000000?text=+) raises for a class with more than 5 lines
799
849
  ```ruby
@@ -859,9 +909,8 @@ end
859
909
  ```
860
910
  ## Ducalis::UncommentedGem
861
911
 
862
- Please, add comment why are you including non-realized gem version for
863
- %<gem>s. It will increase
864
- [bus-factor](<https://en.wikipedia.org/wiki/Bus_factor>).
912
+ Please, add comment why are you including non-realized gem version for %<gem>s.
913
+ It will increase [bus-factor](<https://en.wikipedia.org/wiki/Bus_factor>).
865
914
 
866
915
  ![](https://placehold.it/10/f03c15/000000?text=+) raises for gem from github without comment
867
916
  ```ruby
@@ -879,22 +928,39 @@ gem 'pry', '~> 0.10', '>= 0.10.0'
879
928
  gem 'rake', '~> 12.1'
880
929
  gem 'rspec', github: 'rspec/rspec' # new non released API
881
930
 
931
+ ```
932
+ ## Ducalis::UnlockedGem
933
+
934
+ It's better to lock gem versions explicitly with pessimistic operator (~>).
935
+
936
+ ![](https://placehold.it/10/f03c15/000000?text=+) raises for gem without version
937
+ ```ruby
938
+ gem 'pry'
939
+ ```
940
+
941
+ ![](https://placehold.it/10/2cbe4e/000000?text=+) ignores gems with locked versions
942
+ ```ruby
943
+
944
+ gem 'pry', '~> 0.10', '>= 0.10.0'
945
+ gem 'rake', '~> 12.1'
946
+ gem 'thor', '= 0.20.0'
947
+ gem 'rspec', github: 'rspec/rspec'
948
+
882
949
  ```
883
950
  ## Ducalis::UselessOnly
884
951
 
885
- Seems like there is no any reason to keep before filter only for one
886
- action. Maybe it will be better to inline it?
952
+ Seems like there is no any reason to keep before filter only for one action. Maybe it will be better to inline it?
887
953
 
888
- ```ruby
889
- before_filter :do_something, only: %i[index]
890
- def index; end
954
+ ```ruby
955
+ before_filter :do_something, only: %i[index]
956
+ def index; end
891
957
 
892
- # to
958
+ # to
893
959
 
894
- def index
895
- do_something
896
- end
897
- ```
960
+ def index
961
+ do_something
962
+ end
963
+ ```
898
964
 
899
965
  ![](https://placehold.it/10/f03c15/000000?text=+) raises for `before_filters` with only one method as array
900
966
  ```ruby
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ducalis (0.5.10)
4
+ ducalis (0.5.11)
5
5
  git (~> 1.3, >= 1.3.0)
6
6
  policial (= 0.0.4)
7
7
  regexp-examples (~> 1.3, >= 1.3.2)
data/README.md CHANGED
@@ -59,6 +59,17 @@ 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
61
 
62
+ ## Configuration
63
+
64
+ One or more individual cops can be disabled locally in a section of a file by adding a comment such as
65
+
66
+ ```ruby
67
+ # ducalis:disable Ducalis/PreferableMethods Use `delete_all` because of performance reasons
68
+ def remove_audits
69
+ AuditLog.where(user_id: user_id).delete_all
70
+ end
71
+ ```
72
+
62
73
  ## Contribution
63
74
 
64
75
  To pass your code through the all checks you simply need to run:
data/config/.ducalis.yml CHANGED
@@ -30,7 +30,7 @@ Ducalis/CallbacksActiverecord:
30
30
  Enabled: true
31
31
 
32
32
  Ducalis/PossibleTap:
33
- Enabled: true
33
+ Enabled: false
34
34
 
35
35
  Ducalis/ProtectedScopeCop:
36
36
  Enabled: true
@@ -60,6 +60,9 @@ Ducalis/RestOnlyCop:
60
60
  Ducalis/KeywordDefaults:
61
61
  Enabled: true
62
62
 
63
+ Ducalis/OptionsArgument:
64
+ Enabled: true
65
+
63
66
  Ducalis/RubocopDisable:
64
67
  Enabled: true
65
68
 
@@ -80,9 +83,15 @@ Ducalis/ControllersExcept:
80
83
  Ducalis/PrivateInstanceAssign:
81
84
  Enabled: true
82
85
 
86
+ Ducalis/StandardMethods:
87
+ Enabled: true
88
+
83
89
  Ducalis/RaiseWithoutErrorClass:
84
90
  Enabled: true
85
91
 
92
+ Ducalis/UnlockedGem:
93
+ Enabled: true
94
+
86
95
  Ducalis/ModuleLikeClass:
87
96
  Enabled: true
88
97
  AllowedIncludes:
@@ -4,12 +4,10 @@ require 'rubocop'
4
4
 
5
5
  module Ducalis
6
6
  class BlackListSuffix < RuboCop::Cop::Cop
7
- OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
8
- | Please, avoid using of class suffixes like `Meneger`, `Client`
9
- | and so on. If it has no parts, change the name of the class to what
10
- | each object is managing. It's ok to use Manager as subclass of Person,
11
- | which is there to refine a type of personal that has management
12
- | behavior to it.
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | Please, avoid using of class suffixes like `Meneger`, `Client` and so on. If it has no parts, change the name of the class to what each object is managing.
9
+
10
+ | It's ok to use Manager as subclass of Person, which is there to refine a type of personal that has management behavior to it.
13
11
  | Related [article](<http://www.carlopescio.com/2011/04/your-coding-conventions-are-hurting-you.html>)
14
12
  MESSAGE
15
13
 
@@ -4,11 +4,9 @@ require 'rubocop'
4
4
 
5
5
  module Ducalis
6
6
  class CallbacksActiverecord < RuboCop::Cop::Cop
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).
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | Please, avoid using of callbacks for models. It's better to keep models small ("dumb") and instead use "builder" classes/services: to construct new objects.
9
+ | You can read more [here](https://medium.com/planet-arkency/a61fd75ab2d3).
12
10
  MESSAGE
13
11
 
14
12
  MODELS_CLASS_NAMES = [
@@ -4,87 +4,69 @@ require 'rubocop'
4
4
 
5
5
  module Ducalis
6
6
  class CaseMapping < RuboCop::Cop::Cop
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_`.
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | Try to avoid `case when` statements. You can replace it with a sequence of `if... elsif... elsif... else`.
9
+ | For cases where you need to choose from a large number of possibilities, you can create a dictionary mapping case values to functions to call by `call`. It's nice to have prefix for the method names, i.e.: `visit_`.
13
10
  MESSAGE
14
11
 
15
- DETAILS = %(
16
- Usually `case when` statements are using for the next reasons:
17
-
18
- I. Mapping between different values.
19
- ("A" => 1, "B" => 2, ...)
20
-
21
- This case is all about data representing. If you do not need to execute any code
22
- it's better to use data structure which represents it. This way you are
23
- separating concepts: code returns corresponding value and you have config-like
24
- data structure which describes your data.
25
-
26
- ```ruby
27
- %w[A B ...].index("A") + 1
28
- # or
29
- { "A" => 1, "B" => 2 }.fetch("A")
30
- ```
31
-
32
- II. Code execution depending of parameter or type:
33
-
34
- - a. (:attack => attack, :defend => defend)
35
- - b. (Feet => value * 0.348, Meters => `value`)
36
-
37
- In this case code violates OOP and S[O]LID principle. Code shouldn't know about
38
- object type and classes should be open for extension, but closed for
39
- modification (but you can't do it with case-statements).
40
- This is a signal that you have some problems with architecture.
41
-
42
- a.
43
- ```ruby
44
- attack: -> { execute_attack }, defend: -> { execute_defend }
45
- #{(action = '#{' + 'action' + '}') && '# or'}
46
- call(:"execute_#{action}")
47
- ```
48
-
49
- b.
50
- ```ruby
51
- class Meters; def to_metters; value; end
52
- class Feet; def to_metters; value * 0.348; end
53
- ```
54
-
55
- III. Code execution depending on some statement.
56
- (`a > 0` => 1, `a == 0` => 0, `a < 0` => -1)
57
-
58
- This case is combination of I and II -- high code complexity and unit-tests
59
- complexity. There are variants how to solve it:
60
-
61
- a. Rewrite to simple if statement
62
-
63
- ```ruby
64
- return 0 if a == 0
65
- a > 0 ? 1 : -1
66
- ```
67
-
68
- b. Move statements to lambdas:
69
-
70
- ```ruby
71
- ->(a) { a > 0 } => 1,
72
- ->(a) { a == 0 } => 0,
73
- ->(a) { a < 0 } => -1
74
- ```
75
-
76
- This way decreases code complexity by delegating it to lambdas and makes it easy
77
- to unit-testing because it's easy to test pure lambdas.
78
-
79
- Such approach is named
80
- [table-driven design](<https://www.d.umn.edu/~gshute/softeng/table-driven.html>)
81
- . Table-driven methods are schemes that allow you to look up information in a
82
- table rather than using logic statements (i.e. case, if). In simple cases,
83
- it's quicker and easier to use logic statements, but as the logic chain becomes
84
- more complex, table-driven code is simpler than complicated logic, easier to
85
- modify and more efficient.
86
- </details>
87
- ).strip
12
+ DETAILS = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
13
+ | Usually `case when` statements are using for the next reasons:
14
+
15
+ | I. Mapping between different values.
16
+ | ("A" => 1, "B" => 2, ...)
17
+
18
+ | This case is all about data representing. If you do not need to execute any code it's better to use data structure which represents it. This way you are separating concepts: code returns corresponding value and you have config-like data structure which describes your data.
19
+
20
+ | ```ruby
21
+ | %w[A B ...].index("A") + 1
22
+ | # or
23
+ | { "A" => 1, "B" => 2 }.fetch("A")
24
+ | ```
25
+
26
+ | II. Code execution depending of parameter or type:
27
+
28
+ | - a. (:attack => attack, :defend => defend)
29
+ | - b. (Feet => value * 0.348, Meters => `value`)
30
+
31
+ | In this case code violates OOP and S[O]LID principle. Code shouldn't know about object type and classes should be open for extension, but closed for modification (but you can't do it with case-statements). This is a signal that you have some problems with architecture.
32
+
33
+ | a.
34
+ | ```ruby
35
+ | attack: -> { execute_attack }, defend: -> { execute_defend }
36
+ | #{(action = '#{' + 'action' + '}') && '# or'}
37
+ | call(:"execute_#{action}")
38
+ | ```
39
+
40
+ | b.
41
+ | ```ruby
42
+ | class Meters; def to_metters; value; end
43
+ | class Feet; def to_metters; value * 0.348; end
44
+ | ```
45
+
46
+ | III. Code execution depending on some statement.
47
+ | (`a > 0` => 1, `a == 0` => 0, `a < 0` => -1)
48
+
49
+ | This case is combination of I and II -- high code complexity and unit-tests complexity. There are variants how to solve it:
50
+
51
+ | a. Rewrite to simple if statement
52
+
53
+ | ```ruby
54
+ | return 0 if a == 0
55
+ | a > 0 ? 1 : -1
56
+ | ```
57
+
58
+ | b. Move statements to lambdas:
59
+
60
+ | ```ruby
61
+ | ->(a) { a > 0 } => 1,
62
+ | ->(a) { a == 0 } => 0,
63
+ | ->(a) { a < 0 } => -1
64
+ | ```
65
+
66
+ | This way decreases code complexity by delegating it to lambdas and makes it easy to unit-testing because it's easy to test pure lambdas.
67
+
68
+ | Such approach is named [table-driven design](<https://www.d.umn.edu/~gshute/softeng/table-driven.html>). Table-driven methods are schemes that allow you to look up information in a table rather than using logic statements (i.e. case, if). In simple cases, it's quicker and easier to use logic statements, but as the logic chain becomes more complex, table-driven code is simpler than complicated logic, easier to modify and more efficient.
69
+ MESSAGE
88
70
 
89
71
  def on_case(node)
90
72
  add_offense(node, :expression, OFFENSE)
@@ -4,9 +4,8 @@ require 'rubocop'
4
4
 
5
5
  module Ducalis
6
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.
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | Prefer to use `:only` over `:except` in controllers because it's more explicit and will be easier to maintain for new developers.
10
9
  MESSAGE
11
10
 
12
11
  FILTERS = %i(before_filter after_filter around_filter
@@ -4,9 +4,8 @@ require 'rubocop'
4
4
 
5
5
  module Ducalis
6
6
  class KeywordDefaults < RuboCop::Cop::Cop
7
- OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
8
- | Prefer to use keyword arguments for defaults. It increases readability
9
- | and reduces ambiguities.
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | Prefer to use keyword arguments for defaults. It increases readability and reduces ambiguities.
10
9
  MESSAGE
11
10
 
12
11
  def on_def(node)
@@ -5,9 +5,8 @@ require 'rubocop'
5
5
  module Ducalis
6
6
  class ModuleLikeClass < RuboCop::Cop::Cop
7
7
  include RuboCop::Cop::DefNode
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.
8
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
9
+ | Seems like it will be better to define initialize and pass %<args>s there instead of each method.
11
10
  MESSAGE
12
11
 
13
12
  def on_class(node)
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+
5
+ module Ducalis
6
+ class OptionsArgument < RuboCop::Cop::Cop
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | Default options argument isn't good idea. It's better to explicitly pass which keys are you interested in as keyword arguments. You can use split operator to support hash arguments.
9
+
10
+ | Compare:
11
+
12
+ | ```ruby
13
+ | def generate_1(document, options = {})
14
+ | format = options.delete(:format)
15
+ | limit = options.delete(:limit) || 20
16
+ | # ...
17
+ | [format, limit, options]
18
+ | end
19
+ | generate_1(1, format: 'csv', limit: 5, useless_arg: :value)
20
+
21
+ | # vs
22
+
23
+ | def generate_2(document, format:, limit: 20, **options)
24
+ | # ...
25
+ | [format, limit, options]
26
+ | end
27
+ | generate_2(1, format: 'csv', limit: 5, useless_arg: :value)
28
+ | ```
29
+ MESSAGE
30
+
31
+ def on_def(node)
32
+ _name, args, _body = *node
33
+ return unless default_options?(args)
34
+ add_offense(node, :expression, OFFENSE)
35
+ end
36
+
37
+ private
38
+
39
+ def_node_search :options_arg?, '(arg :options)'
40
+ def_node_search :options_arg_with_default?, '(optarg :options ...)'
41
+
42
+ def default_options?(args)
43
+ args.children.any? do |node|
44
+ options_arg?(node) || options_arg_with_default?(node)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -4,9 +4,8 @@ require 'rubocop'
4
4
 
5
5
  module Ducalis
6
6
  class ParamsPassing < RuboCop::Cop::Cop
7
- OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
8
- | It's better to pass already preprocessed params hash to services. Or
9
- | you can use `arcane` gem.
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | It's better to pass already preprocessed params hash to services. Or you can use `arcane` gem.
10
9
  MESSAGE
11
10
 
12
11
  PARAMS_CALL = s(:send, nil, :params)
@@ -4,12 +4,9 @@ require 'rubocop'
4
4
 
5
5
  module Ducalis
6
6
  class PossibleTap < RuboCop::Cop::Cop
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 method scope more clear. If it isn't
12
- | possible, consider of moving it to method or even inline it.
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | Consider of using `.tap`, default ruby [method](<https://apidock.com/ruby/Object/tap>) which allows to replace intermediate variables with block, by this you are limiting scope pollution and make method scope more clear.
9
+ | If it isn't possible, consider of moving it to method or even inline it.
13
10
  | [Related article](<http://seejohncode.com/2012/01/02/ruby-tap-that/>).
14
11
  MESSAGE
15
12
 
@@ -3,9 +3,8 @@ require 'rubocop'
3
3
 
4
4
  module Ducalis
5
5
  class PreferableMethods < RuboCop::Cop::Cop
6
- OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
7
- | Prefer to use %<alternative>s method instead of %<original>s because of
8
- | %<reason>s.
6
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
7
+ | Prefer to use %<alternative>s method instead of %<original>s because of %<reason>s.
9
8
  MESSAGE
10
9
 
11
10
  ALWAYS_TRUE = ->(_who, _what, _args) { true }
@@ -15,10 +14,22 @@ module Ducalis
15
14
  args.count <= 1 && who.to_s !~ /file/
16
15
  end
17
16
 
17
+ VALIDATE_CHECK = lambda do |_who, _what, args|
18
+ (args.first && args.first.source) =~ /validate/
19
+ end
20
+
18
21
  DESCRIPTION = {
19
22
  # Method => [Alternative, Reason, Callable condition]
23
+ toggle!: ['toggle.save', 'it is not invoking validations', ALWAYS_TRUE],
24
+ save: [:save, 'it is not invoking validations', VALIDATE_CHECK],
25
+ delete: [:destroy, 'it is not invoking callbacks', DELETE_CHECK],
20
26
  delete_all: [:destroy_all, 'it is not invoking callbacks', ALWAYS_TRUE],
21
- delete: [:destroy, 'it is not invoking callbacks', DELETE_CHECK]
27
+ update_attribute: [:update, 'it is not invoking validation', ALWAYS_TRUE],
28
+ update_column: [:update, 'it is not invoking callbacks', ALWAYS_TRUE],
29
+ update_columns: [
30
+ :update, 'it is not invoking validations, callbacks and updated_at',
31
+ ALWAYS_TRUE
32
+ ]
22
33
  }.freeze
23
34
 
24
35
  DETAILS = "Dangerous methods are:
@@ -5,19 +5,13 @@ 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 controller's filter methods for setting instance variables, use
10
- | them only for changing application flow, such as redirecting if a user
11
- | is not 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.
8
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
9
+ | Don't use controller's filter methods for setting instance variables, use them only for changing application flow, such as redirecting if a user is not authenticated. Controller instance variables are forming contract between controller and view. Keeping instance variables defined in one place makes it easier to: reason, refactor and remove old views, test controllers and views, extract actions to new controllers, etc.
15
10
  MESSAGE
16
11
 
17
- ADD_OFFENSE = %(
18
- If you want to memoize variable, please, add underscore to the variable name \
19
- start: `@_name`.
20
- ).strip
12
+ ADD_OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
13
+ If you want to memoize variable, please, add underscore to the variable name start: `@_name`.
14
+ MESSAGE
21
15
 
22
16
  DETAILS = ADD_OFFENSE
23
17
 
@@ -4,11 +4,10 @@ require 'rubocop'
4
4
 
5
5
  module Ducalis
6
6
  class ProtectedScopeCop < RuboCop::Cop::Cop
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
- |
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | Seems like you are using `find` on non-protected scope. Potentially it could lead to unauthorized access. It's better to call `find` on authorized resources scopes.
9
+ | Example:
10
+
12
11
  | ```ruby
13
12
  | current_group.employees.find(params[:id])
14
13
  | # better then
@@ -4,9 +4,8 @@ require 'rubocop'
4
4
 
5
5
  module Ducalis
6
6
  class RaiseWithoutErrorClass < RuboCop::Cop::Cop
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.
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | It's better to add exception class as raise argument. It will make easier to catch and process it later.
10
9
  MESSAGE
11
10
 
12
11
  def on_send(node)
@@ -5,15 +5,13 @@ require 'regexp-examples'
5
5
 
6
6
  module Ducalis
7
7
  class RegexCop < RuboCop::Cop::Cop
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
- |```
8
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
9
+ | It's better to move regex to constants with example instead of direct using it. It will allow you to reuse this regex and provide instructions for others.
10
+
11
+ | ```ruby
12
+ | CONST_NAME = %<constant>s # "%<example>s"
13
+ | %<fixed_string>s
14
+ | ```
17
15
  MESSAGE
18
16
 
19
17
  SELF_DESCRIPTIVE = %w(
@@ -5,12 +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
8
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
9
9
  | It's better for controllers to stay adherent to REST:
10
10
  | http://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/.
11
11
  MESSAGE
12
12
 
13
- DETAILS = <<-MESSAGE.gsub(/^ +\|/, '').strip
13
+ DETAILS = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
14
14
  | [About RESTful architecture](<https://confreaks.tv/videos/railsconf2017-in-relentless-pursuit-of-rest>)
15
15
  MESSAGE
16
16
 
@@ -4,9 +4,8 @@ require 'rubocop'
4
4
 
5
5
  module Ducalis
6
6
  class RubocopDisable < RuboCop::Cop::Cop
7
- OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
8
- | Please, do not suppress RuboCop metrics, may be you can introduce some
9
- | refactoring or another concept.
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | Please, do not suppress RuboCop metrics, may be you can introduce some refactoring or another concept.
10
9
  MESSAGE
11
10
 
12
11
  def investigate(processed_source)
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+
5
+ module Ducalis
6
+ class StandardMethods < RuboCop::Cop::Cop
7
+ BLACK_LIST = [Object].flat_map { |klass| klass.new.methods }
8
+
9
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
10
+ | Please, be sure that you really want to redefine standard ruby methods.
11
+ | You should know what are you doing and all consequences.
12
+ MESSAGE
13
+
14
+ def on_def(node)
15
+ name, _args, _body = *node
16
+ return unless BLACK_LIST.include?(name)
17
+ add_offense(node, :expression, OFFENSE)
18
+ end
19
+ end
20
+ end
@@ -5,10 +5,8 @@ require_relative './callbacks_activerecord'
5
5
 
6
6
  module Ducalis
7
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.
8
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
9
+ | Please, do not use strings as arguments for %<method_name>s argument. It's hard to test, grep sources, code highlighting and so on. Consider using of symbols or lambdas for complex expressions.
12
10
  MESSAGE
13
11
 
14
12
  VALIDATEBLE_METHODS =
@@ -6,11 +6,9 @@ module Ducalis
6
6
  class TooLongWorkers < RuboCop::Cop::Cop
7
7
  include RuboCop::Cop::ClassishLength
8
8
 
9
- OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
10
- | Seems like your worker is doing too much work, consider of moving business
11
- | logic to service object. As rule, workers should have only two responsibilities:
12
- | - __Model materialization__: As async jobs working with serialized attributes
13
- | it's nescessary to cast them into actual objects.
9
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
10
+ | Seems like your worker is doing too much work, consider of moving business logic to service object. As rule, workers should have only two responsibilities:
11
+ | - __Model materialization__: As async jobs working with serialized attributes it's nescessary to cast them into actual objects.
14
12
  | - __Errors handling__: Rescue errors and figure out what to do with them.
15
13
  MESSAGE
16
14
 
@@ -4,10 +4,9 @@ require 'rubocop'
4
4
 
5
5
  module Ducalis
6
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>).
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | Please, add comment why are you including non-realized gem version for %<gem>s.
9
+ | It will increase [bus-factor](<https://en.wikipedia.org/wiki/Bus_factor>).
11
10
  MESSAGE
12
11
 
13
12
  def investigate(processed_source)
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+
5
+ module Ducalis
6
+ class UnlockedGem < RuboCop::Cop::Cop
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | It's better to lock gem versions explicitly with pessimistic operator (~>).
9
+ MESSAGE
10
+
11
+ def investigate(processed_source)
12
+ return unless processed_source.ast
13
+ gem_declarations(processed_source.ast).select do |node|
14
+ _, _, gemname, _args = *node
15
+ add_offense(node, :selector,
16
+ format(OFFENSE, gem: gemname.loc.expression.source))
17
+ end
18
+ end
19
+
20
+ def_node_search :gem_declarations, '(send nil :gem (str _))'
21
+ end
22
+ end
@@ -4,16 +4,15 @@ require 'rubocop'
4
4
 
5
5
  module Ducalis
6
6
  class UselessOnly < RuboCop::Cop::Cop
7
- OFFENSE = <<-MESSAGE.gsub(/^ +\|/, '').strip
8
- | Seems like there is no any reason to keep before filter only for one
9
- | action. Maybe it will be better to inline it?
10
- |
7
+ OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
8
+ | Seems like there is no any reason to keep before filter only for one action. Maybe it will be better to inline it?
9
+
11
10
  | ```ruby
12
11
  | before_filter :do_something, only: %i[index]
13
12
  | def index; end
14
- |
13
+
15
14
  | # to
16
- |
15
+
17
16
  | def index
18
17
  | do_something
19
18
  | end
@@ -96,7 +96,7 @@ class Documentation
96
96
 
97
97
  def spec_cases_for(f)
98
98
  source_code = File.read(
99
- f.sub('/lib/ducalis/', '/spec/')
99
+ f.sub('/lib/', '/spec/')
100
100
  .sub(/.rb$/, '_spec.rb')
101
101
  )
102
102
  SpecsProcessor.new.tap do |processor|
@@ -4,8 +4,10 @@ module Ducalis
4
4
  module PassedArgs
5
5
  module_function
6
6
 
7
+ RUBOCOP_FLAGS = %w(-D).freeze
8
+
7
9
  def help_command?
8
- ARGV.any? { |arg| Thor::HELP_MAPPINGS.include?(arg) }
10
+ ARGV.any? { |arg| (Thor::HELP_MAPPINGS - RUBOCOP_FLAGS).include?(arg) }
9
11
  end
10
12
 
11
13
  def ci_mode?
@@ -8,6 +8,14 @@ module RuboCop
8
8
  end
9
9
  end
10
10
 
11
+ class CommentConfig
12
+ ::Ducalis::Utils.silence_warnings do
13
+ COMMENT_DIRECTIVE_REGEXP = Regexp.new(
14
+ ('# ducalis : ((?:dis|en)able)\b ' + COPS_PATTERN).gsub(' ', '\s*')
15
+ )
16
+ end
17
+ end
18
+
11
19
  class TargetFinder
12
20
  prepend PatchedRubocop::GitTurgetFinder
13
21
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ducalis
4
- VERSION = '0.5.10'
4
+ VERSION = '0.5.11'
5
5
  end
data/lib/ducalis.rb CHANGED
@@ -37,6 +37,7 @@ require 'ducalis/cops/case_mapping'
37
37
  require 'ducalis/cops/controllers_except'
38
38
  require 'ducalis/cops/keyword_defaults'
39
39
  require 'ducalis/cops/module_like_class'
40
+ require 'ducalis/cops/options_argument'
40
41
  require 'ducalis/cops/params_passing'
41
42
  require 'ducalis/cops/possible_tap'
42
43
  require 'ducalis/cops/private_instance_assign'
@@ -46,7 +47,9 @@ require 'ducalis/cops/raise_without_error_class'
46
47
  require 'ducalis/cops/regex_cop'
47
48
  require 'ducalis/cops/rest_only_cop'
48
49
  require 'ducalis/cops/rubocop_disable'
50
+ require 'ducalis/cops/standard_methods'
49
51
  require 'ducalis/cops/strings_in_activerecords'
50
52
  require 'ducalis/cops/too_long_workers'
51
53
  require 'ducalis/cops/uncommented_gem'
54
+ require 'ducalis/cops/unlocked_gem'
52
55
  require 'ducalis/cops/useless_only'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ducalis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.10
4
+ version: 0.5.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ignat Zakrevsky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-21 00:00:00.000000000 Z
11
+ date: 2017-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: git
@@ -136,6 +136,7 @@ files:
136
136
  - lib/ducalis/cops/controllers_except.rb
137
137
  - lib/ducalis/cops/keyword_defaults.rb
138
138
  - lib/ducalis/cops/module_like_class.rb
139
+ - lib/ducalis/cops/options_argument.rb
139
140
  - lib/ducalis/cops/params_passing.rb
140
141
  - lib/ducalis/cops/possible_tap.rb
141
142
  - lib/ducalis/cops/preferable_methods.rb
@@ -145,9 +146,11 @@ files:
145
146
  - lib/ducalis/cops/regex_cop.rb
146
147
  - lib/ducalis/cops/rest_only_cop.rb
147
148
  - lib/ducalis/cops/rubocop_disable.rb
149
+ - lib/ducalis/cops/standard_methods.rb
148
150
  - lib/ducalis/cops/strings_in_activerecords.rb
149
151
  - lib/ducalis/cops/too_long_workers.rb
150
152
  - lib/ducalis/cops/uncommented_gem.rb
153
+ - lib/ducalis/cops/unlocked_gem.rb
151
154
  - lib/ducalis/cops/useless_only.rb
152
155
  - lib/ducalis/documentation.rb
153
156
  - lib/ducalis/passed_args.rb