jeudi 29 décembre 2016

RUBOCOP - A RUBY STATIC CODE ANALYZER, BASED ON THE COMMUNITY RUBY STYLE GUIDE

RUBOCOP - A RUBY STATIC CODE ANALYZER, BASED ON THE COMMUNITY RUBY STYLE GUIDE


RuboCop is a Ruby static code analyzer. Out of the box it will enforce many of the guidelines outlined in the community Ruby Style Guide .

Most aspects of its behavior can be tweaked via various configuration options.

Installation 
RuboCop 's installation is pretty standard: 
$ gem install rubocop
If you'd rather install RuboCop using bundler , don't require it in your Gemfile 
gem 'rubocop', require: false

Basic Usage 
Running rubocop with no arguments will check all Ruby source files in the current directory: 
$ rubocop
Alternatively you can pass rubocop a list of files and directories to check: 
$ rubocop app spec lib/something.rb
Here's RuboCop in action. Consider the following Ruby source code: 
def badName
  if something
    test
    end
end
Running RuboCop on it (assuming it's in a file named test.rb ) would produce the following report: 
Inspecting 1 file
W

Offenses:

test.rb:1:5: C: Use snake_case for method names.
def badName
    ^^^^^^^
test.rb:2:3: C: Use a guard clause instead of wrapping the code inside a conditional expression.
  if something
  ^^
test.rb:2:3: C: Favor modifier if usage when having a single-line body. Another good alternative is the usage of control flow &&/||.
  if something
  ^^
test.rb:4:5: W: end at 4, 4 is not aligned with if at 2, 2
    end
    ^^^

1 file inspected, 4 offenses detected
For more details check the available command-line options: 
$ rubocop -h
Command flagDescription
-v/--versionDisplays the current version and exits.
-V/--verbose-versionDisplays the current version plus the version of Parser and Ruby.
-L/--list-target-filesList all files RuboCop will inspect.
-F/--fail-fastInspects in modification time order and stops after first file with offenses.
-C/--cacheStore and reuse results for faster operation.
-d/--debugDisplays some extra debug output.
-D/--display-cop-namesDisplays cop names in offense messages.
-c/--configRun with specified config file.
-f/--formatChoose a formatter.
-o/--outWrite output to a file instead of STDOUT.
-r/--requireRequire Ruby file (see Loading Extensions ).
-R/--railsRun extra Rails cops.
-l/--lintRun only lint cops.
-a/--auto-correctAuto-correct certain offenses. Note: Experimental - use with caution.
--onlyRun only the specified cop(s) and/or cops in the specified departments.
--exceptRun all cops enabled by configuration except the specified cop(s) and/or departments.
--auto-gen-configGenerate a configuration file acting as a TODO list.
--exclude-limitLimit how many individual files --auto-gen-config can list in Exclude parameters, default is 15.
--show-copsShows available cops and their configuration.
--fail-levelMinimum severity for exit with error code. Full severity name or upper case initial can be given. Normally, auto-corrected offenses are ignored. Use or autocorrect if you'd like them to trigger failure.
-s/--stdinPipe source from STDIN. This is useful for editor integration.

Cops 
In RuboCop lingo the various checks performed on the code are called cops. There are several cop departments. 
You can also load custom cops 

Style 
Most of the cops in RuboCop are so called style cops that check for stylistics problems in your code. Almost all of the them are based on the Ruby Style Guide. Many of the style cops have configurations options allowing them to support different popular coding conventions. 

Lint 
Lint cops check for possible errors and very bad practices in your code. RuboCop implements in a portable way all built-in MRI lint checks ( ruby -wc ) and adds a lot of extra lint checks of its own. You can run only the lint cops like this: 
$ rubocop -l
The -l --lint option can be used together with --only to run all the enabled lint cops plus a selection of other cops. 
Disabling any of the lint cops is generally a bad idea. 

Metrics 
Metrics cops deal with properties of the source code that can be measured, such as class length, method length, etc. Generally speaking, they have a configuration parameter called Max and when running rubocop --auto-gen-config , this parameter will be set to the highest value found for the inspected code. 

Rails 
Rails cops are specific to the Ruby on Rails framework. Unlike style and lint cops they are not used by default and you have to request them specifically: 
$ rubocop -R
or add the following directive to your .rubocop.yml 
AllCops:
  RunRailsCops: true

Configuration 
The behavior of RuboCop can be controlled via the .rubocop.yml configuration file. It makes it possible to enable/disable certain cops (checks) and to alter their behavior if they accept any parameters. The file can be placed either in your home directory or in some project directory. 
RuboCop will start looking for the configuration file in the directory where the inspected file is and continue its way up to the root directory. 
The file has the following format: 
inherit_from: ../.rubocop.yml

Style/Encoding:
  Enabled: false

Metrics/LineLength:
  Max: 99
Note : Qualifying cop name with its type, e.g., Style , is recommended, but not necessary as long as the cop name is unique across all types. 

Inheritance 
RuboCop supports inheriting configuration from one or more supplemental configuration files at runtime. 

Inheriting from another configuration file in the project 
The optional inherit_from directive is used to include configuration from one or more files. This makes it possible to have the common project settings in the.rubocop.yml file at the project root, and then only the deviations from those rules in the subdirectories. The files can be given with absolute paths or paths relative to the file where they are referenced. The settings after an inherit_from directive override any settings in the file(s) inherited from. When multiple files are included, the first file in the list has the lowest precedence and the last one has the highest. The format for multiple inheritance is: 
inherit_from:
  - ../.rubocop.yml
  - ../conf/.rubocop.yml

Inheriting configuration from a dependency gem 
The optional inherit_gem directive is used to include configuration from one or more gems external to the current project. This makes it possible to inherit a shared dependency's RuboCop configuration that can be used from multiple disparate projects. 
Configurations inherited in this way will be essentially prepended to the inherit_from directive, such that the inherit_gem configurations will be loaded first, then the inherit_from relative file paths will be loaded (overriding the configurations from the gems), and finally the remaining directives in the configuration file will supersede any of the inherited configurations. This means the configurations inherited from one or more gems have the lowest precedence of inheritance. 
The directive should be formatted as a YAML Hash using the gem name as the key and the relative path within the gem as the value: 
inherit_gem:
  rubocop: config/default.yml
  my-shared-gem: .rubocop.yml
  cucumber: conf/rubocop.yml
Note : If the shared dependency is declared using a Bundler Gemfile and the gem was installed using bundle install , it would be necessary to also invoke RuboCop using Bundler in order to find the dependency's installation path at runtime: 
$ bundle exec rubocop <options...>

Defaults 
The file config/default.yml under the RuboCop home directory contains the default settings that all configurations inherit from. Project and personal .rubocop.yml files need only make settings that are different from the default ones. If there is no .rubocop.yml file in the project or home directory, config/default.yml will be used.

Including/Excluding files 
RuboCop checks all files found by a recursive search starting from the directory it is run in, or directories given as command line arguments. However, it only recognizes files ending with .rb or extensionless files with a #!.*ruby declaration as Ruby files. Hidden directories (i.e., directories whose names start with a dot) are not searched by default. If you'd like it to check files that are not included by default, you'll need to pass them in on the command line, or to add entries for them under AllCops /Include . Files and directories can also be ignored through AllCops Exclude 
Here is an example that might be used for a Rails project: 
AllCops:
  Include:
    - '**/Rakefile'
    - '**/config.ru'
  Exclude:
    - 'db/**/*'
    - 'config/**/*'
    - 'script/**/*'
    - !ruby/regexp /old_and_unused\.rb$/

# other configuration
# ...
Files and directories are specified relative to the .rubocop.yml file. 
Note : Patterns that are just a file name, e.g. Rakefile , will match that file name in any directory, but this pattern style deprecated. The correct way to match the file in any directory, including the current, is **/Rakefile 
Note : The pattern config/** will match any file recursively under config , but this pattern style is deprecated and should be replaced by config/**/* 
Note : The Include and Exclude parameters are special. They are valid for the directory tree starting where they are defined. They are not shadowed by the setting of Include and Exclude in other .rubocop.yml files in subdirectories. This is different from all other parameters, who follow RuboCop's general principle that configuration for an inspected file is taken from the nearest .rubocop.yml , searching upwards. 
Cops can be run only on specific sets of files when that's needed (for instance you might want to run some Rails model checks only on files whose paths matchapp/models/*.rb ). All cops support the Include param. 
Rails/DefaultScope:
  Include:
    - app/models/*.rb
Cops can also exclude only specific sets of files when that's needed (for instance you might want to run some cop only on a specific file). All cops support the Excludeparam. 
Rails/DefaultScope:
  Exclude:
    - app/models/problematic.rb

Generic configuration parameters 
In addition to Include and Exclude , the following parameters are available for every cop. 

Enabled 
Specific cops can be disabled by setting Enabled to false for that specific cop. 
Metrics/LineLength:
  Enabled: false
Most cops are enabled by default. Some cops, configured in config/disabled.yml , are disabled by default. The cop enabling process can be altered by settingDisabledByDefault to true 
AllCops:
  DisabledByDefault: true
All cops are then disabled by default, and only cops appearing in user configuration files are enabled. Enabled: true does not have to be set for cops in user configuration. They will be enabled anyway. 

Severity 
Each cop has a default severity level based on which department it belongs to. The level is warning for Lint and convention for all the others. Cops can customize their severity level. Allowed params are refactor convention warning error and fatal 
There is one exception from the general rule above and that is Lint/Syntax , a special cop that checks for syntax errors before the other cops are invoked. It can not be disabled and its severity ( fatal ) can not be changed in configuration. 
Metrics/CyclomaticComplexity:
  Severity: warning

AutoCorrect 
Cops that support the --auto-correct option can have that support disabled. For example: 
Style/PerlBackrefs:
  AutoCorrect: false

Automatically Generated Configuration 
If you have a code base with an overwhelming amount of offenses, it can be a good idea to use rubocop --auto-gen-config and add an inherit_from: .rubocop_todo.yml in your .rubocop.yml . The generated file .rubocop_todo.yml contains configuration to disable cops that currently detect an offense in the code by excluding the offending files, or disabling the cop altogether once a file count limit has been reached. 
By adding the option --exclude-limit COUNT , e.g., rubocop --auto-gen-config --exclude-limit 5 , you can change how many files are excluded before the cop is entirely disabled. The default COUNT is 15. 
Then you can start removing the entries in the generated .rubocop_todo.yml file one by one as you work through all the offenses in the code. 

Disabling Cops within Source Code 
One or more individual cops can be disabled locally in a section of a file by adding a comment such as 
# rubocop:disable Metrics/LineLength, Style/StringLiterals
[...]
# rubocop:enable Metrics/LineLength, Style/StringLiterals
You can also disable all cops with 
# rubocop:disable all
[...]
# rubocop:enable all
One or more cops can be disabled on a single line with an end-of-line comment. 
for x in (0..19) # rubocop:disable Style/AvoidFor

Formatters 
You can change the output format of RuboCop by specifying formatters with the -f/--format option. RuboCop ships with several built-in formatters, and also you can create your custom formatter. 
Additionally the output can be redirected to a file instead of $stdout with the -o/--out option. 
Some of the built-in formatters produce machine-parsable output and they are considered public APIs. The rest of the formatters are for humans, so parsing their outputs is discouraged. 
You can enable multiple formatters at the same time by specifying -f/--format multiple times. The -o/--out option applies to the previously specified -f/--format , or the default progress format if no -f/--format is specified before the -o/--out option. 
# Simple format to $stdout.
$ rubocop --format simple

# Progress (default) format to the file result.txt.
$ rubocop --out result.txt

# Both progress and offense count formats to $stdout.
# The offense count formatter outputs only the final summary,
# so you'll mostly see the outputs from the progress formatter,
# and at the end the offense count summary will be outputted.
$ rubocop --format progress --format offenses

# Progress format to $stdout, and JSON format to the file rubocop.json.
$ rubocop --format progress --format json --out rubocop.json
#         ~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
#                 |               |_______________|
#              $stdout

# Progress format to result.txt, and simple format to $stdout.
$ rubocop --output result.txt --format simple
#         ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
#                  |                 |
#           default format        $stdout
You can also load custom formatters 

Progress Formatter (default) 
The default progress formatter outputs a character for each inspected file, and at the end it displays all detected offenses in the clang format. A represents a clean file, and each of the capital letters means the severest offense (convention, warning, error or fatal) found in a file. 
$ rubocop
Inspecting 26 files
..W.C....C..CWCW.C...WC.CC

Offenses:

lib/foo.rb:6:5: C: Missing top-level class documentation comment.
    class Foo
    ^^^^^

...

26 files inspected, 46 offenses detected

Clang Style Formatter 
The clang formatter displays the offenses in a manner similar to clang 
$ rubocop test.rb
Inspecting 1 file
W

Offenses:

test.rb:1:5: C: Use snake_case for method names.
def badName
    ^^^^^^^
test.rb:2:3: C: Use a guard clause instead of wrapping the code inside a conditional expression.
  if something
  ^^
test.rb:2:3: C: Favor modifier if usage when having a single-line body. Another good alternative is the usage of control flow &&/||.
  if something
  ^^
test.rb:4:5: W: end at 4, 4 is not aligned with if at 2, 2
    end
    ^^^

1 file inspected, 4 offenses detected

Fuubar Style Formatter 
The fuubar style formatter displays a progress bar and shows details of offenses in the clang format as soon as they are detected. This is inspired by the Fuubarformatter for RSpec. 
$ rubocop --format fuubar
lib/foo.rb.rb:1:1: C: Use snake_case for methods and variables.
def badName
    ^^^^^^^
lib/bar.rb:13:14: W: File.exists? is deprecated in favor of File.exist?.
        File.exists?(path)
             ^^^^^^^
 22/53 files |======== 43 ========>                           |  ETA: 00:00:02

Emacs Style Formatter 
Machine-parsable 
The emacs formatter displays the offenses in a format suitable for consumption by Emacs (and possibly other tools). 
$ rubocop --format emacs test.rb
/Users/bozhidar/projects/test.rb:1:1: C: Use snake_case for methods and variables.
/Users/bozhidar/projects/test.rb:2:3: C: Favor modifier if/unless usage when you have a single-line body. Another good alternative is the usage of control flow &&/||.
/Users/bozhidar/projects/test.rb:4:5: W: end at 4, 4 is not aligned with if at 2, 2

Simple Formatter 
The name of the formatter says it all :-) 
$ rubocop --format simple test.rb
== test.rb ==
C:  1:  5: Use snake_case for method names.
C:  2:  3: Use a guard clause instead of wrapping the code inside a conditional expression.
C:  2:  3: Favor modifier if usage when having a single-line body. Another good alternative is the usage of control flow &&/||.
W:  4:  5: end at 4, 4 is not aligned with if at 2, 2

1 file inspected, 4 offenses detected

File List Formatter 
Machine-parsable 
Sometimes you might want to just open all files with offenses in your favorite editor. This formatter outputs just the names of the files with offenses in them and makes it possible to do something like: 
$ rubocop --format files | xargs vim

JSON Formatter 
Machine-parsable 
You can get RuboCop's inspection result in JSON format by passing --format json option in command line. The JSON structure is like the following example: 
{
  "metadata": {
    "rubocop_version": "0.9.0",
    "ruby_engine": "ruby",
    "ruby_version": "2.0.0",
    "ruby_patchlevel": "195",
    "ruby_platform": "x86_64-darwin12.3.0"
  },
  "files": [{
      "path": "lib/foo.rb",
      "offenses": []
    }, {
      "path": "lib/bar.rb",
      "offenses": [{
          "severity": "convention",
          "message": "Line is too long. [81/80]",
          "cop_name": "LineLength",
          "corrected": true,
          "location": {
            "line": 546,
            "column": 80,
            "length": 4
          }
        }, {
          "severity": "warning",
          "message": "Unreachable code detected.",
          "cop_name": "UnreachableCode",
          "corrected": false,
          "location": {
            "line": 15,
            "column": 9,
            "length": 10
          }
        }
      ]
    }
  ],
  "summary": {
    "offense_count": 2,
    "target_file_count": 2,
    "inspected_file_count": 2
  }
}

Offense Count Formatter 
Sometimes when first applying RuboCop to a codebase, it's nice to be able to see where most of your style cleanup is going to be spent. 
With this in mind, you can use the offense count formatter to outline the offended cops and the number of offenses found for each by running: 
$ rubocop --format offenses

87   Documentation
12   DotPosition
8    AvoidGlobalVars
7    EmptyLines
6    AssignmentInCondition
4    Blocks
4    CommentAnnotation
3    BlockAlignment
1    IndentationWidth
1    AvoidPerlBackrefs
1    ColonMethodCall
--
134  Total

HTML Formatter 
Useful for CI environments. It will create an HTML report like this 
$ rubocop --format html -o rubocop.html

Compatibility 
RuboCop supports the following Ruby implementations: 
  • MRI 1.9.3
  • MRI 2.0
  • MRI 2.1
  • MRI 2.2
  • JRuby in 1.9 mode
  • Rubinius 2.0+

Editor integration 

Emacs 
rubocop.el is a simple Emacs interface for RuboCop. It allows you to run RuboCop inside Emacs and quickly jump between problems in your code. 
flycheck > 0.9 also supports RuboCop and uses it by default when available. 

Vim 
The vim-rubocop plugin runs RuboCop and displays the results in Vim. 
There's also a RuboCop checker in syntastic 

Sublime Text 
If you're a ST user you might find the Sublime RuboCop plugin useful. 

Brackets 
The brackets-rubocop extension displays RuboCop results in Brackets. It can be installed via the extension manager in Brackets. 

TextMate2 
The textmate2-rubocop bundle displays formatted RuboCop results in a new window. Installation instructions can be found here 

Atom 
The atom-lint package runs RuboCop and highlights the offenses in Atom. 
You can also use the linter-rubocop plugin for Atom's linter 

LightTable 
The lt-rubocop plugin provides LightTable integration. 

RubyMine 
The rubocop-for-rubymine plugin provides basic RuboCop integration for RubyMine/IntelliJ IDEA. 

Other Editors 
Here's one great opportunity to contribute to RuboCop - implement RuboCop integration for your favorite editor. 

Git pre-commit hook integration 
overcommit is a fully configurable and extendable Git commit hook manager. To use RuboCop with overcommit, add the following to your .overcommit.yml file: 
PreCommit:
  RuboCop:
    enabled: true

Guard integration 
If you're fond of Guard you might like guard-rubocop . It allows you to automatically check Ruby code style with RuboCop when files are modified. 

Rake integration 
To use RuboCop in your Rakefile add the following: 
require 'rubocop/rake_task'

RuboCop::RakeTask.new
If you run rake -T , the following two RuboCop tasks should show up: 
rake rubocop                                  # Run RuboCop
rake rubocop:auto_correct                     # Auto-correct RuboCop offenses
The above will use default values 
require 'rubocop/rake_task'

desc 'Run RuboCop on the lib directory'
RuboCop::RakeTask.new(:rubocop) do |task|
  task.patterns = ['lib/**/*.rb']
  # only show the files with failures
  task.formatters = ['files']
  # don't abort rake on failure
  task.fail_on_error = false
end

Caching 
Large projects containing hundreds or even thousands of files can take a really long time to inspect, but RuboCop has functionality to mitigate this problem. There's a caching mechanism that stores information about offenses found in inspected files. 

Cache Validity 
Later runs will be able to retrieve this information and present the stored information instead of inspecting the file again. This will be done if the cache for the file is still valid, which it is if there are no changes in: 
  • the contents of the inspected file
  • RuboCop configuration for the file
  • the options given to rubocop , with some exceptions that have no bearing on which offenses are reported
  • the Ruby version used to invoke rubocop
  • version of the rubocop program (or to be precise, anything in the source code of the invoked rubocop program)

Enabling and Disabling the Cache 
The caching functionality is enabled if the configuration parameter AllCops: UseCache is true , which it is by default. The command line option --cache falsecan be used to turn off caching, thus overriding the configuration parameter. If AllCops: UseCache is set to false in the local .rubocop.yml , then it's --cache true that overrides the setting. 

Cache Path 
By default, the cache is stored in in a subdirectory of the temporary directory, /tmp/rubocop_cache/ on Unix-like systems. The configuration parameter AllCops: CacheRootDirectory can be used to set it to a different path. One reason to use this option could be that there's a network disk where users on different machines want to have a common RuboCop cache. Another could be that a Continuous Integration system allows directories, but not a temporary directory, to be saved between runs. 

Cache Pruning 
Each time a file has changed, its offenses will be stored under a new key in the cache. This means that the cache will continue to grow until we do something to stop it. The configuration parameter AllCops: MaxFilesInCache sets a limit, and when the number of files in the cache exceeds that limit, the oldest files will be automatially removed from the cache. 

Extensions 
It's possible to extend RuboCop with custom cops and formatters. 

Loading Extensions 
Besides the --require command line option you can also specify ruby files that should be loaded with the optional require directive in the .rubocop.yml file:
require:
 - ../my/custom/file.rb
 - rubocop-extension
Note: The paths are directly passed to Kernel.require . If your extension file is not in $LOAD_PATH , you need to specify the path as relative path prefixed with ./explicitly, or absolute path. 

Custom Cops 
You can configure the custom cops in your .rubocop.yml just like any other cop. 

Known Custom Cops 

Custom Formatters 
You can customize RuboCop's output format with custom formatters. 

Creating Custom Formatter 
To implement a custom formatter, you need to subclass RuboCop::Formatter::BaseFormatter and override some methods, or implement all formatter API methods by duck typing. 
Please see the documents below for more formatter API details. 

Using Custom Formatter in Command Line 
You can tell RuboCop to use your custom formatter with a combination of --format and --require option. For example, when you have definedMyCustomFormatter in ./path/to/my_custom_formatter.rb , you would type this command: 
$ rubocop --require ./path/to/my_custom_formatter --format MyCustomFormatter


Aucun commentaire:

Enregistrer un commentaire