ducalis 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ ```