ducalis 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +12 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +6 -0
  7. data/DOCUMENTATION.md +556 -0
  8. data/Dockerfile +33 -0
  9. data/Gemfile +21 -0
  10. data/Gemfile.lock +112 -0
  11. data/LICENSE +21 -0
  12. data/README.md +15 -0
  13. data/Rakefile +17 -0
  14. data/bin/ducalis +38 -0
  15. data/bootstrap.sh +9 -0
  16. data/config.ru +18 -0
  17. data/config/.ducalis.yml +57 -0
  18. data/ducalis.gemspec +39 -0
  19. data/lib/ducalis.rb +47 -0
  20. data/lib/ducalis/adapters/base.rb +17 -0
  21. data/lib/ducalis/adapters/circle_ci.rb +21 -0
  22. data/lib/ducalis/adapters/custom.rb +21 -0
  23. data/lib/ducalis/adapters/pull_request.rb +26 -0
  24. data/lib/ducalis/cli.rb +35 -0
  25. data/lib/ducalis/commentators/console.rb +59 -0
  26. data/lib/ducalis/commentators/github.rb +50 -0
  27. data/lib/ducalis/cops/callbacks_activerecord.rb +47 -0
  28. data/lib/ducalis/cops/controllers_except.rb +38 -0
  29. data/lib/ducalis/cops/keyword_defaults.rb +23 -0
  30. data/lib/ducalis/cops/module_like_class.rb +68 -0
  31. data/lib/ducalis/cops/params_passing.rb +35 -0
  32. data/lib/ducalis/cops/private_instance_assign.rb +39 -0
  33. data/lib/ducalis/cops/protected_scope_cop.rb +38 -0
  34. data/lib/ducalis/cops/raise_withour_error_class.rb +20 -0
  35. data/lib/ducalis/cops/regex_cop.rb +52 -0
  36. data/lib/ducalis/cops/rest_only_cop.rb +33 -0
  37. data/lib/ducalis/cops/rubocop_disable.rb +19 -0
  38. data/lib/ducalis/cops/strings_in_activerecords.rb +39 -0
  39. data/lib/ducalis/cops/uncommented_gem.rb +33 -0
  40. data/lib/ducalis/cops/useless_only.rb +50 -0
  41. data/lib/ducalis/documentation.rb +101 -0
  42. data/lib/ducalis/passed_args.rb +22 -0
  43. data/lib/ducalis/patched_rubocop/diffs.rb +30 -0
  44. data/lib/ducalis/patched_rubocop/ducalis_config_loader.rb +14 -0
  45. data/lib/ducalis/patched_rubocop/git_files_access.rb +42 -0
  46. data/lib/ducalis/patched_rubocop/git_runner.rb +14 -0
  47. data/lib/ducalis/patched_rubocop/git_turget_finder.rb +11 -0
  48. data/lib/ducalis/patched_rubocop/rubo_cop.rb +32 -0
  49. data/lib/ducalis/runner.rb +48 -0
  50. data/lib/ducalis/utils.rb +27 -0
  51. data/lib/ducalis/version.rb +5 -0
  52. metadata +201 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0ab05ed0a584986aef9f907edf69722b74ff8ee6
4
+ data.tar.gz: 4d503a2e465a872c713b5a290872be2bfd24aecb
5
+ SHA512:
6
+ metadata.gz: c664d527142a8799a0b2a052961e8b70e8fe90cc4bec1f9fc7d9fdaccf3f0d58b44901753078e52ce286b33a3d5cc0fefcbc2e7413c440fd84f290303f59b6d8
7
+ data.tar.gz: fddf53bf677b26fc4a1eeea987196bdcb6a29b9e794fa9aba1a86100f25caf921129f9f31d529c41781d9cb39c137a1e641b0697c69e3968284cf82b870d5215
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --require spec_helper
@@ -0,0 +1,12 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+ UseCache: false
4
+
5
+ Metrics/BlockLength:
6
+ Exclude:
7
+ - 'spec/**/*.rb'
8
+ Lint/InterpolationCheck:
9
+ Exclude:
10
+ - 'spec/**/*.rb'
11
+ Style/Documentation:
12
+ Enabled: false
@@ -0,0 +1 @@
1
+ 2.4.2
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.4.2
4
+ before_install:
5
+ - gem update --system
6
+ - gem --version
@@ -0,0 +1,556 @@
1
+ ## Ducalis::CallbacksActiverecord
2
+
3
+ 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. You can read more [here](https://medium.com/planet-arkency/a61fd75ab2d3).
4
+ - rejects ActiveRecord classes which contains callbacks
5
+ ```ruby
6
+
7
+ class A < ActiveRecord::Base
8
+ before_create :generate_code
9
+ end
10
+
11
+ ```
12
+ - ignores non-ActiveRecord classes which contains callbacks
13
+ ```ruby
14
+
15
+ class A < SomeBasicClass
16
+ before_create :generate_code
17
+ end
18
+
19
+ ```
20
+ ## Ducalis::ControllersExcept
21
+
22
+ Prefer to use `:only` over `:except` in controllers because it's more explicit and will be easier to maintain for new developers.
23
+ - raises for `before_filters` with `except` method as array
24
+ ```ruby
25
+
26
+ class MyController < ApplicationController
27
+ before_filter :something, except: [:index]
28
+ def index; end
29
+ def edit; end
30
+ private
31
+ def something; end
32
+ end
33
+
34
+ ```
35
+ - raises for filters with many actions and only one `except` method
36
+ ```ruby
37
+
38
+ class MyController < ApplicationController
39
+ before_filter :something, :load_me, except: %i[edit]
40
+ def index; end
41
+ def edit; end
42
+ private
43
+ def something; end
44
+ def load_me; end
45
+ end
46
+
47
+ ```
48
+ - ignores `before_filters` without arguments
49
+ ```ruby
50
+
51
+ class MyController < ApplicationController
52
+ before_filter :something
53
+ def index; end
54
+ private
55
+ def something; end
56
+ end
57
+
58
+ ```
59
+ ## Ducalis::KeywordDefaults
60
+
61
+ Prefer to use keyword arguments for defaults. It increases readability and reduces ambiguities.
62
+ - rejects if method definition contains default values
63
+ ```ruby
64
+ def some_method(a, b, c = 3); end
65
+ ```
66
+ - rejects if class method definition contains default values
67
+ ```ruby
68
+ def self.some_method(a, b, c = 3); end
69
+ ```
70
+ - works if method definition contains default values through keywords
71
+ ```ruby
72
+ def some_method(a, b, c: 3); end
73
+ ```
74
+ - works for methods without arguments
75
+ ```ruby
76
+ def some_method; end
77
+ ```
78
+ - works for class methods without arguments
79
+ ```ruby
80
+ def self.some_method; end
81
+ ```
82
+ ## Ducalis::ModuleLikeClass
83
+
84
+ Seems like it will be better to define initialize and pass %<args>s there instead of each method.
85
+ - raise if class doesn't contain constructor but accept the same args
86
+ ```ruby
87
+
88
+ class MyClass
89
+
90
+ def initialize(customer)
91
+ # ...
92
+ end
93
+
94
+ def approve(task, estimate, some_args_1)
95
+ # ...
96
+ end
97
+
98
+ def decline(user, task, estimate, some_args_2)
99
+ # ...
100
+ end
101
+
102
+ private
103
+
104
+ def anything_you_want(args)
105
+ # ...
106
+ end
107
+ end
108
+
109
+ ```
110
+ - raise for class with only one public method with args
111
+ ```ruby
112
+
113
+ class MyClass
114
+ def approve(task)
115
+ # ...
116
+ end
117
+
118
+ private
119
+
120
+ def anything_you_want(args)
121
+ # ...
122
+ end
123
+ end
124
+
125
+ ```
126
+ - ignores classes with custom includes
127
+ ```ruby
128
+
129
+ class MyClass
130
+ include Singleton
131
+
132
+ def approve(task)
133
+ # ...
134
+ end
135
+ end
136
+
137
+ ```
138
+ - ignores classes with inheritance
139
+ ```ruby
140
+
141
+ class MyClass < AnotherClass
142
+ def approve(task)
143
+ # ...
144
+ end
145
+
146
+ private
147
+
148
+ def anything_you_want(args)
149
+ # ...
150
+ end
151
+ end
152
+
153
+ ```
154
+ - ignores classes with one method and initializer
155
+ ```ruby
156
+
157
+ class MyClass
158
+ def initialize(task)
159
+ # ...
160
+ end
161
+
162
+ def call(args)
163
+ # ...
164
+ end
165
+ end
166
+
167
+ ```
168
+ ## Ducalis::ParamsPassing
169
+
170
+ It's better to pass already preprocessed params hash to services. Or you can use
171
+ `arcane` gem
172
+ - raise if user pass `params` as argument from controller
173
+ ```ruby
174
+
175
+ class MyController < ApplicationController
176
+ def index
177
+ MyService.new(params).call
178
+ end
179
+ end
180
+
181
+ ```
182
+ - raise if user pass `params` as any argument from controller
183
+ ```ruby
184
+
185
+ class MyController < ApplicationController
186
+ def index
187
+ MyService.new(first_arg, params).call
188
+ end
189
+ end
190
+
191
+ ```
192
+ - raise if user pass `params` as keyword argument from controller
193
+ ```ruby
194
+
195
+ class MyController < ApplicationController
196
+ def index
197
+ MyService.new(first_arg, any_name: params).call
198
+ end
199
+ end
200
+
201
+ ```
202
+ - ignores passing only one `params` field
203
+ ```ruby
204
+
205
+ class MyController < ApplicationController
206
+ def index
207
+ MyService.new(first_arg, params[:id]).call
208
+ end
209
+ end
210
+
211
+ ```
212
+ - ignores passing processed `params`
213
+ ```ruby
214
+
215
+ class MyController < ApplicationController
216
+ def index
217
+ MyService.new(first_arg, params.slice(:name)).call
218
+ end
219
+ end
220
+
221
+ ```
222
+ - ignores passing `params` from `arcane` gem
223
+ ```ruby
224
+
225
+ class MyController < ApplicationController
226
+ def index
227
+ MyService.new(params.for(Log).as(user).refine).call
228
+ end
229
+ end
230
+
231
+ ```
232
+ ## Ducalis::PrivateInstanceAssign
233
+
234
+ Please, don't assign instance variables in controller's private methods. It's make hard to understand what variables are available in views.
235
+ - raises for assigning instance variables in controllers private methods
236
+ ```ruby
237
+
238
+ class MyController < ApplicationController
239
+ private
240
+
241
+ def load_employee
242
+ @employee = Employee.find(params[:id])
243
+ end
244
+ end
245
+
246
+ ```
247
+ - raises for memoization variables in controllers private methods
248
+ ```ruby
249
+
250
+ class MyController < ApplicationController
251
+ private
252
+
253
+ def service
254
+ @service ||= Service.new
255
+ end
256
+ end
257
+
258
+ ```
259
+ - ignores memoization variables in controllers private methods with _
260
+ ```ruby
261
+
262
+ class MyController < ApplicationController
263
+ private
264
+
265
+ def service
266
+ @_service ||= Service.new
267
+ end
268
+ end
269
+
270
+ ```
271
+ - ignores assigning instance variables in controllers public methods
272
+ ```ruby
273
+
274
+ class MyController < ApplicationController
275
+ def index
276
+ @employee = load_employee
277
+ end
278
+
279
+ private
280
+
281
+ def load_employee
282
+ Employee.find(params[:id])
283
+ end
284
+ end
285
+
286
+ ```
287
+ ## Ducalis::ProtectedScopeCop
288
+
289
+ Seems like you are using `find` on non-protected scope. Potentially it could
290
+ lead to unauthorized access. It's better to call `find` on authorized resources
291
+ scopes. Example:
292
+
293
+ ```ruby
294
+ current_group.employees.find(params[:id])
295
+ # better then
296
+ Employee.find(params[:id])
297
+ ```
298
+ - raise if somewhere AR search was called on not protected scope
299
+ ```ruby
300
+ Group.find(8)
301
+ ```
302
+ - raise if AR search was called even for chain of calls
303
+ ```ruby
304
+ Group.includes(:some_relation).find(8)
305
+ ```
306
+ - works ignores where statements and still raises error
307
+ ```ruby
308
+ Group.includes(:some_relation).where(name: "John").find(8)
309
+ ```
310
+ ## Ducalis::RaiseWithourErrorClass
311
+
312
+ It's better to add exception class as raise argument. It will make easier to catch and process it later.
313
+ - raise when `raise` called without exception class
314
+ ```ruby
315
+ raise "Something went wrong"
316
+ ```
317
+ - works when `raise` called with exception class
318
+ ```ruby
319
+ raise StandardError, "Something went wrong"
320
+ ```
321
+ - works when `raise` called with exception instance
322
+ ```ruby
323
+ raise StandardError.new("Something went wrong")
324
+ ```
325
+ ## Ducalis::RegexCop
326
+
327
+ It's better to move regex to constants with example instead of direct using it.
328
+ It will allow you to reuse this regex and provide instructions for others.
329
+
330
+ ```ruby
331
+ CONST_NAME = %<constant>s # "%<example>s"
332
+ %<fixed_string>s
333
+ ```
334
+ - raise if somewhere in code used regex which is not moved to const
335
+ ```ruby
336
+
337
+ name = "john"
338
+ puts "hi" if name =~ /john/
339
+
340
+ ```
341
+ - accepts matching constants
342
+ ```ruby
343
+
344
+ REGEX = /john/
345
+ name = "john"
346
+ puts "hi" if name =~ REGEX
347
+
348
+ ```
349
+ - ignores named ruby constants
350
+ ```ruby
351
+
352
+ name = "john"
353
+ puts "hi" if name =~ /[[:alpha:]]/
354
+
355
+ ```
356
+ - ignores dynamic regexs
357
+ ```ruby
358
+
359
+ name = "john"
360
+ puts "hi" if name =~ /.{#{name.length}}/
361
+
362
+ ```
363
+ ## Ducalis::RestOnlyCop
364
+
365
+ It's better for controllers to stay adherent to REST:
366
+ http://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/
367
+ - raise for controllers with non-REST methods
368
+ ```ruby
369
+
370
+ class MyController < ApplicationController
371
+ def index; end
372
+ def non_rest_method; end
373
+ end
374
+
375
+ ```
376
+ - doesn't raise for controllers with private non-REST methods
377
+ ```ruby
378
+
379
+ class MyController < ApplicationController
380
+ def index; end
381
+ private
382
+ def non_rest_method; end
383
+ end
384
+
385
+ ```
386
+ - doesn't raise for controllers with only REST methods
387
+ ```ruby
388
+
389
+ class MyController < ApplicationController
390
+ def index; end
391
+ def show; end
392
+ def new; end
393
+ def edit; end
394
+ def create; end
395
+ def update; end
396
+ def destroy; end
397
+ end
398
+
399
+ ```
400
+ - doesn't raise for non-controllers with non-REST methods
401
+ ```ruby
402
+
403
+ class MyClass
404
+ def index; end
405
+ def non_rest_method; end
406
+ end
407
+
408
+ ```
409
+ ## Ducalis::RubocopDisable
410
+
411
+
412
+ Please, do not suppress RuboCop metrics, may be you can introduce some refactoring or another concept.
413
+
414
+ - raises on RuboCop disable comments
415
+ ```ruby
416
+
417
+ # rubocop:disable Metrics/ParameterLists
418
+ def some_method(a, b, c, d, e, f); end
419
+
420
+ ```
421
+ - doesnt raise on comment without RuboCop disabling
422
+ ```ruby
423
+
424
+ # some meaningful comment
425
+ def some_method(a, b, c, d, e, f); end
426
+
427
+ ```
428
+ ## Ducalis::StringsInActiverecords
429
+
430
+ Please, do not use strings as arguments for %<method_name>s argument.
431
+ It's hard to test, grep sources, code highlighting and so on.
432
+ Consider using of symbols or lambdas for complex expressions.
433
+ - raise for string if argument
434
+ ```ruby
435
+
436
+ before_save :set_full_name,
437
+ if: 'name_changed? || postfix_name_changed?'
438
+
439
+ ```
440
+ - doesnt raise for lambda if argument
441
+ ```ruby
442
+ validates :file, if: -> { remote_url.blank? }
443
+ ```
444
+ ## Ducalis::UncommentedGem
445
+
446
+ Please, add comment why are you including non-realized gem version for %<gem>s.
447
+ It will increase [bus-factor](<https://en.wikipedia.org/wiki/Bus_factor>).
448
+ - raise for gem from github without comment
449
+ ```ruby
450
+
451
+ gem 'a'
452
+ gem 'b', '~> 1.3.1'
453
+ gem 'c', git: 'https://github.com/c/c'
454
+
455
+ ```
456
+ - doesn't raise for gem from github with comment
457
+ ```ruby
458
+
459
+ gem 'a'
460
+ gem 'b', '~> 1.3.1'
461
+ gem 'c', git: 'https://github.com/c/c' # some description
462
+
463
+ ```
464
+ ## Ducalis::UselessOnly
465
+
466
+ Seems like there is no any reason to keep before filter only for one action. Maybe it will be better to inline it?
467
+
468
+ ```ruby
469
+ before_filter :do_something, only: %i[index]
470
+ def index; end
471
+
472
+ # to
473
+
474
+ def index
475
+ do_something
476
+ end
477
+ ```
478
+ - raises for `before_filters` with only one method as array
479
+ ```ruby
480
+
481
+ class MyController < ApplicationController
482
+ before_filter :do_something, only: [:index]
483
+ def index; end
484
+ private
485
+ def do_something; end
486
+ end
487
+
488
+ ```
489
+ - raises for `before_filters` with only one method as keyword array
490
+ ```ruby
491
+
492
+ class MyController < ApplicationController
493
+ before_filter :do_something, only: %i[index]
494
+ def index; end
495
+ private
496
+ def do_something; end
497
+ end
498
+
499
+ ```
500
+ - raises for `before_filters` with many actions and only one method
501
+ ```ruby
502
+
503
+ class MyController < ApplicationController
504
+ before_filter :do_something, :load_me, only: %i[index]
505
+ def index; end
506
+ private
507
+ def do_something; end
508
+ def load_me; end
509
+ end
510
+
511
+ ```
512
+ - raises for `before_filters` with only one method as argument
513
+ ```ruby
514
+
515
+ class MyController < ApplicationController
516
+ before_filter :do_something, only: :index
517
+ def index; end
518
+ private
519
+ def do_something; end
520
+ end
521
+
522
+ ```
523
+ - ignores `before_filters` without arguments
524
+ ```ruby
525
+
526
+ class MyController < ApplicationController
527
+ before_filter :do_something
528
+ def index; end
529
+ private
530
+ def do_something; end
531
+ end
532
+
533
+ ```
534
+ - ignores `before_filters` with `only` and many arguments
535
+ ```ruby
536
+
537
+ class MyController < ApplicationController
538
+ before_filter :do_something, only: %i[index show]
539
+ def index; end
540
+ def show; end
541
+ private
542
+ def do_something; end
543
+ end
544
+
545
+ ```
546
+ - ignores `before_filters` with `except` and one argument
547
+ ```ruby
548
+
549
+ class MyController < ApplicationController
550
+ before_filter :do_something, except: %i[index]
551
+ def index; end
552
+ private
553
+ def do_something; end
554
+ end
555
+
556
+ ```