diff --git a/.github/workflows/rspec_rubocop.yml b/.github/workflows/rspec_rubocop.yml new file mode 100644 index 0000000..bbad1f1 --- /dev/null +++ b/.github/workflows/rspec_rubocop.yml @@ -0,0 +1,41 @@ +# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake +# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby + +name: Ruby + +on: [pull_request, push] + +permissions: + contents: read + +defaults: + run: + working-directory: rubocop-airbnb + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + include: # use bundler 2.3 for ruby versions < 2.6 (https://bundler.io/compatibility.html) + - ruby-version: '2.7' + bundler-version: latest + - ruby-version: '3.0' + bundler-version: latest + - ruby-version: '3.1' + bundler-version: latest + - ruby-version: '3.3' + bundler-version: latest + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@2a9a743e19810b9f3c38060637daf594dbd7b37f # 1.186.0 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler: ${{ matrix.bundler-version }} + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + working-directory: rubocop-airbnb + - name: Run rspec + run: bundle exec rspec + - name: Run rubocop + run: bundle exec rubocop --config .rubocop.yml --fail-fast -d diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..174330c --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# rcov generated +coverage +coverage.data + +# rdoc generated +rdoc + +# bundler +/rubocop-airbnb/.bundle +.bundle +.config +/rubocop-airbnb/vendor/bundle +Gemfile.lock + +# rbenv +.ruby-version + +# jeweler generated +*.gem +pkg + +# etags +TAGS + +# logfiles and tempfiles. +/rubocop-airbnb/log/*.log +/rubocop-airbnb/tmp +.byebug_history diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c81c08d --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2012 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 5999eae..bf2c87c 100644 --- a/README.md +++ b/README.md @@ -2,29 +2,29 @@ This is Airbnb's Ruby Style Guide. -It was inspired by [Github's guide](https://web.archive.org/web/20160410033955/https://github.com/styleguide/ruby) and [Bozhidar Batsov's guide][bbatsov-ruby]. +It was inspired by [GitHub's guide](https://web.archive.org/web/20160410033955/https://github.com/styleguide/ruby) and [RuboCop's guide][rubocop-guide]. Airbnb also maintains a [JavaScript Style Guide][airbnb-javascript]. ## Table of Contents 1. [Whitespace](#whitespace) - 1. [Indentation](#indentation) - 1. [Inline](#inline) - 1. [Newlines](#newlines) + 1. [Indentation](#indentation) + 1. [Inline](#inline) + 1. [Newlines](#newlines) 1. [Line Length](#line-length) 1. [Commenting](#commenting) - 1. [File/class-level comments](#fileclass-level-comments) - 1. [Function comments](#function-comments) - 1. [Block and inline comments](#block-and-inline-comments) - 1. [Punctuation, spelling, and grammar](#punctuation-spelling-and-grammar) - 1. [TODO comments](#todo-comments) - 1. [Commented-out code](#commented-out-code) + 1. [File/class-level comments](#fileclass-level-comments) + 1. [Function comments](#function-comments) + 1. [Block and inline comments](#block-and-inline-comments) + 1. [Punctuation, spelling, and grammar](#punctuation-spelling-and-grammar) + 1. [TODO comments](#todo-comments) + 1. [Commented-out code](#commented-out-code) 1. [Methods](#methods) - 1. [Method definitions](#method-definitions) - 1. [Method calls](#method-calls) + 1. [Method definitions](#method-definitions) + 1. [Method calls](#method-calls) 1. [Conditional Expressions](#conditional-expressions) - 1. [Conditional keywords](#conditional-keywords) - 1. [Ternary operator](#ternary-operator) + 1. [Conditional keywords](#conditional-keywords) + 1. [Ternary operator](#ternary-operator) 1. [Syntax](#syntax) 1. [Naming](#naming) 1. [Classes](#classes) @@ -34,7 +34,7 @@ Airbnb also maintains a [JavaScript Style Guide][airbnb-javascript]. 1. [Regular Expressions](#regular-expressions) 1. [Percent Literals](#percent-literals) 1. [Rails](#rails) - 1. [Scopes](#scopes) + 1. [Scopes](#scopes) 1. [Be Consistent](#be-consistent) 1. [Translation](#translation) @@ -42,8 +42,8 @@ Airbnb also maintains a [JavaScript Style Guide][airbnb-javascript]. ### Indentation -* Use soft-tabs with a two - space-indent.[[link](#default-indentation)] +* Use soft-tabs with a + two-space indent.[[link](#default-indentation)] * Indent `when` as deep as `case`. [[link](#indent-when-as-case)] @@ -162,7 +162,7 @@ Airbnb also maintains a [JavaScript Style Guide][airbnb-javascript]. * Do not include space inside block parameter pipes. Include one space between parameters in a block. Include one space outside block parameter pipes. - [[link](#spaces-block-params")] + [[link](#spaces-block-params)] ```ruby # bad @@ -211,7 +211,7 @@ Airbnb also maintains a [JavaScript Style Guide][airbnb-javascript]. ### Newlines -* Add a new line after `if` conditions span +* Add a new line after `if` conditions spanning multiple lines to help differentiate between the conditions and the body. [[link](#multiline-if-newline)] @@ -292,7 +292,7 @@ Airbnb also maintains a [JavaScript Style Guide][airbnb-javascript]. ## Line Length * Keep each line of code to a readable length. Unless - you have a reason to, keep lines to fewer than 100 characters. + you have a reason not to, keep lines to fewer than 100 characters. ([rationale](./rationales.md#line-length)) [[link](#line-length)] @@ -388,7 +388,7 @@ all of the following criteria: You may use whatever format you wish. In Ruby, two popular function documentation schemes are [TomDoc](http://tomdoc.org/) and -[YARD](http://rubydoc.info/docs/yard/file/docs/GettingStarted.md). You can also +[YARD](https://rubydoc.info/docs/yard/file/docs/GettingStarted.md). You can also just write things out concisely: ```ruby @@ -514,7 +514,8 @@ Thus when you create a TODO, it is almost always your name that is given. end ``` -* Do not use default arguments. Use an options +* Do not use default positional arguments. + Use keyword arguments (if available - in Ruby 2.0 or later) or an options hash instead.[[link](#no-default-args)] ```ruby @@ -523,14 +524,18 @@ Thus when you create a TODO, it is almost always your name that is given. ... end + # good + def obliterate(things, gently: true, except: [], at: Time.now) + ... + end + # good def obliterate(things, options = {}) - default_options = { + options = { :gently => true, # obliterate with soft-delete :except => [], # skip obliterating these things :at => Time.now, # don't obliterate them until later - } - options.reverse_merge!(default_options) + }.merge(options) ... end @@ -729,6 +734,35 @@ In either case: end ``` +* Avoid `unless` with comparison operators if you can use `if` with an opposing comparison operator.[[link](#unless-with-comparison-operator)] + + ```ruby + # bad + unless x == 10 + ... + end + + # good + if x != 10 + ... + end + + # bad + unless x < 10 + ... + end + + # good + if x >= 10 + ... + end + + # ok + unless x === 10 + ... + end + ``` + * Don't use parentheses around the condition of an `if/unless/while`. [[link](#parens-around-conditions)] @@ -798,6 +832,71 @@ In either case: end ``` +### Nested conditionals + +* + Avoid the use of nested conditionals for flow of control. + ([More on this][avoid-else-return-early].) [[link](#no-nested-conditionals)] + + Prefer a guard clause when you can assert invalid data. A guard clause + is a conditional statement at the top of a function that returns as soon + as it can. + + The general principles boil down to: + * Return immediately once you know your function cannot do anything more. + * Reduce nesting and indentation in the code by returning early. This makes + the code easier to read and requires less mental bookkeeping on the part + of the reader to keep track of `else` branches. + * The core or most important flows should be the least indented. + + ```ruby + # bad + def compute + server = find_server + if server + client = server.client + if client + request = client.make_request + if request + process_request(request) + end + end + end + end + + # good + def compute + server = find_server + return unless server + client = server.client + return unless client + request = client.make_request + return unless request + process_request(request) + end + ``` + + Prefer `next` in loops instead of conditional blocks. + + ```ruby + # bad + [0, 1, 2, 3].each do |item| + if item > 1 + puts item + end + end + + # good + [0, 1, 2, 3].each do |item| + next unless item > 1 + puts item + end + ``` + + See also the section "Guard Clause", p68-70 in Beck, Kent. + *Implementation Patterns*. Upper Saddle River: Addison-Wesley, 2008, which + has inspired some of the content above. + ## Syntax * Never use `for`, unless you know exactly why. Most of the @@ -1025,6 +1124,56 @@ In either case: an attribute when `self` is an ActiveRecord model: `self.guest = user`. 3. Referencing the current instance's class: `self.class`. +* When defining an object of any mutable + type meant to be a constant, make sure to call `freeze` on it. Common + examples are strings, arrays, and hashes. + ([More on this][ruby-freeze].)[[link](#freeze-constants)] + + The reason is that Ruby constants are actually mutable. Calling `freeze` + ensures they are not mutated and are therefore truly constant and + attempting to modify them will raise an exception. For strings, this allows + older versions of Ruby below 2.2 to intern them. + + ```ruby + # bad + class Color + RED = 'red' + BLUE = 'blue' + GREEN = 'green' + + ALL_COLORS = [ + RED, + BLUE, + GREEN, + ] + + COLOR_TO_RGB = { + RED => 0xFF0000, + BLUE => 0x0000FF, + GREEN => 0x00FF00, + } + end + + # good + class Color + RED = 'red'.freeze + BLUE = 'blue'.freeze + GREEN = 'green'.freeze + + ALL_COLORS = [ + RED, + BLUE, + GREEN, + ].freeze + + COLOR_TO_RGB = { + RED => 0xFF0000, + BLUE => 0x0000FF, + GREEN => 0x00FF00, + }.freeze + end + ``` + ## Naming * Use `snake_case` for methods and variables. @@ -1051,9 +1200,8 @@ In either case: [[link](#throwaway-variables)] ```ruby - payment, _ = Payment.complete_paypal_payment!(params[:token], - native_currency, - created_at) + version = '3.2.1' + major_version, minor_version, _ = version.split('.') ``` ## Classes @@ -1315,7 +1463,7 @@ In either case: * Use `Hash#key?` instead of `Hash#has_key?` and `Hash#value?` instead of `Hash#has_value?`. According to Matz, the longer forms are considered deprecated. - [[link](#deprecated-hash-methods") + [[link](#deprecated-hash-methods)] ```ruby # bad @@ -1396,11 +1544,11 @@ In either case: ```ruby # good and also fast - html = '' - html << '

Page title

' + story = '' + story << 'The Ugly Duckling' paragraphs.each do |paragraph| - html << "

#{paragraph}

" + story << paragraph end ``` @@ -1492,19 +1640,19 @@ In either case: ```ruby # bad - no interpolation needed - %(
Some text
) - # should be '
Some text
' + %(Welcome, Jane!) + # should be 'Welcome, Jane!' # bad - no double-quotes %(This is #{quality} style) # should be "This is #{quality} style" # bad - multiple lines - %(
\n#{exclamation}\n
) + %(Welcome, Jane!\nPlease enjoy your stay at #{location}\nCheers!) # should be a heredoc. # good - requires interpolation, has quotes, single line - %(#{name}) + %(Welcome, #{name}!) ``` * Use `%r` only for regular expressions matching *more @@ -1591,12 +1739,14 @@ In either case: —[Google C++ Style Guide][google-c++] [airbnb-javascript]: https://github.com/airbnb/javascript -[bbatsov-ruby]: https://github.com/bbatsov/ruby-style-guide +[rubocop-guide]: https://github.com/rubocop-hq/ruby-style-guide [github-ruby]: https://github.com/styleguide/ruby [google-c++]: https://google.github.io/styleguide/cppguide.html [google-c++-comments]: https://google.github.io/styleguide/cppguide.html#Comments [google-python-comments]: https://google.github.io/styleguide/pyguide.html#Comments -[ruby-naming-bang]: http://dablog.rubypal.com/2007/8/15/bang-methods-or-danger-will-rubyist +[ruby-naming-bang]: https://davidablack.net/dablog.html#2007/8/15/bang-methods-or-danger-will-rubyist +[ruby-freeze]: https://blog.honeybadger.io/when-to-use-freeze-and-frozen-in-ruby/ +[avoid-else-return-early]: http://blog.timoxley.com/post/47041269194/avoid-else-return-early ## Translation diff --git a/rubocop-airbnb/.rspec b/rubocop-airbnb/.rspec new file mode 100644 index 0000000..83e16f8 --- /dev/null +++ b/rubocop-airbnb/.rspec @@ -0,0 +1,2 @@ +--color +--require spec_helper diff --git a/rubocop-airbnb/.rubocop.yml b/rubocop-airbnb/.rubocop.yml new file mode 100644 index 0000000..e8c37e3 --- /dev/null +++ b/rubocop-airbnb/.rubocop.yml @@ -0,0 +1,11 @@ +inherit_from: + - .rubocop_airbnb.yml + +plugins: + - rubocop-airbnb + +AllCops: + CacheRootDirectory: tmp/rubocop + +Rails: + Enabled: false diff --git a/rubocop-airbnb/.rubocop_airbnb.yml b/rubocop-airbnb/.rubocop_airbnb.yml new file mode 100644 index 0000000..61c0084 --- /dev/null +++ b/rubocop-airbnb/.rubocop_airbnb.yml @@ -0,0 +1,8 @@ +# Default rules. These will be overridden by modified rules in +# `.rubocop_todo.yml`. Eventually, both this and `.rubocop_todo.yml` should be +# merged and moved "upstairs" into `.rubocop.yml`, but due to RuboCop's +# inheritance model we'll be doing this for now. + +# For the rubocop-airbnb gem specifically, we want to load the local configuration. +require: + - ./lib/rubocop-airbnb.rb diff --git a/rubocop-airbnb/CHANGELOG.md b/rubocop-airbnb/CHANGELOG.md new file mode 100644 index 0000000..3dbceef --- /dev/null +++ b/rubocop-airbnb/CHANGELOG.md @@ -0,0 +1,86 @@ +# Unreleased + +# 8.1.0 +* [#216](https://github.com/airbnb/ruby/pull/216) handle renamed cop (thanks @santiagorodriguez96!) + * Fix warnings after `Naming/PredicateName` was renamed to `Naming/PredicatePrefix` in `rubocop` `v1.76.0` + * Bump `rubocop` minimum required version from `~> 1.72` to `~> 1.76` + +# 8.0.0 +* [#72](https://github.com/airbnb/ruby/pull/212) Adopt Rubocop's plugin system (thanks @koic!) +* Bump minimum gem versions: + * `rubocop` from `'~> 1.61'` to `'~> 1.72'` + * `rubocop-performance` from `'~> 1.20'` to `'~> 1.24'` + * `rubocop-rails` from `'~> 2.24'` to `'~> 2.30'` + * `rubocop-rspec` from `'~> 2.26'` to `'~> 3.5'` +* Add explicit `rubocop-*` gem dependencies which have been extracted + * `rubocop-capybara` with min version `'~> 2.22'` + * `rubocop-factory_bot` with min version `'~> 2.27'` + +# 7.0.0 +* Add support for Ruby 3.3 +* Drop support for Ruby 2.6 +* Update rubocop to ~> 1.61 + +# 6.0.0 +* Recover code analysis using `TargetRubyVersion` from Ruby 2.0 to 2.4 +* Drop support for Ruby 2.5 +* Update rubocop to ~> 1.32.0 + +# 5.0.0 +* Add support for Ruby 3.1 +* Drop support for Ruby 2.4 +* Update rubocop to 1.22.0 +* Update rubocop-rspec to 2.0.0 +* Remove deprecated cops InvalidPredicateMatcher and CustomIncludeMethods + +# 4.0.0 +* Add support for Ruby 3.0 +* Run CI against Ruby 2.7 +* Drop support for Ruby 2.3 +* Update rubocop to 0.93.1 +* Update rubocop-performance to 1.10.2 +* Update rubocop-rails to 2.9.1 +* Update rubocop-rspec to 1.44.1 +* Disable Style/BracesAroundHashParameters +* Set `DisabledByDefault: true` to disable any new rubocop cops that have not yet been evaluated for this style guide + +# 3.0.2 +* Moves `require`s for `rubocop-performance` and `rubocop-rails` to library code for better transitivity. + +# 3.0.1 +* Update supported ruby versions in gemspec + +# 3.0.0 +* Update to rubocop 0.76 +* Enable Rails/IgnoredSkipActionFilterOption +* Enable Rails/ReflectionClassName +* Disable and delete Airbnb/ClassName +* Enable Layout/IndentFirstParameter +* Drop support for Ruby 2.2 + +# 2.0.0 +* Upgrade to rubocop-rspec 1.30.0, use ~> to allow for PATCH version flexibility +* Upgrade to rubocop 0.58.0, use ~> to allow for PATCH version flexibility +* Enable RSpec/HooksBeforeExamples +* Enable RSpec/MissingExampleGroupArgument +* Enable RSpec/ReceiveNever +* Remove FactoryBot/DynamicAttributeDefinedStatically +* Remove FactoryBot/StaticAttributeDefinedDynamically + +# 1.5.0 +* Upgrade to rubocop-rspec 1.27.0 +* Enable RSpec/Be +* Enable RSpec/EmptyLineAfterExampleGroup +* Enable RSpec/EmptyLineAfterHook +* Enable RSpec/SharedExamples +* Enable FactoryBot/CreateList + +# 1.4.0 +* Upgrade to rubocop 0.57.2 + +# 1.3.0 +* Upgrade to rubocop 0.54.0 +* Add SimpleUnless cop + +# 1.0.0 +* First public release of rubocop-airbnb diff --git a/rubocop-airbnb/Gemfile b/rubocop-airbnb/Gemfile new file mode 100644 index 0000000..f2e1a71 --- /dev/null +++ b/rubocop-airbnb/Gemfile @@ -0,0 +1,7 @@ +source 'https://rubygems.org' + +gemspec + +group :test do + gem 'pry' +end diff --git a/rubocop-airbnb/LICENSE.md b/rubocop-airbnb/LICENSE.md new file mode 100644 index 0000000..c81c08d --- /dev/null +++ b/rubocop-airbnb/LICENSE.md @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2012 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/rubocop-airbnb/README.md b/rubocop-airbnb/README.md new file mode 100644 index 0000000..c579f3c --- /dev/null +++ b/rubocop-airbnb/README.md @@ -0,0 +1,75 @@ +# RuboCop Airbnb + +Airbnb specific analysis for [RuboCop](https://github.com/rubocop-hq/rubocop). + +It contains Airbnb's internally used configuration for +[RuboCop](https://github.com/rubocop-hq/rubocop) and +[RuboCop RSpec](https://github.com/backus/rubocop-rspec). It also includes a handful custom rules +that are not currently addressed by other projects. + +## Installation + +Just put this in your `Gemfile` it depends on the appropriate version of rubocop and rubocop-rspec. + +``` +gem 'rubocop-airbnb' +``` + +Note: If you want to run with Ruby 2.2 you will need to set your version to 2 +``` +gem 'rubocop-airbnb', '~> 2' +``` + +## Usage + +You need to tell RuboCop to load the Airbnb extension. There are three +ways to do this: + +### RuboCop configuration file +First Create a new file `.rubocop_airbnb.yml` in the same directory as your `.rubocop.yml` +this file should contain +``` +plugins: + - rubocop-airbnb +``` + +Next add the following to `.rubocop.yml` +or add before `.rubocop_todo.yml` in your existing `inherit_from` + +``` +inherit_from: + - .rubocop_airbnb.yml + - .rubocop_todo.yml +``` + +You need to inherit `.rubocop_airbnb.yml` from another file because of Rubocop order of operations. +It runs `inherit_from` before `require` commands. If the configuration is not in a separate file +you could potentially experience a bunch of warnings from `.rubocop_todo.yml` for non-existant +`Airbnb` rules. + +Now you can run `rubocop` and it will automatically load the RuboCop Airbnb +cops together with the standard cops. + +> [!NOTE] +> The plugin system is supported in RuboCop 1.72+. In earlier versions, use `require` instead of `plugins`. + +### Command line + +```bash +rubocop --plugin rubocop-airbnb +``` + +## The Cops + +All cops are located under +[`lib/rubocop/cop/airbnb`](lib/rubocop/cop/airbnb), and contain +examples/documentation. + +In your `.rubocop.yml`, you may treat the Airbnb cops just like any other +cop. For example: + +```yaml +Airbnb/PhraseBundleKeys: + Exclude: + - spec/my_poorly_named_spec_file.rb +``` diff --git a/rubocop-airbnb/config/default.yml b/rubocop-airbnb/config/default.yml new file mode 100644 index 0000000..7c13102 --- /dev/null +++ b/rubocop-airbnb/config/default.yml @@ -0,0 +1,51 @@ +Rails: + Enabled: true + +############## +# Global rules + +AllCops: + Exclude: + - '.chefrepo/**/*' + - '.vagrant/**/*' + - '.git/**/*' + - 'node_modules/**/*' + - 'tungsten/**/*' + - 'tungsten-support/**/*' + - 'vendor/**/*' + - Vagrantfile + - Guardfile + + # While Rubocop has released a bunch of new cops, not all of these cops have been evaluated as + # part of this styleguide. To prevent new, unevaluated cops from imposing on this styleguide, we + # are marking these new cops as disabled. Note that as a consumer of this styleguide, you can + # always override any choices here by setting `Enabled: true` on any cops that you would like to + # have be enabled, even if we have explicitly disabled them (or if they are new and we have yet + # to evaluate them). For more on this configuration parameter, see + # https://github.com/rubocop/rubocop/blob/1e55b1aa5e4c5eaeccad5d61f08b7930ed6bc341/config/default.yml#L89-L101 + DisabledByDefault: true + +RSpec: + Include: + - _spec.rb + - "(?:^|/)spec/" +FactoryBot: + Include: + - spec/factories/**/*.rb + - features/support/factories/**/*.rb + +inherit_from: + - './rubocop-airbnb.yml' + - './rubocop-bundler.yml' + - './rubocop-capybara.yml' + - './rubocop-factory_bot.yml' + - './rubocop-gemspec.yml' + - './rubocop-layout.yml' + - './rubocop-lint.yml' + - './rubocop-metrics.yml' + - './rubocop-naming.yml' + - './rubocop-performance.yml' + - './rubocop-rails.yml' + - './rubocop-rspec.yml' + - './rubocop-security.yml' + - './rubocop-style.yml' diff --git a/rubocop-airbnb/config/rubocop-airbnb.yml b/rubocop-airbnb/config/rubocop-airbnb.yml new file mode 100644 index 0000000..dbe7170 --- /dev/null +++ b/rubocop-airbnb/config/rubocop-airbnb.yml @@ -0,0 +1,95 @@ +# All of these rules are implemented in this gem. +# They are custom built for use inside Airbnb and address issues that we have experienced in +# testing and production. + +Airbnb/ClassOrModuleDeclaredInWrongFile: + Description: Declare a class or module in the file that matches its namespace and name. + Enabled: true + Include: + - 'app/**/*' + - 'lib/**/*' + +Airbnb/ConstAssignedInWrongFile: + Description: Assign a const in a file that matches the namespace in which it is scoped. + Enabled: true + Include: + - 'app/**/*' + - 'lib/**/*' + +Airbnb/ContinuationSlash: + Description: Slash continuation should be reserved for string continatuion. Many times it is + used to get around other existing rules. + Enabled: true + +Airbnb/DefaultScope: + Description: Avoid `default_scope`. Default scopes make it difficult to + refactor data access patterns since the scope becomes part of every query unless + explicitly excluded, even when it is unnecessary or incidental to the desired logic. + Enabled: true + +Airbnb/FactoryAttrReferencesClass: + Description: Cop to enforce "attr { CONST }" instead of "attr CONST" in factories, + because the latter forces autoload, which slows down spec startup time and + Zeus reload time after touching a model. + Enabled: true + +Airbnb/FactoryClassUseString: + Description: Cop to tell developers to use :class => "MyClass" instead of :class => MyClass, + because the latter slows down reloading zeus. + Enabled: true + +Airbnb/MassAssignmentAccessibleModifier: + Description: Do no override and objects mass assignment restrictions. + Enabled: true + +Airbnb/ModuleMethodInWrongFile: + Description: Define a module method in the file that defines the module, not a file that + happens to use the module as a namespace. + Enabled: true + Include: + - 'app/**/*' + - 'lib/**/*' + +Airbnb/NoTimeout: + Description: Do not use Timeout.timeout. In combination with Rails autoloading, + timeout can cause Segmentation Faults in version of Ruby we use. + It can also cause logic errors since it can raise in + any callee scope. Use client library timeouts and monitoring to + ensure proper timing behavior for web requests. + Enabled: true + +Airbnb/OptArgParameters: + Description: Do not use default arguments. Use an options hash instead. + Enabled: true + StyleGuide: https://github.com/airbnb/ruby#no-default-args + +Airbnb/PhraseBundleKeys: + Description: Checks for phrase bundle keys that do not match their respective translation keys. + Enabled: true + +Airbnb/RiskyActiverecordInvocation: + Description: Disallow ActiveRecord calls that use interpolated or added strings as arguments. + Enabled: true + +Airbnb/RspecEnvironmentModification: + Description: Disallow Rails.env from being stubbed in half measure ways or from being reassigned + Use `stub_env` instead. + Enabled: true + +Airbnb/SimpleModifierConditional: + Enabled: true + StyleGuide: https://github.com/airbnb/ruby#only-simple-if-unless + +Airbnb/SimpleUnless: + Enabled: true + StyleGuide: https://github.com/airbnb/ruby#unless-with-multiple-conditions + +Airbnb/SpecConstantAssignment: + Description: Constant assignment in specs polute global namespace and are a cause of spurious + specs. They should be avoided. Also modifing existing constants is bad too, use stub_const + instead + Enabled: true + +Airbnb/UnsafeYamlMarshal: + Description: Avoid use of YAML/Marshal methods that can trigger RCE on untrusted input. + Enabled: true diff --git a/rubocop-airbnb/config/rubocop-bundler.yml b/rubocop-airbnb/config/rubocop-bundler.yml new file mode 100644 index 0000000..962f832 --- /dev/null +++ b/rubocop-airbnb/config/rubocop-bundler.yml @@ -0,0 +1,8 @@ +Bundler/DuplicatedGem: + Enabled: true + +Bundler/InsecureProtocolSource: + Enabled: false + +Bundler/OrderedGems: + Enabled: false diff --git a/rubocop-airbnb/config/rubocop-capybara.yml b/rubocop-airbnb/config/rubocop-capybara.yml new file mode 100644 index 0000000..208feba --- /dev/null +++ b/rubocop-airbnb/config/rubocop-capybara.yml @@ -0,0 +1,6 @@ +plugins: + - rubocop-capybara + +Capybara/CurrentPathExpectation: + Description: Checks that no expectations are set on Capybara's `current_path`. + Enabled: false diff --git a/rubocop-airbnb/config/rubocop-factory_bot.yml b/rubocop-airbnb/config/rubocop-factory_bot.yml new file mode 100644 index 0000000..41a4a7b --- /dev/null +++ b/rubocop-airbnb/config/rubocop-factory_bot.yml @@ -0,0 +1,10 @@ +plugins: + - rubocop-factory_bot + +FactoryBot/AttributeDefinedStatically: + Description: Always declare attribute values as blocks. + Enabled: false + +FactoryBot/CreateList: + Description: Checks for create_list usage. + Enabled: true diff --git a/rubocop-airbnb/config/rubocop-gemspec.yml b/rubocop-airbnb/config/rubocop-gemspec.yml new file mode 100644 index 0000000..17cb6d8 --- /dev/null +++ b/rubocop-airbnb/config/rubocop-gemspec.yml @@ -0,0 +1,12 @@ +Gemspec/OrderedDependencies: + Enabled: false + +Gemspec/RequiredRubyVersion: + Description: Checks that `required_ruby_version` of gemspec and `TargetRubyVersion` of + .rubocop.yml are equal. + Enabled: false + Include: + - '**/*.gemspec' + +Gemspec/RubyVersionGlobalsUsage: + Enabled: true diff --git a/rubocop-airbnb/config/rubocop-layout.yml b/rubocop-airbnb/config/rubocop-layout.yml new file mode 100644 index 0000000..9cf7789 --- /dev/null +++ b/rubocop-airbnb/config/rubocop-layout.yml @@ -0,0 +1,561 @@ +# Type 'Style' (166): +# Supports --auto-correct +Layout/AccessModifierIndentation: + Description: Check indentation of private/protected visibility modifiers. + # Airbnb: https://github.com/airbnb/ruby#access-modifiers + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#indent-public-private-protected + Enabled: true + EnforcedStyle: indent + SupportedStyles: + - outdent + - indent + +# Supports --auto-correct +Layout/ArrayAlignment: + Description: Align the elements of an array literal if they span more than one line. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#align-multiline-arrays + Enabled: true + +# Supports --auto-correct +Layout/HashAlignment: + Description: Align the elements of a hash literal if they span more than one line. + Enabled: true + EnforcedHashRocketStyle: key + EnforcedColonStyle: key + EnforcedLastArgumentHashStyle: always_inspect + SupportedLastArgumentHashStyles: + - always_inspect + - always_ignore + - ignore_implicit + - ignore_explicit + +# Supports --auto-correct +Layout/ParameterAlignment: + Description: Align the parameters of a method call if they span more than one line. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-double-indent + Enabled: true + EnforcedStyle: with_first_parameter + SupportedStyles: + - with_first_parameter + - with_fixed_indentation + +# Supports --auto-correct +Layout/BlockAlignment: + Description: Align block ends correctly. + Enabled: true + +# Supports --auto-correct +Layout/BlockEndNewline: + Description: Put end statement of multiline block on its own line. + Enabled: true + +# Supports --auto-correct +Layout/CaseIndentation: + Description: Indentation of when in a case/when/[else/]end. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#indent-when-to-case + Enabled: true + EnforcedStyle: case + IndentOneStep: false + +Layout/ClassStructure: + Enabled: false + Categories: + module_inclusion: + - include + - prepend + - extend + ExpectedOrder: + - module_inclusion + - constants + - public_class_methods + - initializer + - instance_methods + - protected_methods + - private_methods + +Layout/ClosingHeredocIndentation: + Enabled: false + +# Supports --auto-correct +Layout/ClosingParenthesisIndentation: + Description: Checks the indentation of hanging closing parentheses. + Enabled: true + +# Supports --auto-correct +Layout/CommentIndentation: + Description: Indentation of comments. + Enabled: true + +Layout/ConditionPosition: + Description: Checks for condition placed in a confusing position relative to the keyword. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#same-line-condition + Enabled: true + +# Supports --auto-correct +Layout/DefEndAlignment: + Description: Align ends corresponding to defs correctly. + Enabled: true + EnforcedStyleAlignWith: start_of_line + AutoCorrect: false + +# Use trailing commas, because there are safer in ruby. +Layout/DotPosition: + Enabled: true + EnforcedStyle: trailing + +# Supports --auto-correct +Layout/ElseAlignment: + Description: Align elses and elsifs correctly. + Enabled: true + +Layout/EmptyComment: + Description: 'Checks empty comment.' + Enabled: true + +Layout/EmptyLineAfterMagicComment: + Enabled: true + +# Supports --auto-correct +Layout/EmptyLineBetweenDefs: + Description: Use empty lines between defs. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#empty-lines-between-methods + Enabled: true + AllowAdjacentOneLineDefs: false + +# Supports --auto-correct +Layout/EmptyLines: + Description: Don't use several empty lines in a row. + Enabled: true + +Layout/EmptyLineAfterGuardClause: + Enabled: false + +# Supports --auto-correct +Layout/EmptyLinesAroundAccessModifier: + Description: Keep blank lines around access modifiers. + Enabled: true + +Layout/EmptyLinesAroundArguments: + Description: "Keeps track of empty lines around method arguments." + Enabled: false + +Layout/EmptyLinesAroundBeginBody: + Enabled: true + +# Supports --auto-correct +Layout/EmptyLinesAroundBlockBody: + Description: Keeps track of empty lines around block bodies. + Enabled: true + EnforcedStyle: no_empty_lines + SupportedStyles: + - empty_lines + - no_empty_lines + +# Supports --auto-correct +Layout/EmptyLinesAroundClassBody: + Description: Keeps track of empty lines around class bodies. + Enabled: true + EnforcedStyle: no_empty_lines + SupportedStyles: + - beginning_only + - empty_lines + - end_only + - no_empty_lines + +Layout/EmptyLinesAroundExceptionHandlingKeywords: + Enabled: false + +# Supports --auto-correct +Layout/EmptyLinesAroundMethodBody: + Description: Keeps track of empty lines around method bodies. + Enabled: true + +# Supports --auto-correct +Layout/EmptyLinesAroundModuleBody: + Description: Keeps track of empty lines around module bodies. + Enabled: true + EnforcedStyle: no_empty_lines + SupportedStyles: + - empty_lines + - no_empty_lines + +# Supports --auto-correct +Layout/EndAlignment: + Description: Align ends correctly. + # The value `keyword` means that `end` should be aligned with the matching + # keyword (if, while, etc.). + # The value `variable` means that in assignments, `end` should be aligned + # with the start of the variable on the left hand side of `=`. In all other + # situations, `end` should still be aligned with the keyword. + # The value `start_of_line` means that `end` should be aligned with the start + # of the line which the matching keyword appears on. + Enabled: true + EnforcedStyleAlignWith: keyword + AutoCorrect: false + +Layout/EndOfLine: + Description: Use Unix-style line endings. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#crlf + Enabled: false + +# Supports --auto-correct +Layout/ExtraSpacing: + Description: Do not use unnecessary spacing. + Enabled: true + AllowForAlignment: true + +# Supports --auto-correct +Layout/FirstArrayElementLineBreak: + Description: Checks for a line break before the first element in a multi-line array. + Enabled: true + +# Supports --auto-correct +Layout/FirstHashElementLineBreak: + Description: Checks for a line break before the first element in a multi-line hash. + Enabled: true + +# Supports --auto-correct +Layout/FirstMethodArgumentLineBreak: + Description: Checks for a line break before the first argument in a multi-line method + call. + Enabled: false + +# Supports --auto-correct +Layout/FirstMethodParameterLineBreak: + Description: Checks for a line break before the first parameter in a multi-line method + parameter definition. + Enabled: false + +# Supports --auto-correct +Layout/FirstArgumentIndentation: + Description: Checks the indentation of the first parameter in a method call. + Enabled: true + EnforcedStyle: consistent + SupportedStyles: + - consistent + - consistent_relative_to_receiver + - special_for_inner_method_call + - special_for_inner_method_call_in_parentheses + +# Supports --auto-correct +Layout/FirstArrayElementIndentation: + Description: Checks the indentation of the first element in an array literal. + Enabled: true + EnforcedStyle: consistent + +# Supports --auto-correct +Layout/AssignmentIndentation: + Description: Checks the indentation of the first line of the right-hand-side of a + multi-line assignment. + Enabled: true + +# Supports --auto-correct +Layout/FirstHashElementIndentation: + Description: Checks the indentation of the first key in a hash literal. + Enabled: true + EnforcedStyle: consistent + SupportedStyles: + - special_inside_parentheses + - consistent + +Layout/HeredocIndentation: + Enabled: false + +# Supports --auto-correct +Layout/IndentationConsistency: + Description: Keep indentation straight. + Enabled: true + EnforcedStyle: normal + SupportedStyles: + - normal + - indented_internal_methods + +# Supports --auto-correct +Layout/IndentationWidth: + Description: Use 2 spaces for indentation. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#spaces-indentation + Enabled: true + Width: 2 + +# Supports --auto-correct +Layout/InitialIndentation: + Description: Checks the indentation of the first non-blank non-comment line in a file. + Enabled: true + +Layout/LeadingEmptyLines: + Enabled: true + +# Supports --auto-correct +Layout/LeadingCommentSpace: + Description: Comments should start with a space. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#hash-space + Enabled: true + +# Supports --auto-correct +Layout/MultilineArrayBraceLayout: + Description: Checks that the closing brace in an array literal is symmetrical with + respect to the opening brace and the array elements. + Enabled: true + +# Supports --auto-correct +Layout/MultilineAssignmentLayout: + Description: Check for a newline after the assignment operator in multi-line assignments. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#indent-conditional-assignment + Enabled: false + SupportedTypes: + - block + - case + - class + - if + - kwbegin + - module + EnforcedStyle: new_line + SupportedStyles: + - same_line + - new_line + +# Supports --auto-correct +Layout/MultilineBlockLayout: + Description: Ensures newlines after multiline block do statements. + Enabled: true + +Layout/MultilineHashBraceLayout: + Description: >- + Checks that the closing brace in a hash literal is + symmetrical with respect to the opening brace and the + hash elements. + Enabled: true + +Layout/MultilineMethodCallBraceLayout: + Description: >- + Checks that the closing brace in a method call is + symmetrical with respect to the opening brace and the + method arguments. + Enabled: true + EnforcedStyle: symmetrical + +# Supports --auto-correct +Layout/MultilineMethodCallIndentation: + Description: Checks indentation of method calls with the dot operator that span more + than one line. + Enabled: true + EnforcedStyle: indented + SupportedStyles: + - aligned + - indented + +Layout/MultilineMethodDefinitionBraceLayout: + Description: >- + Checks that the closing brace in a method definition is + symmetrical with respect to the opening brace and the + method parametters. + Enabled: true + +# The default (aligned) would force multi line operations to look like this +# balance = Balance. +# where(cond). +# where(cond). +# first +Layout/MultilineOperationIndentation: + Enabled: false + EnforcedStyle: indented + +# Supports --auto-correct +Layout/RescueEnsureAlignment: + Description: Align rescues and ensures correctly. + Enabled: true + +# Supports --auto-correct +Layout/SpaceAfterColon: + Description: Use spaces after colons. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#spaces-operators + Enabled: true + +# Supports --auto-correct +Layout/SpaceAfterComma: + Description: Use spaces after commas. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#spaces-operators + Enabled: true + +# Supports --auto-correct +Layout/SpaceAfterMethodName: + Description: Do not put a space between a method name and the opening parenthesis + in a method definition. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#parens-no-spaces + Enabled: true + +# Supports --auto-correct +Layout/SpaceAfterNot: + Description: Tracks redundant space after the ! operator. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-space-bang + Enabled: true + +# Supports --auto-correct +Layout/SpaceAfterSemicolon: + Description: Use spaces after semicolons. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#spaces-operators + Enabled: true + +# Supports --auto-correct +Layout/SpaceAroundBlockParameters: + Description: Checks the spacing inside and after block parameters pipes. + Enabled: true + EnforcedStyleInsidePipes: no_space + +# Supports --auto-correct +Layout/SpaceAroundEqualsInParameterDefault: + Description: Checks that the equals signs in parameter default assignments have or + don't have surrounding space depending on configuration. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#spaces-around-equals + Enabled: true + EnforcedStyle: space + SupportedStyles: + - space + - no_space + +Layout/SpaceAroundKeyword: + Description: 'Use a space around keywords if appropriate.' + Enabled: true + +# Supports --auto-correct +Layout/SpaceAroundOperators: + Description: Use a single space around operators. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#spaces-operators + Enabled: true + AllowForAlignment: true + +# Supports --auto-correct +Layout/SpaceBeforeBlockBraces: + Description: Checks that the left block brace has or doesn't have space before it. + Enabled: true + EnforcedStyle: space + SupportedStyles: + - space + - no_space + +# Supports --auto-correct +Layout/SpaceBeforeComma: + Description: No spaces before commas. + Enabled: true + +# Supports --auto-correct +Layout/SpaceBeforeComment: + Description: Checks for missing space between code and a comment on the same line. + Enabled: true + +# Supports --auto-correct +Layout/SpaceBeforeFirstArg: + Description: Put a space between a method name and the first argument in a method + call without parentheses. + Enabled: true + +# Supports --auto-correct +Layout/SpaceBeforeSemicolon: + Description: No spaces before semicolons. + Enabled: true + +Layout/SpaceInLambdaLiteral: + Enabled: false + +Layout/SpaceInsideArrayPercentLiteral: + Enabled: false + +# Supports --auto-correct +Layout/SpaceInsideBlockBraces: + Description: Checks that block braces have or don't have surrounding space. For blocks + taking parameters, checks that the left brace has or doesn't have trailing space. + Enabled: true + EnforcedStyle: space + SupportedStyles: + - space + - no_space + EnforcedStyleForEmptyBraces: no_space + SpaceBeforeBlockParameters: true + +Layout/SpaceInsideArrayLiteralBrackets: + EnforcedStyle: no_space + SupportedStyles: + - space + - no_space + # 'compact' normally requires a space inside the brackets, with the exception + # that successive left brackets or right brackets are collapsed together + - compact + EnforcedStyleForEmptyBrackets: no_space + SupportedStylesForEmptyBrackets: + - space + - no_space + Enabled: true + +Layout/SpaceInsideReferenceBrackets: + EnforcedStyleForEmptyBrackets: no_space + SupportedStyles: + - space + - no_space + Enabled: true + +# Supports --auto-correct +Layout/SpaceInsideHashLiteralBraces: + Description: Use spaces inside hash literal braces - or don't. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#spaces-operators + Enabled: true + EnforcedStyle: space + EnforcedStyleForEmptyBraces: no_space + SupportedStyles: + - space + - no_space + +# Supports --auto-correct +Layout/SpaceInsideParens: + Description: No spaces after ( or before ). + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-spaces-braces + Enabled: true + +Layout/SpaceInsidePercentLiteralDelimiters: + Description: 'No unnecessary spaces inside delimiters of %i/%w/%x literals.' + Enabled: true + +# Supports --auto-correct +Layout/SpaceInsideRangeLiteral: + Description: No spaces inside range literals. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-space-inside-range-literals + Enabled: true + +# Supports --auto-correct +Layout/SpaceInsideStringInterpolation: + Description: Checks for padding/surrounding spaces inside string interpolation. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#string-interpolation + Enabled: true + EnforcedStyle: no_space + SupportedStyles: + - space + - no_space + +# Supports --auto-correct +Layout/IndentationStyle: + Description: No hard tabs. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#spaces-indentation + Enabled: true + +# Supports --auto-correct +Layout/TrailingEmptyLines: + Description: Checks trailing blank lines and final newline. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#newline-eof + Enabled: true + EnforcedStyle: final_newline + SupportedStyles: + - final_newline + - final_blank_line + +# Supports --auto-correct +Layout/TrailingWhitespace: + Description: Avoid trailing whitespace. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-trailing-whitespace + Enabled: true + +Layout/FirstParameterIndentation: + Enabled: true + +# Supports --auto-correct +Layout/LineLength: + Max: 100 + AllowURI: true diff --git a/rubocop-airbnb/config/rubocop-lint.yml b/rubocop-airbnb/config/rubocop-lint.yml new file mode 100644 index 0000000..d23e12a --- /dev/null +++ b/rubocop-airbnb/config/rubocop-lint.yml @@ -0,0 +1,305 @@ +Lint/AmbiguousBlockAssociation: + Enabled: false + +Lint/AmbiguousOperator: + Description: Checks for ambiguous operators in the first argument of a method invocation + without parentheses. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#parens-as-args + Enabled: true + +Lint/AmbiguousRegexpLiteral: + Description: Checks for ambiguous regexp literals in the first argument of a method + invocation without parenthesis. + Enabled: false + +Lint/AssignmentInCondition: + Description: Don't use assignment in conditions. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#safe-assignment-in-condition + Enabled: true + AllowSafeAssignment: false + +Lint/BigDecimalNew: + Description: '`BigDecimal.new()` is deprecated. Use `BigDecimal()` instead.' + Enabled: false + +Lint/BooleanSymbol: + Enabled: false + +Lint/CircularArgumentReference: + Description: Don't refer to the keyword argument in the default value. + Enabled: false + +Lint/Debugger: + Description: Check for debugger calls. + Enabled: true + +# Supports --auto-correct +Lint/DeprecatedClassMethods: + Description: Check for deprecated class method calls. + Enabled: false + +Lint/DuplicateCaseCondition: + Enabled: false + +Lint/DuplicateMethods: + Description: Check for duplicate methods calls. + Enabled: true + +Lint/DuplicateHashKey: + Description: Check for duplicate keys in hash literals. + Enabled: true + +Lint/EachWithObjectArgument: + Description: Check for immutable argument given to each_with_object. + Enabled: false + +Lint/ElseLayout: + Description: Check for odd code arrangement in an else block. + Enabled: true + +Lint/EmptyEnsure: + Description: Checks for empty ensure block. + Enabled: false + +Lint/EmptyInterpolation: + Description: Checks for empty string interpolation. + Enabled: true + +Lint/EmptyWhen: + Enabled: false + +Lint/EnsureReturn: + Description: Do not use return in an ensure block. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-return-ensure + Enabled: false + +Lint/ErbNewArguments: + Enabled: false + +Lint/FlipFlop: + Description: Checks for flip flops + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-flip-flops + Enabled: false + +Lint/FloatOutOfRange: + Description: Catches floating-point literals too large or small for Ruby to represent. + Enabled: false + +Lint/FormatParameterMismatch: + Description: The number of parameters to format/sprint must match the fields. + Enabled: true + +Lint/SuppressedException: + Description: Don't suppress exception. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#dont-hide-exceptions + Enabled: false + +Lint/ImplicitStringConcatenation: + Description: Checks for adjacent string literals on the same line, which could better + be represented as a single string literal. + Enabled: true + +Lint/IneffectiveAccessModifier: + Description: Checks for attempts to use `private` or `protected` to set the visibility + of a class method, which does not work. + Enabled: true + +Lint/InheritException: + Enabled: true + # The default base class in favour of `Exception`. + EnforcedStyle: standard_error + +Lint/InterpolationCheck: + Enabled: false + +Lint/LiteralAsCondition: + Description: Checks of literals used in conditions. + Enabled: false + +# Supports --auto-correct +Lint/LiteralInInterpolation: + Description: Avoid interpolating literals in strings + Enabled: false + AutoCorrect: false + +Lint/Loop: + Description: Use Kernel#loop with break rather than begin/end/until or begin/end/while + for post-loop tests. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#loop-with-break + Enabled: false + +Lint/MissingCopEnableDirective: + # Maximum number of consecutive lines the cop can be disabled for. + # 0 allows only single-line disables + # 1 would mean the maximum allowed is the following: + # # rubocop:disable SomeCop + # a = 1 + # # rubocop:enable SomeCop + # .inf for any size + MaximumRangeSize: .inf + Description: 'Checks for a `# rubocop:enable` after `# rubocop:disable`' + Enabled: true + +Lint/MissingSuper: + Enabled: false + +Lint/MultipleComparison: + Enabled: false + +Lint/NestedMethodDefinition: + Description: Do not use nested method definitions. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-nested-methods + Enabled: false + +Lint/NextWithoutAccumulator: + Enabled: false + +Lint/NonLocalExitFromIterator: + Description: Do not use return in iterator to cause non-local exit. + Enabled: false + +Lint/OrderedMagicComments: + Description: 'Checks the proper ordering of magic comments and whether a magic comment is not placed before a shebang.' + Enabled: true + +Lint/ParenthesesAsGroupedExpression: + Description: Checks for method calls with a space before the opening parenthesis. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#parens-no-spaces + Enabled: true + +Lint/PercentStringArray: + Description: >- + Checks for unwanted commas and quotes in %w/%W literals. + Enabled: true + +Lint/PercentSymbolArray: + Enabled: false + +Lint/RandOne: + Description: Checks for `rand(1)` calls. Such calls always return `0` and most likely + a mistake. + Enabled: true + +Lint/RedundantWithIndex: + Enabled: false + +Lint/RedundantWithObject: + Enabled: false + +Lint/RegexpAsCondition: + Enabled: false + +Lint/RequireParentheses: + Description: Use parentheses in the method call to avoid confusion about precedence. + Enabled: true + +Lint/RescueException: + Description: Avoid rescuing the Exception class. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-blind-rescues + Enabled: true + +Lint/RescueType: + Enabled: false + +Lint/ReturnInVoidContext: + Enabled: false + +Lint/SafeNavigationChain: + Enabled: false + +Lint/SafeNavigationConsistency: + Enabled: false + +Lint/ScriptPermission: + Enabled: false + +Lint/ShadowedArgument: + Description: 'Avoid reassigning arguments before they were used.' + Enabled: true + +Lint/ShadowedException: + Description: >- + Avoid rescuing a higher level exception + before a lower level exception. + Enabled: false + +Lint/ShadowingOuterLocalVariable: + Description: Do not use the same name as outer local variable for block arguments + or block local variables. + Enabled: true + +# Supports --auto-correct +Lint/RedundantStringCoercion: + Description: Checks for Object#to_s usage in string interpolation. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-to-s + Enabled: true + +Lint/UnderscorePrefixedVariableName: + Description: Do not use prefix `_` for a variable that is used. + Enabled: false + +Lint/UnifiedInteger: + Enabled: false + +Lint/RedundantCopDisableDirective: + Description: >- + Checks for rubocop:disable comments that can be removed. + Note: this cop is not disabled when disabling all cops. It must be explicitly disabled. + Enabled: true + +Lint/RedundantCopEnableDirective: + Description: Checks for rubocop:enable comments that can be removed. + Enabled: true + +Lint/RedundantRequireStatement: + Enabled: false + +Lint/RedundantSplatExpansion: + Enabled: false + +Lint/UnreachableCode: + Description: Unreachable code. + Enabled: false + +# Supports --auto-correct +Lint/UnusedBlockArgument: + Description: Checks for unused block arguments. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#underscore-unused-vars + Enabled: false + +# Supports --auto-correct +Lint/UnusedMethodArgument: + Description: Checks for unused method arguments. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#underscore-unused-vars + Enabled: false + +Lint/UriEscapeUnescape: + Enabled: false + +Lint/UriRegexp: + Enabled: false + +Lint/UselessAccessModifier: + Description: Checks for useless access modifiers. + Enabled: true + +Lint/UselessAssignment: + Description: Checks for useless assignment to a local variable. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#underscore-unused-vars + Enabled: true + +Lint/BinaryOperatorWithIdenticalOperands: + Description: Checks for comparison of something with itself. + Enabled: true + +Lint/UselessElseWithoutRescue: + Description: Checks for useless `else` in `begin..end` without `rescue`. + Enabled: true + +Lint/UselessSetterCall: + Description: Checks for useless setter call to a local variable. + Enabled: true + +Lint/Void: + Description: Possible use of operator/literal/variable in void context. + Enabled: false diff --git a/rubocop-airbnb/config/rubocop-metrics.yml b/rubocop-airbnb/config/rubocop-metrics.yml new file mode 100644 index 0000000..e99b172 --- /dev/null +++ b/rubocop-airbnb/config/rubocop-metrics.yml @@ -0,0 +1,43 @@ +# We're using cane for ABC complexity checks +Metrics/AbcSize: + Enabled: false + +Metrics/BlockLength: + Enabled: false + +Metrics/BlockNesting: + Description: Avoid excessive block nesting + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#three-is-the-number-thou-shalt-count + Enabled: false + Max: 3 + +# We are not perfect robot-humans +Metrics/ClassLength: + Enabled: false + +Metrics/CyclomaticComplexity: + Description: A complexity metric that is strongly correlated to the number of test + cases needed to validate a method. + Enabled: false + Max: 6 + +Metrics/MethodLength: + Enabled: false + +Metrics/ModuleLength: + Description: Avoid modules longer than 100 lines of code. + Enabled: false + CountComments: false + Max: 100 + +Metrics/ParameterLists: + Description: Avoid parameter lists longer than three or four parameters. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#too-many-params + Enabled: false + Max: 5 + CountKeywordArgs: true + +Metrics/PerceivedComplexity: + Description: A complexity metric geared towards measuring complexity for a human reader. + Enabled: false + Max: 7 diff --git a/rubocop-airbnb/config/rubocop-naming.yml b/rubocop-airbnb/config/rubocop-naming.yml new file mode 100644 index 0000000..f73e1e9 --- /dev/null +++ b/rubocop-airbnb/config/rubocop-naming.yml @@ -0,0 +1,88 @@ +Naming/AccessorMethodName: + Description: Check the naming of accessor methods for get_/set_. + Enabled: false + +Naming/AsciiIdentifiers: + Description: Use only ascii symbols in identifiers. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#english-identifiers + Enabled: true + +Naming/BinaryOperatorParameterName: + Description: When defining binary operators, name the argument other. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#other-arg + Enabled: false + +Naming/ClassAndModuleCamelCase: + Description: Use CamelCase for classes and modules. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#camelcase-classes + Enabled: true + +Naming/ConstantName: + Description: Constants should use SCREAMING_SNAKE_CASE. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#screaming-snake-case + Enabled: false + +Naming/FileName: + Description: Use snake_case for source file names. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#snake-case-files + Enabled: false + +Naming/HeredocDelimiterCase: + Enabled: false + +Naming/HeredocDelimiterNaming: + Enabled: false + +Naming/MemoizedInstanceVariableName: + Description: >- + Memoized method name should match memo instance variable name. + Enabled: false + +Naming/MethodName: + Description: Use the configured style when naming methods. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#snake-case-symbols-methods-vars + Enabled: false + EnforcedStyle: snake_case + SupportedStyles: + - snake_case + - camelCase + +Naming/PredicatePrefix: + Description: Check the names of predicate methods. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#bool-methods-qmark + Enabled: false + NamePrefix: + - is_ + - has_ + - have_ + ForbiddenPrefixes: + - is_ + - has_ + - have_ + +Naming/BlockParameterName: + Description: >- + Checks for block parameter names that contain capital letters, + end in numbers, or do not meet a minimal length. + Enabled: false + +Naming/MethodParameterName: + Description: >- + Checks for method parameter names that contain capital letters, + end in numbers, or do not meet a minimal length. + Enabled: false + +Naming/VariableName: + Description: Use the configured style when naming variables. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#snake-case-symbols-methods-vars + Enabled: true + EnforcedStyle: snake_case + SupportedStyles: + - snake_case + - camelCase + +Naming/VariableNumber: + Enabled: false + +Naming/RescuedExceptionsVariableName: + Enabled: false diff --git a/rubocop-airbnb/config/rubocop-performance.yml b/rubocop-airbnb/config/rubocop-performance.yml new file mode 100644 index 0000000..3dbae27 --- /dev/null +++ b/rubocop-airbnb/config/rubocop-performance.yml @@ -0,0 +1,124 @@ +plugins: + - rubocop-performance + +Performance/Caller: + Enabled: false + +# Type 'Performance' (8): +# Supports --auto-correct +Performance/CaseWhenSplat: + Description: Place `when` conditions that use splat at the end of the list of `when` + branches. + Enabled: false + +# Supports --auto-correct +Performance/Casecmp: + Description: Use `casecmp` rather than `downcase ==`. + Reference: https://github.com/JuanitoFatas/fast-ruby#stringcasecmp-vs-stringdowncase---code + Enabled: false + +Performance/CompareWithBlock: + Enabled: false + +# Supports --auto-correct +Performance/Count: + Description: Use `count` instead of `select...size`, `reject...size`, `select...count`, + `reject...count`, `select...length`, and `reject...length`. + Enabled: false + +# Supports --auto-correct +Performance/Detect: + Description: Use `detect` instead of `select.first`, `find_all.first`, `select.last`, + and `find_all.last`. + Reference: https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code + Enabled: false + +Performance/DoubleStartEndWith: + Description: Use `str.{start,end}_with?(x, ..., y, ...)` instead of `str.{start,end}_with?(x, + ...) || str.{start,end}_with?(y, ...)`. + Enabled: false + +# Supports --auto-correct +Performance/EndWith: + Description: Use `end_with?` instead of a regex match anchored to the end of a string. + Reference: https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end + Enabled: false + +Performance/FixedSize: + Description: Do not compute the size of statically sized objects except in constants + Enabled: false + +# Supports --auto-correct +Performance/FlatMap: + Description: Use `Enumerable#flat_map` instead of `Enumerable#map...Array#flatten(1)` + or `Enumberable#collect..Array#flatten(1)` + Reference: https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code + Enabled: false + EnabledForFlattenWithoutParams: false + +Performance/InefficientHashSearch: + Enabled: false + +# Supports --auto-correct +Performance/RangeInclude: + Description: Use `Range#cover?` instead of `Range#include?`. + Reference: https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code + Enabled: false + +# Supports --auto-correct +Performance/RedundantBlockCall: + Description: Use `yield` instead of `block.call`. + Reference: https://github.com/JuanitoFatas/fast-ruby#proccall-vs-yield-code + Enabled: false + +# Supports --auto-correct +Performance/RedundantMatch: + Description: Use `=~` instead of `String#match` or `Regexp#match` in a context where + the returned `MatchData` is not needed. + Enabled: false + +# Supports --auto-correct +Performance/RedundantMerge: + Description: Use Hash#[]=, rather than Hash#merge! with a single key-value pair. + Reference: https://github.com/JuanitoFatas/fast-ruby#hashmerge-vs-hash-code + Enabled: false + +Performance/RegexpMatch: + Enabled: false + +# Supports --auto-correct +Performance/ReverseEach: + Description: Use `reverse_each` instead of `reverse.each`. + Reference: https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code + Enabled: false + +# Supports --auto-correct +Performance/Size: + Description: Use `size` instead of `count` for counting the number of elements in + `Array` and `Hash`. + Reference: https://github.com/JuanitoFatas/fast-ruby#arraycount-vs-arraysize-code + Enabled: false + +# Supports --auto-correct +Performance/StartWith: + Description: Use `start_with?` instead of a regex match anchored to the beginning + of a string. + Reference: https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end + Enabled: false + +# Supports --auto-correct +Performance/StringReplacement: + Description: Use `tr` instead of `gsub` when you are replacing the same number of + characters. Use `delete` instead of `gsub` when you are deleting characters. + Reference: https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code + Enabled: false + +Performance/TimesMap: + Description: Checks for .times.map calls. + Enabled: false + +Performance/UnfreezeString: + Enabled: false + +Performance/UriDefaultParser: + Enabled: false diff --git a/rubocop-airbnb/config/rubocop-rails.yml b/rubocop-airbnb/config/rubocop-rails.yml new file mode 100644 index 0000000..ddd4980 --- /dev/null +++ b/rubocop-airbnb/config/rubocop-rails.yml @@ -0,0 +1,221 @@ +plugins: + - rubocop-rails + +# before_action doesn't seem to exist, so this doesn't make sense. +Rails/ActionFilter: + Enabled: false + +Rails/ActiveRecordAliases: + Enabled: false + +Rails/ActiveSupportAliases: + Enabled: false + +Rails/ApplicationJob: + Enabled: false + +Rails/ApplicationRecord: + Enabled: false + +Rails/AssertNot: + Enabled: false + +Rails/Blank: + Description: 'Enforce using `blank?` and `present?`.' + Enabled: true + # Convert checks for `nil` or `empty?` to `blank?` + NilOrEmpty: true + # Convert usages of not `present?` to `blank?` + NotPresent: true + # Convert usages of `unless` `present?` to `if` `blank?` + UnlessPresent: true + +Rails/BulkChangeTable: + Enabled: false + +Rails/CreateTableWithTimestamps: + Description: Checks the migration for which timestamps are not included when creating a new table. + Enabled: true + +Rails/Date: + Description: Checks the correct usage of date aware methods, such as Date.today, Date.current + etc. + Enabled: false + EnforcedStyle: flexible + SupportedStyles: + - strict + - flexible + +# Supports --auto-correct +Rails/Delegate: + Description: Prefer delegate method for delegations. + Enabled: false + +Rails/DelegateAllowBlank: + Enabled: false + +Rails/DynamicFindBy: + Enabled: false + +Rails/EnumUniqueness: + Enabled: false + +Rails/EnvironmentComparison: + Description: "Favor `Rails.env.production?` over `Rails.env == 'production'`" + Enabled: true + +Rails/Exit: + Description: >- + Favor `fail`, `break`, `return`, etc. over `exit` in + application or library code outside of Rake files to avoid + exits during unit testing or running in production. + Enabled: false + +Rails/FilePath: + Enabled: false + +# Supports --auto-correct +Rails/FindBy: + Description: Prefer find_by over where.first. + Enabled: false + Include: + - app/models/**/*.rb + +# Supports --auto-correct +Rails/FindEach: + Description: Prefer all.find_each over all.find. + Enabled: false + Include: + - app/models/**/*.rb + +Rails/HasAndBelongsToMany: + Description: Prefer has_many :through to has_and_belongs_to_many. + Enabled: false + Include: + - app/models/**/*.rb + +Rails/HasManyOrHasOneDependent: + Enabled: false + +Rails/HttpPositionalArguments: + Enabled: false + +Rails/HttpStatus: + Enabled: false + +Rails/InverseOf: + Description: 'Checks for associations where the inverse cannot be determined automatically.' + Enabled: false + +Rails/LexicallyScopedActionFilter: + Description: Checks that methods specified in the filter's `only` or `except` options are + explicitly defined in the controller. + StyleGuide: 'https://github.com/rubocop-hq/rails-style-guide#lexically-scoped-action-filter' + Enabled: false + +Rails/NotNullColumn: + Enabled: false + +Rails/Output: + Description: Checks for calls to puts, print, etc. + Enabled: false + Include: + - app/**/*.rb + - config/**/*.rb + - db/**/*.rb + - lib/**/*.rb + +Rails/OutputSafety: + Description: 'The use of `html_safe` or `raw` may be a security risk.' + Enabled: false + +# Supports --auto-correct +Rails/PluralizationGrammar: + Description: Checks for incorrect grammar when using methods like `3.day.ago`. + Enabled: false + +Rails/Presence: + Description: Checks code that can be written more easily using `Object#presence` defined by + Active Support. + Enabled: false + +Rails/Present: + Description: 'Enforce using `blank?` and `present?`.' + Enabled: true + NotNilAndNotEmpty: true + # Convert checks for not `nil` and not `empty?` to `present?` + NotBlank: true + # Convert usages of not `blank?` to `present?` + UnlessBlank: true + # Convert usages of `unless` `blank?` to `if` `present?` + +# Supports --auto-correct +Rails/ReadWriteAttribute: + Description: Checks for read_attribute(:attr) and write_attribute(:attr, val). + Enabled: false + Include: + - app/models/**/*.rb + +Rails/RedundantReceiverInWithOptions: + Description: 'Checks for redundant receiver in `with_options`.' + Enabled: true + +Rails/RefuteMethods: + Enabled: false + +Rails/RelativeDateConstant: + Enabled: false + +Rails/RequestReferer: + Description: 'Use consistent syntax for request.referer.' + Enabled: false + +Rails/ReversibleMigration: + Enabled: false + +Rails/SafeNavigation: + Enabled: false + +Rails/SaveBang: + Enabled: false + +Rails/ScopeArgs: + Description: Checks the arguments of ActiveRecord scopes. + Enabled: true + Include: + - app/models/**/*.rb + +Rails/SkipsModelValidations: + Enabled: false + +Rails/TimeZone: + Description: Checks the correct usage of time zone aware methods. + StyleGuide: https://github.com/rubocop-hq/rails-style-guide#time + Reference: http://danilenko.org/2012/7/6/rails_timezones + Enabled: false + EnforcedStyle: flexible + SupportedStyles: + - strict + - flexible + +Rails/UniqBeforePluck: + Enabled: false + +Rails/UnknownEnv: + Enabled: false + +Rails/Validation: + Description: Use validates :attribute, hash of validations. + Enabled: false + Include: + - app/models/**/*.rb + +Rails/IgnoredSkipActionFilterOption: + Enabled: true + +Rails/ReflectionClassName: + Enabled: true + +Rails/RakeEnvironment: + Description: Ensures that rake tasks depend on :environment + Enabled: false diff --git a/rubocop-airbnb/config/rubocop-rspec.yml b/rubocop-airbnb/config/rubocop-rspec.yml new file mode 100644 index 0000000..e0cc01f --- /dev/null +++ b/rubocop-airbnb/config/rubocop-rspec.yml @@ -0,0 +1,318 @@ +plugins: + - rubocop-rspec + +RSpec/AlignLeftLetBrace: + Description: Checks that left braces for adjacent single line lets are aligned. + Enabled: false + +RSpec/AlignRightLetBrace: + Description: Checks that right braces for adjacent single line lets are aligned. + Enabled: false + +RSpec/AnyInstance: + Description: 'Prefer instance doubles over stubbing any instance of a class' + Enabled: false + +RSpec/AroundBlock: + Description: Checks that around blocks actually run the test. + Enabled: true + +RSpec/Be: + Description: Check for expectations where `be` is used without argument. + Enabled: true + +RSpec/BeEql: + Description: Check for expectations where `be(...)` can replace `eql(...)`. + Enabled: false + +RSpec/BeforeAfterAll: + Description: Check that before/after(:all) isn't being used. + Enabled: true + Exclude: + - spec/spec_helper.rb + - spec/rails_helper.rb + - spec/support/**/*.rb + +RSpec/ContextWording: + Description: "`context` block descriptions should start with 'when', or 'with'." + Enabled: false + +RSpec/DescribeClass: + Description: 'Check that the first argument to the top level describe is the tested class or module.' + Enabled: false + +RSpec/DescribeMethod: + Description: 'Checks that the second argument to top level describe is the tested method name.' + Enabled: false + +RSpec/DescribeSymbol: + Description: Avoid describing symbols. + Enabled: true + +RSpec/DescribedClass: + Description: 'Use `described_class` for tested class / module' + Enabled: false + +RSpec/Dialect: + Description: Enforces custom RSpec dialects. + Enabled: false + +RSpec/EmptyExampleGroup: + Description: Checks if an example group does not include any tests. + Enabled: true + +RSpec/EmptyLineAfterExampleGroup: + Description: Checks if there is an empty line after example group blocks. + Enabled: true + +RSpec/EmptyLineAfterFinalLet: + Description: Checks if there is an empty line after the last let block. + Enabled: true + +RSpec/EmptyLineAfterHook: + Description: Checks if there is an empty line after hook blocks. + Enabled: true + +RSpec/EmptyLineAfterSubject: + Description: Checks if there is an empty line after subject block. + Enabled: true + +RSpec/ExampleLength: + Description: >- + A long example is usually more difficult to understand. + Consider extracting out some behaviour, e.g. with a `let` block, or a helper method. + Enabled: false + +RSpec/ExampleWording: + Description: 'Do not use should when describing your tests.' + Enabled: true + CustomTransform: + be: is + have: has + not: does not + NOT: does not + automatically: automatically + correctly: correctly + successfully: successfully + only: only + properly: properly + response: responds + be redirect: redirects + IgnoredWords: [] + +RSpec/ExpectActual: + Description: Checks for `expect(...)` calls containing literal values. + Enabled: true + Exclude: + - spec/routing/**/* + +RSpec/ExpectInHook: + Enabled: true + Description: Do not use `expect` in hooks such as `before`. + +RSpec/ExpectOutput: + Description: Checks for opportunities to use `expect { ... }.to output`. + Enabled: false + +RSpec/Focus: + Description: Checks if examples are focused. + Enabled: false + +RSpec/HookArgument: + Description: Checks the arguments passed to `before`, `around`, and `after`. + Enabled: false + EnforcedStyle: implicit + SupportedStyles: + - implicit + - each + - example + +RSpec/HooksBeforeExamples: + Description: Checks for before/around/after hooks that come after an example. + Enabled: true + +RSpec/ImplicitExpect: + Description: Check that a consistent implicit expectation style is used. + Enabled: false + EnforcedStyle: is_expected + SupportedStyles: + - is_expected + - should + +RSpec/ImplicitSubject: + Description: 'Checks for usage of implicit subject (`is_expected` / `should`).' + Enabled: false + +RSpec/InstanceSpy: + Description: Checks for `instance_double` used with `have_received`. + Enabled: false + +RSpec/InstanceVariable: + Description: 'Checks for the usage of instance variables.' + Enabled: false + +RSpec/ItBehavesLike: + Description: Checks that only one `it_behaves_like` style is used. + Enabled: false + EnforcedStyle: it_behaves_like + SupportedStyles: + - it_behaves_like + - it_should_behave_like + +RSpec/IteratedExpectation: + Description: Check that `all` matcher is used instead of iterating over an array. + Enabled: false + +RSpec/LeadingSubject: + Description: Checks for `subject` definitions that come after `let` definitions. + Enabled: true + +RSpec/LetBeforeExamples: + Description: Checks for `let` definitions that come after an example. + Enabled: true + +RSpec/LetSetup: + Description: Checks unreferenced `let!` calls being used for test setup. + Enabled: false + +RSpec/MessageChain: + Description: Check that chains of messages are not being stubbed. + Enabled: false + +RSpec/MessageExpectation: + Description: Checks for consistent message expectation style. + Enabled: false + EnforcedStyle: allow + SupportedStyles: + - allow + - expect + +RSpec/MessageSpies: + Description: Checks that message expectations are set using spies. + Enabled: false + EnforcedStyle: have_received + SupportedStyles: + - have_received + - receive + +RSpec/MissingExampleGroupArgument: + Description: Checks that the first argument to an example group is not empty. + Enabled: true + +RSpec/MultipleDescribes: + Description: 'Checks for multiple top level describes.' + Enabled: true + +RSpec/MultipleExpectations: + Description: Checks if examples contain too many `expect` calls. + Enabled: false + Max: 1 + +RSpec/MultipleSubjects: + Description: Checks if an example group defines `subject` multiple times. + Enabled: true + +RSpec/NamedSubject: + Description: Checks for explicitly referenced test subjects. + Enabled: false + +RSpec/NestedGroups: + Description: Checks for nested example groups. + Enabled: false + Max: 3 + +RSpec/ReceiveNever: + Description: 'Prefer `not_to receive(…)` over `receive(…).never`.' + Enabled: true + +RSpec/NotToNot: + Description: 'Enforces the usage of the same method on all negative message expectations.' + Enabled: true + EnforcedStyle: not_to + SupportedStyles: + - not_to + - to_not + +RSpec/OverwritingSetup: + Enabled: false + Description: Checks if there is a let/subject that overwrites an existing one. + +RSpec/Pending: + Description: Checks for any pending or skipped examples. + Enabled: false + +RSpec/PredicateMatcher: + Description: Prefer using predicate matcher over using predicate method directly. + Enabled: false + Strict: true + EnforcedStyle: inflected + SupportedStyles: + - inflected + - explicit + +RSpec/ReceiveCounts: + Description: Check for `once` and `twice` receive counts matchers usage. + Enabled: false + +RSpec/RepeatedDescription: + Description: Check for repeated description strings in example groups. + Enabled: true + +RSpec/RepeatedExample: + Enabled: true + Description: Check for repeated examples within example groups. + +RSpec/ReturnFromStub: + Enabled: true + Description: Checks for consistent style of stub's return setting. + EnforcedStyle: and_return + SupportedStyles: + - and_return + - block + +RSpec/ScatteredLet: + Description: Checks for let scattered across the example group. + Enabled: true + +RSpec/ScatteredSetup: + Description: Checks for setup scattered across multiple hooks in an example group. + Enabled: true + +RSpec/SharedContext: + Description: Checks for proper shared_context and shared_examples usage. + Enabled: false + +RSpec/SharedExamples: + Description: Enforces use of string to titleize shared examples. + Enabled: true + +RSpec/SingleArgumentMessageChain: + Description: Checks that chains of messages contain more than one element. + Enabled: true + +RSpec/SpecFilePathFormat: + Description: Checks that spec file paths are consistent and well-formed. + Enabled: false + CustomTransform: + RuboCop: rubocop + RSpec: rspec + +RSpec/SpecFilePathSuffix: + Description: Checks that spec file paths suffix are consistent and well-formed. + Enabled: false + +RSpec/SubjectStub: + Description: Checks for stubbed test subjects. + Enabled: false + +RSpec/UnspecifiedException: + Description: Checks for a specified error in checking raised errors. + Enabled: false + +RSpec/VerifiedDoubles: + Description: 'Prefer using verifying doubles over normal doubles.' + Enabled: false + +RSpec/VoidExpect: + Description: This cop checks void `expect()`. + Enabled: false diff --git a/rubocop-airbnb/config/rubocop-security.yml b/rubocop-airbnb/config/rubocop-security.yml new file mode 100644 index 0000000..6f23c7c --- /dev/null +++ b/rubocop-airbnb/config/rubocop-security.yml @@ -0,0 +1,17 @@ +Security/Eval: + Description: The use of eval represents a serious security risk. + Enabled: true + +Security/JSONLoad: + Enabled: false + +Security/MarshalLoad: + Enabled: false + +Security/Open: + Description: 'The use of Kernel#open represents a serious security risk.' + Enabled: false + +Security/YAMLLoad: + Enabled: false + diff --git a/rubocop-airbnb/config/rubocop-style.yml b/rubocop-airbnb/config/rubocop-style.yml new file mode 100644 index 0000000..d81ce16 --- /dev/null +++ b/rubocop-airbnb/config/rubocop-style.yml @@ -0,0 +1,1000 @@ +Style/AccessModifierDeclarations: + Enabled: false + +# Supports --auto-correct +Style/Alias: + Description: Use alias_method instead of alias. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#alias-method + Enabled: false + +# Supports --auto-correct +Style/AndOr: + Description: Use &&/|| instead of and/or. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-and-or-or + Enabled: true + EnforcedStyle: always + SupportedStyles: + - always + - conditionals + +# Supports --auto-correct +Style/ArrayJoin: + Description: Use Array#join instead of Array#*. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#array-join + Enabled: true + +Style/AsciiComments: + Description: Use only ascii symbols in comments. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#english-comments + Enabled: false + +# Supports --auto-correct +Style/Attr: + Description: Checks for uses of Module#attr. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#attr + Enabled: false + +Style/AutoResourceCleanup: + Description: Suggests the usage of an auto resource cleanup version of a method (if + available). + Enabled: false + +# Supports --auto-correct +Style/BarePercentLiterals: + Description: Checks if usage of %() or %Q() matches configuration. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#percent-q-shorthand + Enabled: false + EnforcedStyle: bare_percent + SupportedStyles: + - percent_q + - bare_percent + +Style/BeginBlock: + Description: Avoid the use of BEGIN blocks. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-BEGIN-blocks + Enabled: false + +# Supports --auto-correct +Style/BlockComments: + Description: Do not use block comments. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-block-comments + Enabled: true + +# Supports --auto-correct +Style/BlockDelimiters: + Description: Check for uses of braces or do/end around single line or multi-line blocks. + Enabled: true + EnforcedStyle: line_count_based + SupportedStyles: + # The `line_count_based` style enforces braces around single line blocks and + # do..end around multi-line blocks. + - line_count_based + # The `semantic` style enforces braces around functional blocks, where the + # primary purpose of the block is to return a value and do..end for + # procedural blocks, where the primary purpose of the block is its + # side-effects. + # + # This looks at the usage of a block's method to determine its type (e.g. is + # the result of a `map` assigned to a variable or passed to another + # method) but exceptions are permitted in the `ProceduralMethods`, + # `FunctionalMethods` and `IgnoredMethods` sections below. + - semantic + # The `braces_for_chaining` style enforces braces around single line blocks + # and do..end around multi-line blocks, except for multi-line blocks whose + # return value is being chained with another method (in which case braces + # are enforced). + - braces_for_chaining + ProceduralMethods: + # Methods that are known to be procedural in nature but look functional from + # their usage, e.g. + # + # time = Benchmark.realtime do + # foo.bar + # end + # + # Here, the return value of the block is discarded but the return value of + # `Benchmark.realtime` is used. + - benchmark + - bm + - bmbm + - create + - each_with_object + - measure + - new + - realtime + - tap + - with_object + FunctionalMethods: + # Methods that are known to be functional in nature but look procedural from + # their usage, e.g. + # + # let(:foo) { Foo.new } + # + # Here, the return value of `Foo.new` is used to define a `foo` helper but + # doesn't appear to be used from the return value of `let`. + - let + - let! + - subject + - watch + AllowedMethods: + # Methods that can be either procedural or functional and cannot be + # categorised from their usage alone, e.g. + # + # foo = lambda do |x| + # puts "Hello, #{x}" + # end + # + # foo = lambda do |x| + # x * 100 + # end + # + # Here, it is impossible to tell from the return value of `lambda` whether + # the inner block's return value is significant. + - lambda + - proc + - it + +Style/CaseEquality: + Description: Avoid explicit use of the case equality operator(===). + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-case-equality + Enabled: false + +# Supports --auto-correct +Style/CharacterLiteral: + Description: Checks for uses of character literals. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-character-literals + Enabled: false + +Style/ClassAndModuleChildren: + Description: Checks style of children classes and modules. + Enabled: false + EnforcedStyle: nested + +# Supports --auto-correct +Style/ClassCheck: + Description: Enforces consistent use of `Object#is_a?` or `Object#kind_of?`. + Enabled: true + EnforcedStyle: is_a? + +# Supports --auto-correct +Style/ClassMethods: + Description: Use self when defining module/class methods. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#def-self-class-methods + Enabled: false + +Style/ClassVars: + Description: Avoid the use of class variables. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-class-vars + Enabled: true + +# Supports --auto-correct +Style/CollectionMethods: + Description: Preferred collection methods. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#map-find-select-reduce-size + Enabled: false + PreferredMethods: + collect: map + collect!: map! + inject: reduce + detect: detect + find: detect + find_all: select + +# Supports --auto-correct +Style/ColonMethodCall: + Description: ! 'Do not use :: for method call.' + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#double-colons + Enabled: true + +Style/ColonMethodDefinition: + Description: 'Do not use :: for defining class methods.' + StyleGuide: '#colon-method-definition' + Enabled: true + +# Supports --auto-correct +Style/CommandLiteral: + Description: Use `` or %x around command literals. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#percent-x + Enabled: true + EnforcedStyle: backticks + AllowInnerBackticks: false + +# Supports --auto-correct +Style/CommentAnnotation: + Description: Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK, REVIEW). + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#annotate-keywords + Enabled: false + Keywords: + - TODO + - FIXME + - OPTIMIZE + - HACK + - REVIEW + +Style/CommentedKeyword: + Enabled: false + +# Supports --auto-correct +Style/ConditionalAssignment: + Description: Use the return value of `if` and `case` statements for assignment to + a variable and variable comparison instead of assigning that variable inside of + each branch. + Enabled: false + SingleLineConditionsOnly: true + +# Supports --auto-correct +Style/Copyright: + Description: Include a copyright notice in each file before any code. + Enabled: false + Notice: ^Copyright (\(c\) )?2[0-9]{3} .+ + AutocorrectNotice: '' + +Style/DateTime: + Enabled: false + +# Supports --auto-correct +Style/DefWithParentheses: + Description: Use def with parentheses when there are arguments. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#method-parens + Enabled: false + +Style/Dir: + Enabled: false + +# Don't force documentation +Style/Documentation: + Enabled: false + +Style/DocumentationMethod: + Enabled: false + +Style/DoubleNegation: + Description: Checks for uses of double negation (!!). + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-bang-bang + Enabled: false + +Style/EachForSimpleLoop: + Description: >- + Use `Integer#times` for a simple loop which iterates a fixed + number of times. + Enabled: true + +Style/EachWithObject: + Description: Prefer `each_with_object` over `inject` or `reduce`. + Enabled: false + +Style/EmptyBlockParameter: + Description: 'Omit pipes for empty block parameters.' + Enabled: true + +Style/EmptyCaseCondition: + Enabled: false + +# Supports --auto-correct +Style/EmptyElse: + Description: Avoid empty else-clauses. + Enabled: false + EnforcedStyle: both + SupportedStyles: + - empty + - nil + - both + +Style/EmptyLambdaParameter: + Description: 'Omit parens for empty lambda parameters.' + Enabled: true + +# Supports --auto-correct +Style/EmptyLiteral: + Description: Prefer literals to Array.new/Hash.new/String.new. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#literal-array-hash + Enabled: true + +Style/EmptyMethod: + Enabled: false + +# Supports --auto-correct +Style/Encoding: + Description: Use UTF-8 as the source file encoding. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#utf-8 + Enabled: false + +Style/EndBlock: + Description: Avoid the use of END blocks. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-END-blocks + Enabled: false + +Style/EvalWithLocation: + Description: 'Pass `__FILE__` and `__LINE__` to `eval` method, as they are used by backtraces.' + Enabled: false + +# Supports --auto-correct +Style/EvenOdd: + Description: Favor the use of Fixnum#even? && Fixnum#odd? + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#predicate-methods + Enabled: false + +Style/ExpandPathArguments: + Description: "Use `expand_path(__dir__)` instead of `expand_path('..', __FILE__)`." + Enabled: false + +Style/For: + Description: Checks use of for or each in multiline loops. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-for-loops + Enabled: false + EnforcedStyle: each + SupportedStyles: + - for + - each + +Style/FormatString: + Description: Enforce the use of Kernel#sprintf, Kernel#format or String#%. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#sprintf + Enabled: false + EnforcedStyle: format + SupportedStyles: + - format + - sprintf + - percent + +Style/FormatStringToken: + Enabled: false + +# Supports --auto-correct +Style/FrozenStringLiteralComment: + Description: Add the frozen_string_literal comment to the top of files to help transition + from Ruby 2.3.0 to Ruby 3.0. + Enabled: false + SupportedStyles: + - always + - always_true + - never + EnforcedStyle: always_true + +Style/GlobalVars: + Description: Do not introduce global variables. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#instance-vars + Reference: http://www.zenspider.com/Languages/Ruby/QuickRef.html + Enabled: false + AllowedVariables: [] + +# This thing seems a little error prone, and is kind of annoying. Let's +# leave this up to the individual. +Style/GuardClause: + Enabled: false + +# Don't force colon-style hash pairs. Sometimes ya just don't want 'em. +# (Allen approved!) +Style/HashSyntax: + Enabled: false + +Style/IdenticalConditionalBranches: + Description: Checks that conditional statements do not have an identical line at the + end of each branch, which can validly be moved out of the conditional. + Enabled: false + +Style/IfInsideElse: + Description: Finds if nodes inside else, which can be converted to elsif. + Enabled: false + +# Don't force trailing if/unless for single-line conditionals +Style/IfUnlessModifier: + Enabled: false + +Style/IfUnlessModifierOfIfUnless: + Description: >- + Checks for if and unless statements used as modifers of other if or unless statements. + Enabled: true + +Style/IfWithSemicolon: + Description: Do not use if x; .... Use the ternary operator instead. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-semicolon-ifs + Enabled: true + +# Supports --auto-correct +Style/InfiniteLoop: + Description: Use Kernel#loop for infinite loops. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#infinite-loop + Enabled: false + +Style/InlineComment: + Description: Avoid inline comments. + Enabled: false + +Style/InverseMethods: + Enabled: false + +# Supports --auto-correct +Style/Lambda: + Description: Use the new lambda literal syntax for single-line blocks. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#lambda-multi-line + Enabled: false + +# Supports --auto-correct +Style/LambdaCall: + Description: Use lambda.call(...) instead of lambda.(...). + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#proc-call + Enabled: true + EnforcedStyle: call + SupportedStyles: + - call + - braces + +# Supports --auto-correct +Style/LineEndConcatenation: + Description: Use \ instead of + or << to concatenate two string literals at line end. + Enabled: true + +Style/MethodCallWithArgsParentheses: + Enabled: false + +# Supports --auto-correct +Style/MethodCallWithoutArgsParentheses: + Description: Do not use parentheses for method calls with no arguments. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-args-no-parens + Enabled: true + +Style/MethodCalledOnDoEndBlock: + Description: Avoid chaining a method call on a do...end block. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#single-line-blocks + Enabled: false + +# Supports --auto-correct +Style/MethodDefParentheses: + Description: Checks if the method definitions have or don't have parentheses. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#method-parens + Enabled: true + EnforcedStyle: require_parentheses + SupportedStyles: + - require_parentheses + - require_no_parentheses + +Style/MissingRespondToMissing: + Enabled: false + +Style/MinMax: + Enabled: false + +Style/MissingElse: + Description: Require if/case expressions to have an else branches. If enabled, it + is recommended that Style/UnlessElse and Style/EmptyElse be enabled. This will conflict + with Style/EmptyElse if Style/EmptyElse is configured to style "both" + Enabled: false + EnforcedStyle: both + SupportedStyles: + - if + - case + - both + +Style/MixinGrouping: + Description: This cop checks for grouping of mixins in `class` and `module` bodies. By default + it enforces mixins to be placed in separate declarations, but it can be configured to enforce + grouping them in one declaration. + Enabled: true + +Style/MixinUsage: + Description: This cop checks that `include`, `extend` and `prepend` exists at the top level. + Using these at the top level affects the behavior of `Object`. There will not be using + `include`, `extend` and `prepend` at the top level. Let's use it inside `class` or `module`. + Enabled: true + +# Supports --auto-correct +Style/ModuleFunction: + Description: Checks for usage of `extend self` in modules. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#module-function + Enabled: false + +Style/MultilineBlockChain: + Description: Avoid multi-line chains of blocks. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#single-line-blocks + Enabled: false + +Style/MultilineIfModifier: + Enabled: true + +# Supports --auto-correct +Style/MultilineIfThen: + Description: Do not use then for multi-line if/unless. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-then + Enabled: true + +Style/MultilineMemoization: + Enabled: false + +Style/MultilineTernaryOperator: + Description: ! 'Avoid multi-line ?: (the ternary operator); use if/unless instead.' + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-multiline-ternary + Enabled: true + +Style/MultipleComparison: + Enabled: false + +# Supports --auto-correct +Style/MutableConstant: + Description: Do not assign mutable objects to constants. + Enabled: true + +# Supports --auto-correct +Style/NegatedIf: + Description: Favor unless over if for negative conditions (or control flow or). + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#unless-for-negatives + Enabled: false + +# Supports --auto-correct +Style/NegatedWhile: + Description: Favor until over while for negative conditions. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#until-for-negatives + Enabled: false + +Style/NestedModifier: + Enabled: true + +Style/NestedParenthesizedCalls: + Description: Parenthesize method calls which are nested inside the argument list of + another parenthesized method call. + Enabled: true + +Style/NestedTernaryOperator: + Description: Use one expression per branch in a ternary operator. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-nested-ternary + Enabled: true + +Style/Next: + Description: Use `next` to skip iteration instead of a condition at the end. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-nested-conditionals + Enabled: false + EnforcedStyle: skip_modifier_ifs + MinBodyLength: 3 + SupportedStyles: + - skip_modifier_ifs + - always + +# Supports --auto-correct +Style/NilComparison: + Description: Prefer x.nil? to x == nil. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#predicate-methods + Enabled: true + +# Supports --auto-correct +Style/NonNilCheck: + Description: Checks for redundant nil checks. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-non-nil-checks + Enabled: true + IncludeSemanticChanges: false + +# Supports --auto-correct +Style/Not: + Description: Use ! instead of not. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#bang-not-not + Enabled: true + +Style/NumericLiteralPrefix: + EnforcedOctalStyle: zero_with_o + SupportedOctalStyles: + - zero_with_o + - zero_only + Enabled: true + +# We just don't like this style. +Style/NumericLiterals: + Description: Add underscores to large numeric literals to improve their readability. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#underscores-in-numerics + Enabled: false + +Style/NumericPredicate: + Enabled: false + +Style/OneLineConditional: + Description: Favor the ternary operator(?:) over if/then/else/end constructs. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#ternary-operator + Enabled: true + +Style/OptionHash: + Description: Don't use option hashes when you can use keyword arguments. + Enabled: false + SuspiciousParamNames: + - options + - opts + - args + - params + - parameters + +Style/OptionalArguments: + Description: Checks for optional arguments that do not appear at the end of the argument + list + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#optional-arguments + Enabled: true + +Style/OrAssignment: + Enabled: true + +# Supports --auto-correct +Style/ParallelAssignment: + Description: Check for simple usages of parallel assignment. It will only warn when + the number of variables matches on both sides of the assignment. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#parallel-assignment + Enabled: false + +# Supports --auto-correct +Style/ParenthesesAroundCondition: + Description: Don't use parentheses around the condition of an if/unless/while. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-parens-if + Enabled: true + AllowSafeAssignment: true + +# Supports --auto-correct +Style/PercentLiteralDelimiters: + Description: Use `%`-literal delimiters consistently + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#percent-literal-braces + Enabled: true + PreferredDelimiters: + ! '%': () + ! '%i': () + ! '%q': () + ! '%Q': () + ! '%r': ! '{}' + ! '%s': () + ! '%w': () + ! '%W': () + ! '%x': () + +# Supports --auto-correct +Style/PercentQLiterals: + Description: Checks if uses of %Q/%q match the configured preference. + Enabled: false + EnforcedStyle: lower_case_q + SupportedStyles: + - lower_case_q + - upper_case_q + +# Supports --auto-correct +Style/PerlBackrefs: + Description: Avoid Perl-style regex back references. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-perl-regexp-last-matchers + Enabled: true + +# Supports --auto-correct +Style/PreferredHashMethods: + Description: Checks for use of deprecated Hash methods. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#hash-key + Enabled: true + +# Supports --auto-correct +Style/Proc: + Description: Use proc instead of Proc.new. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#proc + Enabled: false + +Style/RaiseArgs: + Description: Checks the arguments passed to raise/fail. + # Also https://github.com/airbnb/ruby#exception-class-messages + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#exception-class-messages + Enabled: true + EnforcedStyle: exploded + SupportedStyles: + - compact + - exploded + +Style/RandomWithOffset: + Description: Prefer to use ranges when generating random numbers instead of integers with offsets. + StyleGuide: '#random-numbers' + Enabled: false + +# Supports --auto-correct +Style/RedundantBegin: + Description: Don't use begin blocks when they are not needed. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#begin-implicit + Enabled: true + +Style/RedundantConditional: + Enabled: true + +# Supports --auto-correct +Style/RedundantException: + Description: Checks for an obsolete RuntimeException argument in raise/fail. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-explicit-runtimeerror + Enabled: true + +# Supports --auto-correct +Style/RedundantFreeze: + Description: Checks usages of Object#freeze on immutable objects. + Enabled: true + +# Supports --auto-correct +Style/RedundantParentheses: + Description: Checks for parentheses that seem not to serve any purpose. + Enabled: true + +# Supports --auto-correct +Style/RedundantReturn: + Description: Don't use return where it's not required. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-explicit-return + Enabled: true + AllowMultipleReturnValues: false + +# Supports --auto-correct +Style/RedundantSelf: + Description: Don't use self where it's not needed. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-self-unless-required + Enabled: true + +# Supports --auto-correct +Style/RedundantSortBy: + Description: Use `sort` instead of `sort_by { |x| x }`. + Enabled: false + +# Supports --auto-correct +Style/RegexpLiteral: + Description: Use / or %r around regular expressions. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#percent-r + Enabled: false + EnforcedStyle: slashes + SupportedStyles: + - slashes + - percent_r + - mixed + AllowInnerSlashes: false + +# Supports --auto-correct +Style/RescueModifier: + Description: Avoid using rescue in its modifier form. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-rescue-modifiers + Enabled: true + +Style/RescueStandardError: + Description: 'Avoid rescuing without specifying an error class.' + Enabled: false + +Style/ReturnNil: + Enabled: false + +Style/SafeNavigation: + Enabled: false + +# Supports --auto-correct +Style/Sample: + Description: Use `sample` instead of `shuffle.first`, `shuffle.last`, and `shuffle[Fixnum]`. + Reference: https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code + Enabled: false + +# Supports --auto-correct +Style/SelfAssignment: + Description: Checks for places where self-assignment shorthand should have been used. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#self-assignment + Enabled: true + +# Supports --auto-correct +Style/Semicolon: + Description: Don't use semicolons to terminate expressions. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-semicolon + Enabled: true + AllowAsExpressionSeparator: false + +Style/Send: + Description: Prefer `Object#__send__` or `Object#public_send` to `send`, as `send` + may overlap with existing methods. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#prefer-public-send + Enabled: false + +# Supports --auto-correct +Style/SignalException: + Description: Checks for proper usage of fail and raise. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#fail-method + Enabled: false + EnforcedStyle: semantic + SupportedStyles: + - only_raise + - only_fail + - semantic + +Style/SingleLineBlockParams: + Description: Enforces the names of some block params. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#reduce-blocks + Enabled: false + Methods: + - reduce: + - a + - e + - inject: + - a + - e + +# Supports --auto-correct +Style/SingleLineMethods: + Description: Avoid single-line methods. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-single-line-methods + Enabled: true + AllowIfMethodIsEmpty: true + +# Supports --auto-correct +Style/SpecialGlobalVars: + Description: Avoid Perl-style global variables. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-cryptic-perlisms + Enabled: true + +Style/HashEachMethods: + Description: Enforce use of each_key and each_value Hash methods. + StyleGuide: https://docs.rubocop.org/en/latest/cops_style/#stylehasheachmethods + Enabled: false + +Style/HashTransformKeys: + Description: Enforce use of transform_keys Hash methods. Not suggested for use below ruby 2.5 + StyleGuide: https://docs.rubocop.org/en/latest/cops_style/#stylehashtransformkeys + Enabled: false + +Style/HashTransformValues: + Description: Enforce use of transform_values Hash methods. Not suggested for use below ruby 2.5 + StyleGuide: https://docs.rubocop.org/en/latest/cops_style/#stylehashtransformvalues + Enabled: false + +Style/StabbyLambdaParentheses: + Description: Check for the usage of parentheses around stabby lambda arguments. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#stabby-lambda-with-args + Enabled: false + EnforcedStyle: require_parentheses + SupportedStyles: + - require_parentheses + - require_no_parentheses + +Style/StderrPuts: + Enabled: true + +Style/StringHashKeys: + Description: 'Prefer symbols instead of strings as hash keys.' + StyleGuide: '#symbols-as-keys' + Enabled: false + +# Allow double-quoted strings without interpolation. The customer is always right. +Style/StringLiterals: + Enabled: false + +# Supports --auto-correct +Style/StringLiteralsInInterpolation: + Description: Checks if uses of quotes inside expressions in interpolated strings match + the configured preference. + Enabled: false + EnforcedStyle: single_quotes + SupportedStyles: + - single_quotes + - double_quotes + +# Supports --auto-correct +Style/StringMethods: + Description: Checks if configured preferred methods are used over non-preferred. + Enabled: false + PreferredMethods: + intern: to_sym + +# Supports --auto-correct +Style/Strip: + Description: Use `strip` instead of `lstrip.rstrip`. + Enabled: false + +Style/StructInheritance: + Description: Checks for inheritance from Struct.new. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-extend-struct-new + Enabled: false + +Style/SymbolArray: + Description: Use %i or %I for arrays of symbols. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#percent-i + Enabled: false + +# Supports --auto-correct +Style/SymbolLiteral: + Description: Use plain symbols instead of string symbols when possible. + Enabled: true + +# Supports --auto-correct +Style/SymbolProc: + Description: Use symbols as procs instead of blocks when possible. + Enabled: false + AllowedMethods: + - respond_to + +Style/TernaryParentheses: + Enabled: false + +Style/TrailingBodyOnClass: + Description: 'Class body goes below class statement.' + Enabled: true + +Style/TrailingBodyOnMethodDefinition: + Description: 'Method body goes below definition.' + Enabled: true + +Style/TrailingBodyOnModule: + Description: 'Module body goes below module statement.' + Enabled: true + +Style/TrailingCommaInArguments: + Enabled: false + +# Enforce trailing commas (we like 'em!) +Style/TrailingCommaInArrayLiteral: + Enabled: true + EnforcedStyleForMultiline: consistent_comma + +# Enforce trailing commas (we like 'em!) +Style/TrailingCommaInHashLiteral: + Enabled: true + EnforcedStyleForMultiline: consistent_comma + +# Supports --auto-correct +Style/TrailingUnderscoreVariable: + Description: Checks for the usage of unneeded trailing underscores at the end of parallel + variable assignment. + Enabled: false + +# Allow question mark accessor methods +Style/TrivialAccessors: + AllowPredicates: true + +Style/UnlessElse: + Description: Do not use unless with else. Rewrite these with the positive case first. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-else-with-unless + Enabled: true + +# Supports --auto-correct +Style/RedundantCapitalW: + Description: Checks for %W when interpolation is not needed. + Enabled: false + +Style/RedundantCondition: + Enabled: false + +# Supports --auto-correct +Style/RedundantInterpolation: + Description: Checks for strings that are just an interpolated expression. + Enabled: false + +# Supports --auto-correct +Style/RedundantPercentQ: + Description: Checks for %q/%Q when single quotes or double quotes would do. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#percent-q + Enabled: false + +Style/RedundantSort: + Enabled: false + +Style/UnpackFirst: + Enabled: false + +# Supports --auto-correct +Style/VariableInterpolation: + Description: Don't interpolate global, instance and class variables directly in strings. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#curlies-interpolate + Enabled: false + +# Supports --auto-correct +Style/WhenThen: + Description: Use when x then ... for one-line cases. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#one-line-cases + Enabled: false + +# Supports --auto-correct +Style/WhileUntilDo: + Description: Checks for redundant do after while or until. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#no-multiline-while-do + Enabled: true + +# Supports --auto-correct +Style/WhileUntilModifier: + Description: Favor modifier while/until usage when you have a single-line body. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#while-as-a-modifier + Enabled: false + +# Supports --auto-correct +Style/WordArray: + Description: Use %w or %W for arrays of words. + StyleGuide: https://github.com/rubocop-hq/ruby-style-guide#percent-w + Enabled: false + MinSize: 0 + WordRegex: !ruby/regexp /\A[\p{Word}]+\z/ + +Style/YodaCondition: + Enabled: false + +Style/ZeroLengthPredicate: + Description: 'Use #empty? when testing for objects of length 0.' + Enabled: false diff --git a/rubocop-airbnb/lib/rubocop-airbnb.rb b/rubocop-airbnb/lib/rubocop-airbnb.rb new file mode 100644 index 0000000..d7cc81b --- /dev/null +++ b/rubocop-airbnb/lib/rubocop-airbnb.rb @@ -0,0 +1,12 @@ +require 'pathname' +require 'yaml' + +# Load original rubocop gem +require 'rubocop' + +require 'rubocop-performance' +require 'rubocop-rails' + +require_relative 'rubocop/airbnb' +require_relative 'rubocop/airbnb/plugin' +require_relative 'rubocop/airbnb/version' diff --git a/rubocop-airbnb/lib/rubocop/airbnb.rb b/rubocop-airbnb/lib/rubocop/airbnb.rb new file mode 100644 index 0000000..27cebe8 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/airbnb.rb @@ -0,0 +1,16 @@ +require 'pathname' +require 'psych' + +Dir.glob(File.expand_path('cop/**/*.rb', File.dirname(__FILE__))).map(&method(:require)) + +module RuboCop + # RuboCop Airbnb project namespace + module Airbnb + PROJECT_ROOT = + Pathname.new(__FILE__).parent.parent.parent.expand_path.freeze + CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze + CONFIG = Psych.safe_load(CONFIG_DEFAULT.read).freeze + + private_constant(*constants(false)) + end +end diff --git a/rubocop-airbnb/lib/rubocop/airbnb/inflections.rb b/rubocop-airbnb/lib/rubocop/airbnb/inflections.rb new file mode 100644 index 0000000..39a794a --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/airbnb/inflections.rb @@ -0,0 +1,14 @@ +# String inflections copied over from ActiveSupport +module Inflections + # Convert Foo::BarBaz to foo/bar_baz. + # Copied from ActiveSupport. + def underscore(camel_cased_word) + word = camel_cased_word.to_s.dup + word.gsub!(/::/, '/') + word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2') + word.gsub!(/([a-z\d])([A-Z])/, '\1_\2') + word.tr!("-", "_") + word.downcase! + word + end +end diff --git a/rubocop-airbnb/lib/rubocop/airbnb/plugin.rb b/rubocop-airbnb/lib/rubocop/airbnb/plugin.rb new file mode 100644 index 0000000..6406024 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/airbnb/plugin.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'lint_roller' + +module RuboCop + module Airbnb + # A plugin that integrates RuboCop Airbnb with RuboCop's plugin system. + class Plugin < LintRoller::Plugin + def about + LintRoller::About.new( + name: 'rubocop-airbnb', + version: VERSION, + homepage: 'https://github.com/airbnb/ruby', + description: 'A plugin for RuboCop code style enforcing & linting tool.' + ) + end + + def supported?(context) + context.engine == :rubocop + end + + def rules(_context) + LintRoller::Rules.new( + type: :path, + config_format: :rubocop, + value: Pathname.new(__dir__).join('../../../config/default.yml'), + ) + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/airbnb/rails_autoloading.rb b/rubocop-airbnb/lib/rubocop/airbnb/rails_autoloading.rb new file mode 100644 index 0000000..26fee31 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/airbnb/rails_autoloading.rb @@ -0,0 +1,55 @@ +# These methods are useful for Rubocop rules related to Rails autoloading. +module RailsAutoloading + def run_rails_autoloading_cops?(path) + return false unless config["Rails".freeze] + return false unless config["Rails".freeze]["Enabled".freeze] + + # Ignore rake tasks + return false unless path.end_with?(".rb") + + true + end + + # Given "foo/bar/baz", return: + # [ + # %r{/foo.rb$}, + # %r{/foo/bar.rb$}, + # %r{/foo/bar/baz.rb$}, + # %r{/foo/bar/baz/}, # <= only if allow_dir = true + # ] + def allowable_paths_for(expected_dir, options = {}) + options = { allow_dir: false }.merge(options) + allowable_paths = [] + next_slash = expected_dir.index("/") + while next_slash + allowable_paths << %r{/#{expected_dir[0...next_slash]}.rb$} + next_slash = expected_dir.index("/", next_slash + 1) + end + allowable_paths << %r{#{expected_dir}.rb$} + allowable_paths << %r{/#{expected_dir}/} if options[:allow_dir] + allowable_paths + end + + def normalize_module_name(module_name) + return '' if module_name.nil? + normalized_name = module_name.gsub(/#/, "") + normalized_name = "" if normalized_name == "Object" + normalized_name + end + + # module_name looks like one of these: + # Foo::Bar for an instance method + # # for a class method. + # For either case we return ["Foo", "Bar"] + def split_modules(module_name) + normalize_module_name(module_name).split("::") + end + + def full_const_name(parent_module_name, const_name) + if parent_module_name == "".freeze + "#{const_name}" + else + "#{parent_module_name}::#{const_name}" + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/airbnb/version.rb b/rubocop-airbnb/lib/rubocop/airbnb/version.rb new file mode 100644 index 0000000..b0b82e4 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/airbnb/version.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module RuboCop + module Airbnb + # Version information for the the Airbnb RuboCop plugin. + VERSION = '8.1.0' + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file.rb new file mode 100644 index 0000000..96268af --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file.rb @@ -0,0 +1,138 @@ +require_relative '../../airbnb/inflections' +require_relative '../../airbnb/rails_autoloading' + +module RuboCop + module Cop + module Airbnb + # This cop checks for a class or module declared in a file that does not match its name. + # The Rails autoloader can't find such a constant, but sometimes + # people "get lucky" if the file happened to be loaded before the method was defined. + # + # @example + # # bad + # + # # foo/bar.rb + # module Foo + # class Goop + # end + # + # module Moo + # end + # end + # + # # good + # + # # foo.rb + # + # # foo/goop.rb + # module Foo + # class Goop + # end + # end + # + # # foo/moo.rb + # module Foo + # module Moo + # end + # end + # + # Note that autoloading works fine if classes are defined in the file that defines + # the module. This is common usage for things like error classes, so we'll allow it. + # Nested classes are also allowed: + # + # @example + # # good + # + # # foo.rb + # module Foo + # class Bar < StandardError + # end + # end + # + # # good + # + # # foo.rb + # class Foo + # class Bar # nested class + # end + # end + class ClassOrModuleDeclaredInWrongFile < Base + include Inflections + include RailsAutoloading + + # class Foo or module Foo in the wrong file + CLASS_OR_MODULE_MSG = + "In order for Rails autoloading to be able to find and load this file when " \ + "someone references this class/module, move its definition to a file that matches " \ + "its name. %s %s should be defined in %s.".freeze + # class FooError < StandardError, in the wrong file + ERROR_CLASS_MSG = + "In order for Rails autoloading to be able to find and load this file when " \ + "someone references this class, move its definition to a file that defines " \ + "the owning module. Class %s should be defined in %s.".freeze + + # module M + def on_module(node) + on_class_or_module(node) + end + + # class C + def on_class(node) + on_class_or_module(node) + end + + private + + def on_class_or_module(node) + path = node.source_range.source_buffer.name + return unless run_rails_autoloading_cops?(path) + + const_name = node.loc.name.source + parent_module_name = normalize_module_name(node.parent_module_name) + fully_qualified_const_name = full_const_name(parent_module_name, const_name) + expected_dir = underscore(fully_qualified_const_name) + allowable_paths = allowable_paths_for(expected_dir, allow_dir: true) + if allowable_paths.none? { |allowable_path| path =~ allowable_path } + add_error(const_name, fully_qualified_const_name, node) + end + rescue => e + puts e.backtrace + raise + end + + def add_error(const_name, fully_qualified_const_name, node) + class_or_module = node.type.to_s.capitalize + error_class = error_class?(node, class_or_module, const_name) + if error_class + parent_module_names = split_modules(node.parent_module_name) + else + parent_module_names = split_modules(fully_qualified_const_name) + end + expected_file = "#{parent_module_names.map { |name| underscore(name) }.join("/")}.rb" + if error_class + add_offense(node, message: ERROR_CLASS_MSG % [const_name, expected_file]) + else + add_offense( + node, + message: CLASS_OR_MODULE_MSG % [class_or_module, const_name, expected_file] + ) + end + end + + # Does this node define an Error class? (Classname or base class includes the word + # "Error" or "Exception".) + def error_class?(node, class_or_module, const_name) + return false unless class_or_module == "Class" + _, base_class, *_ = *node + return unless base_class + base_class_name = base_class.children[1].to_s + if const_name =~ /Error|Exception/ || base_class_name =~ /Error|Exception/ + return true + end + + false + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/const_assigned_in_wrong_file.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/const_assigned_in_wrong_file.rb new file mode 100644 index 0000000..ff87f1e --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/const_assigned_in_wrong_file.rb @@ -0,0 +1,74 @@ +require_relative '../../airbnb/inflections' +require_relative '../../airbnb/rails_autoloading' + +module RuboCop + module Cop + module Airbnb + # This cop checks for a constant assigned in a file that does not match its owning scope. + # The Rails autoloader can't find such a constant, but sometimes + # people "get lucky" if the file happened to be loaded before the method was defined. + # + # @example + # # bad + # + # # foo/bar.rb + # module Foo + # BAZ = 42 + # end + # + # # good + # + # # foo.rb + # module Foo + # BAZ = 42 + # end + class ConstAssignedInWrongFile < Base + include Inflections + include RailsAutoloading + + # FOO = 42 + ASSIGNMENT_MSG = + "In order for Rails autoloading to be able to find and load this file when " \ + "someone references this const, move the const assignment to a file that defines " \ + "the owning module. Const %s should be defined in %s.".freeze + # FOO = 42 at global scope + GLOBAL_ASSIGNMENT = + "In order for Rails autoloading to be able to find and load this file when " \ + "someone references this const, move the const assignment to a file that defines " \ + "the owning module. Const %s should be moved into a namespace or defined in %s.".freeze + + # FOO = 42 + def on_casgn(node) + path = node.source_range.source_buffer.name + return unless run_rails_autoloading_cops?(path) + return unless node.parent_module_name + + # Ignore assignments like Foo::Bar = 42 + return if node.children[0] + + const_name = node.children[1] + parent_module_name = normalize_module_name(node.parent_module_name) + fully_qualified_const_name = full_const_name(parent_module_name, const_name) + expected_dir = underscore(fully_qualified_const_name) + allowable_paths = allowable_paths_for(expected_dir) + if allowable_paths.none? { |allowable_path| path =~ allowable_path } + add_error(const_name, node) + end + end + + private + + def add_error(const_name, node) + parent_module_names = split_modules(node.parent_module_name) + expected_file = "#{parent_module_names.map { |name| underscore(name) }.join("/")}.rb" + if expected_file == ".rb" # global namespace + expected_file = "#{underscore(const_name)}.rb" + add_offense(node, message: GLOBAL_ASSIGNMENT % [const_name, expected_file]) + else + add_offense(node, message: ASSIGNMENT_MSG % [const_name, expected_file]) + end + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/continuation_slash.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/continuation_slash.rb new file mode 100644 index 0000000..730c05c --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/continuation_slash.rb @@ -0,0 +1,25 @@ +module RuboCop + module Cop + module Airbnb + class ContinuationSlash < Base + MSG = 'Slash continuation should be reserved for closed string continuation. ' \ + 'Many times it is used to get around other existing rules.'.freeze + + def enforce_violation(node) + return if node.source.match(/["']\s*\\\n/) + return unless node.source.match(/\\\n/) + add_offense(node, message: message) + end + + alias on_send enforce_violation + alias on_if enforce_violation + + ::RuboCop::AST::Node::ASSIGNMENTS.each do |type| + define_method("on_#{type}") do |node| + enforce_violation(node) + end + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/default_scope.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/default_scope.rb new file mode 100644 index 0000000..7cfb064 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/default_scope.rb @@ -0,0 +1,21 @@ +module RuboCop + module Cop + module Airbnb + # Cop to help prevent the scorge of Default Scopes from ActiveRecord. + # Once in place they are almost impossible to remove. + class DefaultScope < Base + MSG = 'Avoid `default_scope`. Default scopes make it difficult to '\ + 'refactor data access patterns since the scope becomes part '\ + 'of every query unless explicitly excluded, even when it is '\ + 'unnecessary or incidental to the desired logic.'.freeze + RESTRICT_ON_SEND = %i(default_scope).freeze + + def on_send(node) + return if node.receiver + + add_offense(node) + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_attr_references_class.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_attr_references_class.rb new file mode 100644 index 0000000..1ca9de0 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_attr_references_class.rb @@ -0,0 +1,74 @@ +module RuboCop + module Cop + module Airbnb + # Cop to enforce "attr { CONST }" instead of "attr CONST" in factories, + # because the latter forces autoload, which slows down spec startup time and + # Zeus reload time after touching a model. + class FactoryAttrReferencesClass < Base + MSG = "Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. " \ + "This enables faster spec startup time and Zeus reload time.".freeze + + def_node_search :factory_attributes, <<-PATTERN + (block (send nil {:factory :trait} ...) _ { (begin $...) $(send ...) } ) + PATTERN + + # Look for "attr CONST" expressions in factories or traits. In RuboCop, this is + # a `send` node, sending the attr method. + def on_send(node) + return unless in_factory_file?(node) + return unless in_factory_or_trait?(node) + + add_const_offenses(node) + end + + private + + def in_factory_file?(node) + filename = node.location.expression.source_buffer.name + + # For tests, the input is a string + filename.include?("spec/factories/") || filename == "(string)" + end + + # Is this node in a factory or trait, but not inside a nested block in a factory or trait? + def in_factory_or_trait?(node) + return false unless node + + # Bail out if this IS the factory or trait node. + return false unless factory_attributes(node) + return false unless node.parent + + # Is this node in a block that was passed to the factory or trait method? + if node.parent.is_a?(RuboCop::AST::Node) && node.parent.block_type? + send_node = node.parent.children.first + return false unless send_node + return false unless send_node.send_type? + + # Const is referenced in the block passed to a factory or trait. + return true if send_node.command?(:factory) + return true if send_node.command?(:trait) + + # Const is a block that's nested deeper inside a factory or trait. This is what we want + # developers to do. + return false + end + + in_factory_or_trait?(node.parent) + end + + def add_const_offenses(node) + # Add an offense for any const reference + node.each_child_node(:const) do |const_node| + add_offense(const_node) + end + + # Recurse into arrays, hashes, and method calls such as ConstName[:symbol], + # adding offenses for any const reference inside them. + node.each_child_node(:array, :hash, :pair, :send) do |array_node| + add_const_offenses(array_node) + end + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_class_use_string.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_class_use_string.rb new file mode 100644 index 0000000..3a1c6fa --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_class_use_string.rb @@ -0,0 +1,40 @@ +module RuboCop + module Cop + module Airbnb + # Cop to tell developers to use :class => "MyClass" instead of :class => MyClass, + # because the latter slows down reloading zeus. + class FactoryClassUseString < Base + MSG = 'Instead of :class => MyClass, use :class => "MyClass". ' \ + "This enables faster spec startup time and faster Zeus reload time.".freeze + RESTRICT_ON_SEND = %i(factory).freeze + + def on_send(node) + return if node.receiver + + class_pair = class_node(node) + + if class_pair && !string_class_name?(class_pair) + add_offense(class_pair) + end + end + + private + + # Return the descendant node that is a hash pair (:key => value) whose key + # is :class. + def class_node(node) + node.descendants.detect do |e| + e.is_a?(Parser::AST::Node) && + e.pair_type? && + e.children[0].children[0] == :class + end + end + + # Given a hash pair :class_name => value, is the value a hardcoded string? + def string_class_name?(class_pair) + class_pair.children[1].str_type? + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/mass_assignment_accessible_modifier.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/mass_assignment_accessible_modifier.rb new file mode 100644 index 0000000..233cf0f --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/mass_assignment_accessible_modifier.rb @@ -0,0 +1,16 @@ +module RuboCop + module Cop + module Airbnb + # Modifying Mass assignment restrictions eliminates the entire point of disabling + # mass assignment. It's a lazy, potentially dangerous approach that should be discouraged. + class MassAssignmentAccessibleModifier < Base + MSG = 'Do no override and objects mass assignment restrictions.'.freeze + RESTRICT_ON_SEND = %i(accessible=).freeze + + def on_send(node) + add_offense(node, message: MSG) + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/module_method_in_wrong_file.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/module_method_in_wrong_file.rb new file mode 100644 index 0000000..c5ca811 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/module_method_in_wrong_file.rb @@ -0,0 +1,104 @@ +require_relative '../../airbnb/inflections' +require_relative '../../airbnb/rails_autoloading' + +module RuboCop + module Cop + module Airbnb + # This cop checks for methods defined in a module declaration, in a file that doesn't + # match the module name. The Rails autoloader can't find such a method, but sometimes + # people "get lucky" if the file happened to be loaded before the method was defined. + # + # @example + # # bad + # + # # foo/bar.rb + # module Foo + # class Bar + # end + # + # def baz + # 42 + # end + # end + # + # # good + # + # # foo/bar.rb + # module Foo + # class Bar + # end + # end + # + # # foo.rb + # module Foo + # def baz + # 42 + # end + # end + # + # Note that autoloading works fine if classes are defined in the file that defines + # the module. This is common usage for things like error classes, so we'll allow it: + # + # @example + # # good + # + # # foo.rb + # module Foo + # class Bar < StandardError + # def baz + # end + # end + # end + # + # # good + # + # # foo.rb + # class Foo + # class Bar # nested class + # def baz + # end + # end + # end + class ModuleMethodInWrongFile < Base + include Inflections + include RailsAutoloading + + MSG_TEMPLATE = + "In order for Rails autoloading to be able to find and load this file when " \ + "someone calls this method, move the method definition to a file that defines " \ + "the module. This file just uses the module as a namespace for another class " \ + "or module. Method %s should be defined in %s.".freeze + + def on_def(node) + method_name, args, body = *node + on_method_def(node, method_name, args, body) + end + + alias on_defs on_def + + private + + def on_method_def(node, method_name, args, body) + path = node.source_range.source_buffer.name + return unless run_rails_autoloading_cops?(path) + return unless node.parent_module_name + # "#" is the parent module name of a method being defined in an if/unless. + return if node.parent_module_name == "#" + + expected_dir = underscore(normalize_module_name(node.parent_module_name)) + allowable_filenames = expected_dir.split("/").map { |file| "#{file}.rb" } + basename = File.basename(path) + if !allowable_filenames.include?(basename) + parent_module_names = split_modules(node.parent_module_name) + expected_parent_module_file = + "#{parent_module_names.map { |name| underscore(name) }.join("/")}.rb" + add_offense( + node, + message: MSG_TEMPLATE % [method_name, expected_parent_module_file] + ) + end + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/no_timeout.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/no_timeout.rb new file mode 100644 index 0000000..b2c452d --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/no_timeout.rb @@ -0,0 +1,24 @@ +module RuboCop + module Cop + module Airbnb + class NoTimeout < Base + MSG = + 'Do not use Timeout.timeout. In combination with Rails autoloading, ' \ + 'timeout can cause Segmentation Faults in version of Ruby we use. ' \ + 'It can also cause logic errors since it can raise in ' \ + 'any callee scope. Use client library timeouts and monitoring to ' \ + 'ensure proper timing behavior for web requests.'.freeze + RESTRICT_ON_SEND = %i(timeout).freeze + + def_node_matcher :timeout_const?, <<~PATTERN + (const {cbase nil?} :Timeout) + PATTERN + + def on_send(node) + return unless timeout_const?(node.receiver) + add_offense(node, message: MSG) + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/opt_arg_parameters.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/opt_arg_parameters.rb new file mode 100644 index 0000000..c1cf686 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/opt_arg_parameters.rb @@ -0,0 +1,38 @@ +module RuboCop + module Cop + module Airbnb + # Cop to enforce use of options hash over default arguments + # https://github.com/airbnb/ruby#no-default-args + class OptArgParameters < Base + MSG = + 'Do not use default positional arguments. '\ + 'Use keyword arguments or an options hash instead.'.freeze + + def on_args(node) + *but_last, last_arg = *node + + if last_arg && last_arg.blockarg_type? + last_arg = but_last.pop + end + + but_last.each do |arg| + next unless arg.optarg_type? + add_offense(arg, message: MSG) + end + return if last_arg.nil? + + return unless last_arg.optarg_type? + + _arg_name, default_value = *last_arg + if default_value.hash_type? + # asserting default value is empty hash + *key_value_pairs = *default_value + return if key_value_pairs.empty? + end + + add_offense(last_arg, message: MSG) + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/phrase_bundle_keys.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/phrase_bundle_keys.rb new file mode 100644 index 0000000..069db77 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/phrase_bundle_keys.rb @@ -0,0 +1,64 @@ +module RuboCop + module Cop + module Airbnb + # Prefer matching Phrase Bundle and t call keys inside of + # PhraseBundleClasses. + # + # @example + # # bad + # def phrases + # { + # "shortened_key" => t( + # "my_real_translation_key", + # default: 'Does not matter', + # ), + # } + # end + # + # # good + # def phrases + # { + # "my_real_translation_key" => t( + # "my_real_translation_key", + # default: 'Does not matter', + # ), + # } + # end + class PhraseBundleKeys < Base + MESSAGE = + 'Phrase bundle keys should match their translation keys.'.freeze + RESTRICT_ON_SEND = %i(t).freeze + + def on_send(node) + parent = node.parent + if in_phrase_bundle_class?(node) && parent.pair_type? + hash_key = parent.children[0] + unless hash_key.children[0] == node.children[2].children[0] + add_offense(hash_key, message: MESSAGE) + end + end + end + + private + + def in_phrase_bundle_class?(node) + if node.class_type? && !const_phrase_bundle_children(node).empty? + true + elsif node.parent + in_phrase_bundle_class?(node.parent) + else + false + end + end + + def const_phrase_bundle_children(node) + node.children.select do |e| + e.is_a?(Parser::AST::Node) && + e.const_type? && + e.children[1] == :PhraseBundle + end + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/risky_activerecord_invocation.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/risky_activerecord_invocation.rb new file mode 100644 index 0000000..52903c9 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/risky_activerecord_invocation.rb @@ -0,0 +1,56 @@ +module RuboCop + module Cop + module Airbnb + # Disallow ActiveRecord calls that pass interpolated or added strings as an argument. + class RiskyActiverecordInvocation < Base + MSG = 'Passing a string computed by interpolation or addition to an ActiveRecord ' \ + 'method is likely to lead to SQL injection. Use hash or parameterized syntax. For ' \ + 'more information, see ' \ + 'http://guides.rubyonrails.org/security.html#sql-injection-countermeasures and ' \ + 'https://rails-sqli.org/rails3. If you have confirmed with Security that this is a ' \ + 'safe usage of this style, disable this alert with ' \ + '`# rubocop:disable Airbnb/RiskyActiverecordInvocation`.'.freeze + RESTRICT_ON_SEND = [ + :delete_all, + :destroy_all, + :exists?, + :execute, + :find_by_sql, + :group, + :having, + :insert, + :order, + :pluck, + :reorder, + :select, + :select_rows, + :select_values, + :select_all, + :update_all, + :where, + ].freeze + def on_send(node) + return if node.receiver.nil? + if !includes_interpolation?(node.arguments) && !includes_sum?(node.arguments) + return + end + + add_offense(node) + end + + # Return true if the first arg is a :dstr that has non-:str components + def includes_interpolation?(args) + !args.first.nil? && + args.first.type == :dstr && + args.first.each_child_node.any? { |child| child.type != :str } + end + + def includes_sum?(args) + !args.first.nil? && + args.first.type == :send && + args.first.method_name == :+ + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace.rb new file mode 100644 index 0000000..a5a3369 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace.rb @@ -0,0 +1,114 @@ +module RuboCop + module Cop + module Airbnb + # This cop checks for Rspec describe or context method calls under a namespace. + # It can potentially cause autoloading to occur in a different order than it + # would have in development or production. This could cause flaky tests. + # + # @example + # # bad + # + # # spec/foo/bar_spec.rb + # module Foo + # describe Bar do + # end + # end + # + # # good + # + # # spec/foo/bar_spec.rb do + # + # describe Foo::Bar + # end + class RspecDescribeOrContextUnderNamespace < Base + DESCRIBE_OR_CONTEXT_UNDER_NAMESPACE_MSG = + 'Declaring a `module` in a spec can break autoloading because subsequent references ' \ + 'to it will not cause it to be loaded from the app. This could cause flaky tests.'.freeze + + FIX_DESCRIBE_OR_CONTEXT_HELP_MSG = + 'Change `%{describe} %{klass} do` to `%{describe} %{module_name}::%{klass} do`.'.freeze + + FIX_CODE_HELP_MSG = + 'Remove `module %{module_name}` and fix `%{module_name}::CONST` and ' \ + '`%{module_name}.method` calls accordingly.'.freeze + + def_node_matcher :describe_or_context?, + '(send {(const nil? :RSpec) nil?} {:describe :context} ...)'.freeze + + def on_module(node) + path = node.source_range.source_buffer.name + return unless is_spec_file?(path) + + matched_node = search_children_for_describe_or_context(node.children) + return unless matched_node + + method_name = matched_node.method_name + module_name = get_module_name(node) + message = [DESCRIBE_OR_CONTEXT_UNDER_NAMESPACE_MSG] + + described_class = get_described_class(matched_node) + method_parent = get_method_parent(matched_node) + parent_dot_method = method_parent ? "#{method_parent}.#{method_name}" : method_name + if described_class + message << FIX_DESCRIBE_OR_CONTEXT_HELP_MSG % { + describe: parent_dot_method, + klass: described_class, + module_name: module_name, + } + end + + message << FIX_CODE_HELP_MSG % { module_name: module_name } + add_offense(node, message: message.join(' ')) + end + + def search_children_for_describe_or_context(nodes) + blocks = [] + # match nodes for send describe or context + nodes.detect do |node| + next unless node + + if is_block?(node) + blocks << node + next + end + return node if describe_or_context?(node) + end + + # Process child nodes of block + blocks.each do |node| + matched_node = search_children_for_describe_or_context(node.children) + return matched_node if matched_node + end + + nil + end + + def is_spec_file?(path) + path.end_with?('_spec.rb') + end + + def get_module_name(node) + const_node = node.children[0] + return unless const_node + const_node.const_name + end + + def get_described_class(node) + const_node = node.children[2] + return unless const_node + const_node.const_name + end + + def get_method_parent(node) + const_node = node.children[0] + return unless const_node + const_node.const_name + end + + def is_block?(node) + node && [:block, :begin].include?(node.type) + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_environment_modification.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_environment_modification.rb new file mode 100644 index 0000000..55e0326 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_environment_modification.rb @@ -0,0 +1,59 @@ +module RuboCop + module Cop + module Airbnb + # This cop checks how the Rails environment is modified in specs. If an individual method on + # Rails.env is modified multiple environment related branchs could be run down. Rather than + # modifying a single path or setting Rails.env in a way that could bleed into other specs, + # use `stub_env` + # + # @example + # # bad + # + # # spec/foo/bar_spec.rb + # before(:each) do + # allow(Rails.env).to receive(:production).and_return(true) + # end + # + # before(:each) do + # expect(Rails.env).to receive(:production).and_return(true) + # end + # + # before(:each) do + # Rails.env = :production + # end + # + # # good + # + # # spec/foo/bar_spec.rb do + # before(:each) do + # stub_env(:production) + # end + class RspecEnvironmentModification < Base + def_node_matcher :allow_or_expect_rails_env, <<-PATTERN + (send (send nil? {:expect :allow} (send (const nil? :Rails) :env)) :to ...) + PATTERN + + def_node_matcher :stub_rails_env, <<-PATTERN + (send (send (const nil? :Rails) :env) :stub _) + PATTERN + + def_node_matcher :rails_env_assignment, '(send (const nil? :Rails) :env= ...)' + + MESSAGE = "Do not stub or set Rails.env in specs. Use the `stub_env` method instead".freeze + RESTRICT_ON_SEND = %i(to stub env=).freeze + + def on_send(node) + path = node.source_range.source_buffer.name + return unless is_spec_file?(path) + if rails_env_assignment(node) || allow_or_expect_rails_env(node) || stub_rails_env(node) + add_offense(node, message: MESSAGE) + end + end + + def is_spec_file?(path) + path.end_with?('_spec.rb') + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_modifier_conditional.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_modifier_conditional.rb new file mode 100644 index 0000000..714cd9f --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_modifier_conditional.rb @@ -0,0 +1,23 @@ +module RuboCop + module Cop + module Airbnb + # Cop to tackle prevent more complicated modifier if/unless statements + # https://github.com/airbnb/ruby#only-simple-if-unless + class SimpleModifierConditional < Base + MSG = 'Modifier if/unless usage is okay when the body is simple, ' \ + 'the condition is simple, and the whole thing fits on one line. ' \ + 'Otherwise, avoid modifier if/unless.'.freeze + + def_node_matcher :multiple_conditionals?, '(if ({and or :^} ...) ...)' + + def on_if(node) + return unless node.modifier_form? + + if multiple_conditionals?(node) || node.multiline? + add_offense(node) + end + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_unless.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_unless.rb new file mode 100644 index 0000000..7a57f7a --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_unless.rb @@ -0,0 +1,19 @@ +module RuboCop + module Cop + module Airbnb + # Cop to tackle prevent unless statements with multiple conditions + # https://github.com/airbnb/ruby#unless-with-multiple-conditions + class SimpleUnless < Base + MSG = 'Unless usage is okay when there is only one conditional'.freeze + + def_node_matcher :multiple_conditionals?, '(if ({and or :^} ...) ...)' + + def on_if(node) + return unless node.unless? + + add_offense(node) if multiple_conditionals?(node) + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/spec_constant_assignment.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/spec_constant_assignment.rb new file mode 100644 index 0000000..92ff742 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/spec_constant_assignment.rb @@ -0,0 +1,54 @@ +require 'rubocop-rspec' + +module RuboCop + module Cop + module Airbnb + # This cop checks for constant assignment inside of specs + # + # @example + # # bad + # describe Something do + # PAYLOAD = [1, 2, 3] + # end + # + # # good + # describe Something do + # let(:payload) { [1, 2, 3] } + # end + # + # # bad + # describe Something do + # MyClass::PAYLOAD = [1, 2, 3] + # end + # + # # good + # describe Something do + # before { stub_const('MyClass::PAYLOAD', [1, 2, 3]) + # end + class SpecConstantAssignment < Base + MESSAGE = "Defining constants inside of specs can cause spurious behavior. " \ + "It is almost always preferable to use `let` statements, " \ + "anonymous class/module definitions, or stub_const".freeze + + def on_casgn(node) + return unless in_spec_file?(node) + parent_module_name = node.parent_module_name + if parent_module_name && parent_module_name != "Object" + return + end + add_offense(node, message: MESSAGE) + end + + private + + def in_spec_file?(node) + filename = node.location.expression.source_buffer.name + + # For tests, the input is a string + return true if filename == "(string)" + filename.include?("/spec/") + end + end + end + end +end diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/unsafe_yaml_marshal.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/unsafe_yaml_marshal.rb new file mode 100644 index 0000000..34d5991 --- /dev/null +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/unsafe_yaml_marshal.rb @@ -0,0 +1,46 @@ +module RuboCop + module Cop + module Airbnb + # Disallow use of YAML/Marshal methods that can trigger RCE on untrusted input + class UnsafeYamlMarshal < Base + MSG = 'Using unsafe YAML parsing methods on untrusted input can lead ' \ + 'to remote code execution. Use `safe_load`, `parse`, `parse_file`, or ' \ + '`parse_stream` instead'.freeze + RESTRICT_ON_SEND = %i(load load_documents load_file load_stream).freeze + + def on_send(node) + return if node.receiver.nil? + return unless node.receiver.const_type? + + check_yaml(node) + check_marshal(node) + rescue => e + puts e + puts e.backtrace + raise + end + + def check_yaml(node) + const_name = node.receiver.const_name + return unless ['YAML', 'Psych'].include?(const_name) + + message = "Using `#{const_name}.#{node.method_name}` on untrusted input can lead " \ + "to remote code execution. Use `safe_load`, `parse`, `parse_file`, or " \ + "`parse_stream` instead" + + add_offense(node, message: message) + end + + def check_marshal(node) + return unless node.receiver.const_name == 'Marshal' + return unless node.method?(:load) + + message = 'Using `Marshal.load` on untrusted input can lead to remote code execution. ' \ + 'Restructure your code to not use Marshal' + + add_offense(node, message: message) + end + end + end + end +end diff --git a/rubocop-airbnb/rubocop-airbnb.gemspec b/rubocop-airbnb/rubocop-airbnb.gemspec new file mode 100644 index 0000000..95d5443 --- /dev/null +++ b/rubocop-airbnb/rubocop-airbnb.gemspec @@ -0,0 +1,38 @@ +$LOAD_PATH.unshift File.expand_path('../lib', __FILE__) +require 'rubocop/airbnb/version' + +Gem::Specification.new do |spec| + spec.name = 'rubocop-airbnb' + spec.summary = 'Custom code style checking for Airbnb.' + spec.description = <<-EOF + A plugin for RuboCop code style enforcing & linting tool. It includes Rubocop configuration + used at Airbnb and a few custom rules that have cause internal issues at Airbnb but are not + supported by core Rubocop. + EOF + spec.authors = ['Airbnb Engineering'] + spec.email = ['rubocop@airbnb.com'] + spec.homepage = 'https://github.com/airbnb/ruby' + spec.license = 'MIT' + spec.version = RuboCop::Airbnb::VERSION + spec.platform = Gem::Platform::RUBY + spec.required_ruby_version = '>= 2.7' + + spec.require_paths = ['lib'] + spec.files = Dir[ + '{config,lib,spec}/**/*', + '*.md', + '*.gemspec', + 'Gemfile', + ] + + spec.metadata['default_lint_roller_plugin'] = 'RuboCop::Airbnb::Plugin' + + spec.add_dependency('lint_roller', '~> 1.1') + spec.add_dependency('rubocop', '~> 1.76') + spec.add_dependency('rubocop-capybara', '~> 2.22') + spec.add_dependency('rubocop-factory_bot', '~> 2.27') + spec.add_dependency('rubocop-performance', '~> 1.24') + spec.add_dependency('rubocop-rails', '~> 2.30') + spec.add_dependency('rubocop-rspec', '~> 3.5') + spec.add_development_dependency('rspec', '~> 3.5') +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file_spec.rb new file mode 100644 index 0000000..78e1073 --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file_spec.rb @@ -0,0 +1,141 @@ +describe RuboCop::Cop::Airbnb::ClassOrModuleDeclaredInWrongFile, :config do + let(:config) do + RuboCop::Config.new( + { + "Rails" => { + "Enabled" => true, + }, + } + ) + end + + # Put source in a directory under /tmp because this cop cares about the filename + # but not the parent dir name. + let(:tmpdir) { Dir.mktmpdir } + let(:models_dir) do + gemfile = File.new("#{tmpdir}/Gemfile", "w") + gemfile.close + FileUtils.mkdir_p("#{tmpdir}/app/models").first + end + + after do + FileUtils.rm_rf tmpdir + end + + it 'rejects if class declaration is in a file with non-matching name' do + File.open "#{models_dir}/qux.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this class/module, move its definition to a file that matches its name. Module Foo should be defined in foo.rb. + module Bar + ^^^^^^^^^^ In order for Rails autoloading [...] + class Baz + ^^^^^^^^^ In order for Rails autoloading [...] + end + end + end + RUBY + end + end + + it 'rejects if class declaration is in a file with matching name but wrong parent dir' do + File.open "#{models_dir}/baz.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ In order for Rails autoloading [...] + module Bar + ^^^^^^^^^^ In order for Rails autoloading [...] + class Baz + ^^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this class/module, move its definition to a file that matches its name. Class Baz should be defined in foo/bar/baz.rb. + end + end + end + RUBY + end + end + + it 'accepts if class declaration is in a file with matching name and right parent dir' do + FileUtils.mkdir_p "#{models_dir}/foo/bar" + File.open "#{models_dir}/foo/bar/baz.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo + module Bar + class Baz + end + end + end + RUBY + end + end + + it 'rejects if class declaration is in wrong dir and parent module uses ::' do + FileUtils.mkdir_p "#{models_dir}/bar" + File.open "#{models_dir}/bar/baz.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo::Bar + ^^^^^^^^^^^^^^^ In order for Rails autoloading [...] + class Baz + ^^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this class/module, move its definition to a file that matches its name. Class Baz should be defined in foo/bar/baz.rb. + end + end + RUBY + end + end + + it 'accepts if class declaration is in a file with matching name and parent module uses ::' do + FileUtils.mkdir_p "#{models_dir}/foo/bar" + File.open "#{models_dir}/foo/bar/baz.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo::Bar + class Baz + end + end + RUBY + end + end + + it 'accepts class declaration where the containing class uses an acronym' do + FileUtils.mkdir_p "#{models_dir}/csv_foo" + File.open "#{models_dir}/csv_foo/baz.rb", "w" do |file| + expect_no_offenses(<<~RUBY) + module CSVFoo + class Baz + end + end + RUBY + end + end + + it 'ignores class/module declaration in a rake task' do + File.open "#{models_dir}/foo.rake", "w" do |file| + expect_no_offenses(<<~RUBY, file) + class Baz + end + RUBY + end + end + + it 'suggests moving error classes into the file that defines the owning scope' do + File.open "#{models_dir}/bar.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ In order for Rails autoloading [...] + class BarError < StandardError; end + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this class, move its definition to a file that defines the owning module. Class BarError should be defined in foo.rb. + end + RUBY + end + end + + it 'recognizes error class based on the superclass name' do + File.open "#{models_dir}/bar.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ In order for Rails autoloading [...] + class Bar < StandardError; end + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this class, move its definition to a file that defines the owning module. Class Bar should be defined in foo.rb. + end + RUBY + end + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/const_assigned_in_wrong_file_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/const_assigned_in_wrong_file_spec.rb new file mode 100644 index 0000000..216d57a --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/const_assigned_in_wrong_file_spec.rb @@ -0,0 +1,135 @@ +describe RuboCop::Cop::Airbnb::ConstAssignedInWrongFile, :config do + let(:config) do + RuboCop::Config.new( + { + "Rails" => { + "Enabled" => true, + }, + } + ) + end + + # Put source in a directory under /tmp because this cop cares about the filename + # but not the parent dir name. + let(:tmpdir) { Dir.mktmpdir } + let(:models_dir) do + gemfile = File.new("#{tmpdir}/Gemfile", "w") + gemfile.close + FileUtils.mkdir_p("#{tmpdir}/app/models").first + end + + after do + FileUtils.rm_rf tmpdir + end + + it 'rejects if const assignment is in a file with non-matching name' do + File.open "#{models_dir}/qux.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo + module Bar + BAZ = 42 + ^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this const, move the const assignment to a file that defines the owning module. Const BAZ should be defined in foo/bar.rb. + end + end + RUBY + end + end + + it 'rejects if const assignment is in a file with matching name but wrong parent dir' do + File.open "#{models_dir}/bar.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo + module Bar + BAZ = 42 + ^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this const, move the const assignment to a file that defines the owning module. Const BAZ should be defined in foo/bar.rb. + end + end + RUBY + end + end + + it 'accepts if const assignment is in a file with matching name and right parent dir' do + FileUtils.mkdir "#{models_dir}/foo" + File.open "#{models_dir}/foo/bar.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo + module Bar + BAZ = 42 + end + end + RUBY + end + end + + it 'accepts if const assignment is in a file with matching name and right parent dir' \ + 'and parent modules were defined on a single line' do + FileUtils.mkdir "#{models_dir}/foo" + File.open "#{models_dir}/foo/bar.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo::Bar + BAZ = 42 + end + RUBY + end + end + + it 'accepts if const assignment is in a file whose name matches the const and right parent dir' do + FileUtils.mkdir_p "#{models_dir}/foo/bar" + File.open "#{models_dir}/foo/bar/baz.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo + module Bar + BAZ = 42 + end + end + RUBY + end + end + + it 'ignores if const assignment is assigning something in another scope' do + File.open "#{models_dir}/foo.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo + Bar::BAZ = 42 + end + RUBY + end + end + + it 'accepts const assignment where the containing class uses an acronym' do + File.open "#{models_dir}/csv_foo.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module CSVFoo + BAZ = 42 + end + RUBY + end + end + + it 'suggests moving a global const into a namespace' do + File.open "#{models_dir}/bar.rb", "w" do |file| + expect_offense(<<~RUBY, file) + FOO = 42 + ^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this const, move the const assignment to a file that defines the owning module. Const FOO should be moved into a namespace or defined in foo.rb. + RUBY + end + end + + it 'ignores const assignment in global namespace in a rake task' do + File.open "#{models_dir}/foo.rake", "w" do |file| + expect_no_offenses(<<~RUBY, file) + FOO = 42 + RUBY + end + end + + it 'ignores const assignment in a class in a rake task' do + File.open "#{models_dir}/foo.rake", "w" do |file| + expect_no_offenses(<<~RUBY, file) + class Baz + FOO = 42 + end + RUBY + end + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/continuation_slash_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/continuation_slash_spec.rb new file mode 100644 index 0000000..4728dd2 --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/continuation_slash_spec.rb @@ -0,0 +1,127 @@ +describe RuboCop::Cop::Airbnb::ContinuationSlash, :config do + it 'rejects continuations used to continue a method call with trailing dot' do + expect_offense(<<~RUBY) + User. \\ + ^^^^^^^ Slash continuation should be reserved [...] + first_name + RUBY + end + + context 'on assignment' do + it 'rejects on constant assignment' do + expect_offense(<<~RUBY) + CONSTANT = "I am a string that \\ + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Slash continuation should be reserved [...] + spans multiple lines" + RUBY + end + + it 'rejects on local variable assignment' do + expect_offense(<<~RUBY) + variable = "I am a string that \\ + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Slash continuation should be reserved [...] + spans multiple lines" + RUBY + end + + it 'rejects on @var assignment' do + expect_offense(<<~RUBY) + class SomeClass + @class_var = "I am a string that \\ + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Slash continuation should be reserved [...] + spans multiple lines" + end + RUBY + end + + it 'rejects on @@var assignment' do + expect_offense(<<~RUBY) + class SomeClass + @@class_var = "I am a string that \\ + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Slash continuation should be reserved [...] + spans multiple lines" + end + RUBY + end + end + + context 'in conditional continuation' do + it 'rejects if with continuation \\ before operator' do + expect_offense(<<~RUBY) + if true \\ + ^^^^^^^^^ Slash continuation should be reserved [...] + && false + return false + end + RUBY + end + + it 'rejects unless with continuation \\' do + expect_offense(<<~RUBY) + unless true \\ + ^^^^^^^^^^^^^ Slash continuation should be reserved [...] + && false + return false + end + RUBY + end + + it 'rejects if with continuation \\ after operator' do + expect_offense(<<~RUBY) + if true || \\ + ^^^^^^^^^^^^ Slash continuation should be reserved [...] + false + return false + end + RUBY + end + end + + context 'open string continuation' do + it 'rejects contination with space before \\' do + expect_offense(<<~RUBY) + I18n.t("I am a string that \\ + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Slash continuation should be reserved [...] + spans multiple lines") + RUBY + end + + it 'rejects contination with no space before \\' do + expect_offense(<<~RUBY) + I18n.t(\'I am a string that \\ + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Slash continuation should be reserved [...] + spans multiple lines\') + RUBY + end + end + + context 'closed string continuation' do + it 'allows double quote string with no space before \\' do + expect_no_offenses(<<~RUBY) + I18n.t("I am a string that "\\ + "spans multiple lines") + RUBY + end + + it 'allows double quote string with space before \\' do + expect_no_offenses(<<~RUBY) + I18n.t("I am a string that " \\ + "spans multiple lines") + RUBY + end + + it 'allows single quote string with no space before \\' do + expect_no_offenses(<<~RUBY) + I18n.t(\'I am a string that \'\\ + \'spans multiple lines\') + RUBY + end + + it 'allows single quote string with space before \\' do + expect_no_offenses(<<~RUBY) + I18n.t(\'I am a string that \' \\ + \'spans multiple lines\') + RUBY + end + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/default_scope_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/default_scope_spec.rb new file mode 100644 index 0000000..63eec89 --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/default_scope_spec.rb @@ -0,0 +1,31 @@ +describe RuboCop::Cop::Airbnb::DefaultScope, :config do + it 'rejects with default_scopes' do + expect_offense(<<~RUBY) + # encoding: UTF-8 + module SurveyQuestion + class Host < PhraseBundle + db_magic :connection => AIRMISC_MASTER, + :slaves => AIRMISC_DB_SLAVES, + :force_slave_reads => FORCE_SLAVE_READS + + default_scope where(active: true) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid `default_scope`. [...] + + end + end + RUBY + end + + it 'passes when there is no default_scope' do + expect_no_offenses(<<~RUBY) + # encoding: UTF-8 + module SurveyQuestion + class Host < PhraseBundle + db_magic :connection => AIRMISC_MASTER, + :slaves => AIRMISC_DB_SLAVES, + :force_slave_reads => FORCE_SLAVE_READS + end + end + RUBY + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_attr_references_class_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_attr_references_class_spec.rb new file mode 100644 index 0000000..68d2842 --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_attr_references_class_spec.rb @@ -0,0 +1,120 @@ +describe RuboCop::Cop::Airbnb::FactoryAttrReferencesClass, :config do + it 'rejects with `attr_name CONST_NAME` in a factory' do + expect_offense(<<~RUBY) + factory :reservation2 do + status Reservation2::STATUS_NEW + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + RUBY + end + + it 'passes with `attr_name { CONST_NAME }` in a factory' do + expect_no_offenses(<<~RUBY) + factory :reservation2 do + status { Reservation2::STATUS_NEW } + end + RUBY + end + + it 'rejects with `attr_name [CONST_NAME]`' do + expect_offense(<<~RUBY) + factory :reservation2 do + statuses [Reservation2::STATUS_NEW] + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + RUBY + end + + it 'passes with `attr_name { [CONST_NAME] }`' do + expect_no_offenses(<<~RUBY) + factory :reservation2 do + statuses { [Reservation2::STATUS_NEW] } + end + RUBY + end + + it 'rejects with `attr_name [[CONST_NAME]]`' do + expect_offense(<<~RUBY) + factory :reservation2 do + statuses [[Reservation2::STATUS_NEW]] + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + RUBY + end + + it 'passes with `attr_name { [[CONST_NAME]] }`' do + expect_no_offenses(<<~RUBY) + factory :reservation2 do + statuses { [[Reservation2::STATUS_NEW]] } + end + RUBY + end + + it 'rejects with `attr_name({ ConstName => something })' do + expect_offense(<<~RUBY) + factory :reservation2 do + status_names({ Reservation2::STATUS_NEW => "new" }) + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + RUBY + end + + it 'passes with `attr_name { { ConstName => something } }`' do + expect_no_offenses(<<~RUBY) + factory :reservation2 do + status_names { { Reservation2::STATUS_NEW => "new" } } + end + RUBY + end + + it 'rejects with `attr_name ConstName[:symbol]`' do + expect_offense(<<~RUBY) + factory :airlock_rule do + stickiness Airlock::STICKINESS[:user] + ^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + RUBY + end + + it 'passes with `attr_name { ConstName[:symbol] }`' do + expect_no_offenses(<<~RUBY) + factory :airlock_rule do + stickiness { Airlock::STICKINESS[:user] } + end + RUBY + end + + it 'rejects even if the const is not the first attr' do + expect_offense(<<~RUBY) + factory :reservation2 do + trait :accepted do + cancel_policy Hosting::CANCEL_FLEXIBLE + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + status Reservation2::STATUS_NEW + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + end + RUBY + end + + it 'rejects with `attr_name CONST_NAME` in a trait' do + expect_offense(<<~RUBY) + factory :reservation2 do + trait :accepted do + status Reservation2::STATUS_NEW + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + end + RUBY + end + + it 'passes with `attr_name { CONST_NAME }` in a trait' do + expect_no_offenses(<<~RUBY) + factory :reservation2 do + trait :accepted do + status { Reservation2::STATUS_NEW } + end + end + RUBY + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_class_use_string_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_class_use_string_spec.rb new file mode 100644 index 0000000..4dbe899 --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_class_use_string_spec.rb @@ -0,0 +1,18 @@ +describe RuboCop::Cop::Airbnb::FactoryClassUseString, :config do + it 'rejects with :class => Model' do + expect_offense(<<~RUBY) + factory :help_answer, :class => Help::Answer do + ^^^^^^^^^^^^^^^^^^^^^^ Instead of :class => MyClass, use :class => "MyClass". [...] + text { Faker::Company.name } + end + RUBY + end + + it 'passes with :class => "Model"' do + expect_no_offenses(<<~RUBY) + factory :help_answer, :class => "Help::Answer" do + text { Faker::Company.name } + end + RUBY + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/mass_assignment_accessible_modifier_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/mass_assignment_accessible_modifier_spec.rb new file mode 100644 index 0000000..70ea76e --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/mass_assignment_accessible_modifier_spec.rb @@ -0,0 +1,23 @@ +describe RuboCop::Cop::Airbnb::MassAssignmentAccessibleModifier, :config do + it 'rejects when accessible= is called' do + expect_offense(<<~RUBY) + def some_method + user = User.new + user.accessible = [:first_name, :last_name] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do no override and objects mass assignment restrictions. + user.update_attributes(:first_name => "Walter", :last_name => "Udoing") + end + RUBY + end + + it 'accepts when accessible= is not called' do + expect_no_offenses(<<~RUBY) + def some_method + user = User.new + user.first_name = "Walter" + user.last_name = "Udoing" + user.save! + end + RUBY + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/module_method_in_wrong_file_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/module_method_in_wrong_file_spec.rb new file mode 100644 index 0000000..b053a67 --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/module_method_in_wrong_file_spec.rb @@ -0,0 +1,142 @@ +describe RuboCop::Cop::Airbnb::ModuleMethodInWrongFile, :config do + let(:config) do + RuboCop::Config.new( + { + "Rails" => { + "Enabled" => true, + }, + } + ) + end + + # Put source in a directory under /tmp because this cop cares about the filename + # but not the parent dir name. + let(:tmpdir) { Dir.mktmpdir } + let(:models_dir) do + gemfile = File.new("#{tmpdir}/Gemfile", "w") + gemfile.close + FileUtils.mkdir_p("#{tmpdir}/app/models").first + end + + after do + FileUtils.rm_rf tmpdir + end + + it 'rejects if module method is in file with non-matching name' do + File.open "#{models_dir}/bar.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Hello + module Foo + def baz + ^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone calls this method, move the method definition to a file that defines the module. This file just uses the module as a namespace for another class or module. Method baz should be defined in hello/foo.rb. + 42 + end + end + end + RUBY + end + end + + it 'accepts if module method is in file with matching name' do + File.open "#{models_dir}/foo.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo + def baz + 42 + end + end + RUBY + end + end + + it 'rejects with "self." static methods and a non-matching name' do + File.open "#{models_dir}/bar.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo + def self.baz + ^^^^^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone calls this method, move the method definition to a file that defines the module. This file just uses the module as a namespace for another class or module. Method (self) should be defined in foo.rb. + 42 + end + end + RUBY + end + end + + it 'accepts with "self." static methods and a matching name' do + File.open "#{models_dir}/foo.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo + def self.baz + 42 + end + end + RUBY + end + end + + xit 'rejects with "<<" static methods and a non-matching name' do + File.open "#{models_dir}/bar.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo + class << self + def baz + ^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone calls this method, move the method definition to a file that defines the module. This file just uses the module as a namespace for another class or module. Method baz should be defined in foo.rb. + 42 + end + end + end + RUBY + end + end + + it 'accepts with "<<" static methods and a matching name' do + File.open "#{models_dir}/foo.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo + class << self + def baz + 42 + end + end + end + RUBY + end + end + + it 'accepts methods defined in a nested class' do + File.open "#{models_dir}/foo.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo + class Bar + def qux + end + end + end + RUBY + end + end + + it 'accepts methods where the containing class uses an acronym' do + File.open "#{models_dir}/csv_foo.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module CSVFoo + def baz + 42 + end + end + RUBY + end + end + + it 'ignores rake tasks' do + File.open "#{models_dir}/bar.rake", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Hello + def baz + 42 + end + end + RUBY + end + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/no_timeout_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/no_timeout_spec.rb new file mode 100644 index 0000000..631021a --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/no_timeout_spec.rb @@ -0,0 +1,35 @@ +describe RuboCop::Cop::Airbnb::NoTimeout, :config do + context 'send' do + it 'rejects Timeout.timeout' do + expect_offense(<<~RUBY) + def some_method(a) + Timeout.timeout(5) do + ^^^^^^^^^^^^^^^^^^ Do not use Timeout.timeout. [...] + some_other_method(a) + end + end + RUBY + end + + it 'rejects ::Timeout.timeout' do + expect_offense(<<~RUBY) + def some_method(a) + ::Timeout.timeout(5) do + ^^^^^^^^^^^^^^^^^^^^ Do not use Timeout.timeout. [...] + some_other_method(a) + end + end + RUBY + end + + it 'accepts foo.timeout' do + expect_no_offenses(<<~RUBY) + def some_method(a) + foo.timeout do + some_other_method(a) + end + end + RUBY + end + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/opt_arg_parameter_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/opt_arg_parameter_spec.rb new file mode 100644 index 0000000..bafb659 --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/opt_arg_parameter_spec.rb @@ -0,0 +1,76 @@ +describe RuboCop::Cop::Airbnb::OptArgParameters, :config do + it 'allows method with no parameters' do + expect_no_offenses(<<~RUBY) + def my_method + end + RUBY + end + + it 'allows method with one parameter' do + expect_no_offenses(<<~RUBY) + def my_method(params) + end + RUBY + end + + it 'allows method with one parameter with a default hash value' do + expect_no_offenses(<<~RUBY) + def my_method(params = {}) + end + RUBY + end + + it 'allows method named default parameters' do + expect_no_offenses(<<~RUBY) + def my_method(a, b, c: 5, d: 6) + end + RUBY + end + + it 'allows method with multiple parameter with a default hash value' do + expect_no_offenses(<<~RUBY) + def my_method(a, b, c, params = {}) + end + RUBY + end + + it 'allows method with default parameter before block parameter' do + expect_no_offenses(<<~RUBY) + def my_method(a, b, c, params = {}, &block) + end + RUBY + end + + it 'rejects method with a default parameter other than the last non-block parameter' do + expect_offense(<<~RUBY) + def my_method(a, b = nil, c, &block) + ^^^^^^^ Do not use default positional arguments. [...] + end + RUBY + end + + it 'rejects method with a default parameter other than the last parameter' do + expect_offense(<<~RUBY) + def my_method(a, b = 5, c = nil, params = {}) + ^^^^^ Do not use default positional arguments. [...] + ^^^^^^^ Do not use default positional arguments. [...] + end + RUBY + end + + it 'rejects method where last parameter has a default value of nil' do + expect_offense(<<~RUBY) + def my_method(a, b, c, params = nil) + ^^^^^^^^^^^^ Do not use default positional arguments. [...] + end + RUBY + end + + it 'rejects method where last parameter has a default value of a constant' do + expect_offense(<<~RUBY) + def my_method(a, b, c, params = Constants::A_CONSTANT) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not use default positional arguments. [...] + end + RUBY + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/phrase_bundle_keys_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/phrase_bundle_keys_spec.rb new file mode 100644 index 0000000..6323d4f --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/phrase_bundle_keys_spec.rb @@ -0,0 +1,59 @@ +describe RuboCop::Cop::Airbnb::PhraseBundleKeys, :config do + it 'generates offenses for mismatched keys in PhraseBundle classes' do + expect_offense(< t( + ^^^^^^^^^^^^^^^ Phrase bundle keys should match their translation keys. + "my_real_translation_key", + default: 'Does not matter', + ), + } + end + end +end +EOS + end + + it 'does not generate offenses for matching keys in PhraseBundle classes' do + expect_no_offenses(< t( + "my_real_translation_key", + default: 'Does not matter', + ), + } + end + end +end +EOS + end + + it 'passes on t calls that are not in PhraseBundle classes' do + expect_no_offenses(< t( + "my_real_translation_key", + default: 'Does not matter', + ), + } + end + end +end +EOS + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/risky_activerecord_invocation_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/risky_activerecord_invocation_spec.rb new file mode 100644 index 0000000..f51ebd0 --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/risky_activerecord_invocation_spec.rb @@ -0,0 +1,40 @@ +describe RuboCop::Cop::Airbnb::RiskyActiverecordInvocation, :config do + it "allows where statement that's a hash" do + expect_no_offenses(<<~RUBY) + Users.where({:name => "Bob"}) + RUBY + end + + it "allows where statement that's a flat string" do + expect_no_offenses('Users.where("age = 24")') + end + + it "allows a multiline where statement" do + expect_no_offenses("Users.where(\"age = 24 OR \" \\\n\"age = 25\")") + end + + it "allows interpolation in subsequent arguments to where" do + expect_no_offenses('Users.where("name like ?", "%#{name}%")') + end + + it "disallows interpolation in where statements" do + expect_offense(<<~RUBY) + Users.where("name = \#{username}") + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Passing a string computed by interpolation or addition to an ActiveRecord method is likely to lead to SQL injection. [...] + RUBY + end + + it "disallows addition in where statements" do + expect_offense(<<~RUBY) + Users.where("name = " + username) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Passing a string computed by interpolation or addition to an ActiveRecord method is likely to lead to SQL injection. [...] + RUBY + end + + it "disallows interpolation in order statements" do + expect_offense(<<~RUBY) + Users.where("age = 24").order("name \#{sortorder}") + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Passing a string computed by interpolation or addition to an ActiveRecord method is likely to lead to SQL injection. [...] + RUBY + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace_spec.rb new file mode 100644 index 0000000..febe2b8 --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace_spec.rb @@ -0,0 +1,206 @@ +describe RuboCop::Cop::Airbnb::RspecDescribeOrContextUnderNamespace, :config do + let(:tmpdir) { Dir.mktmpdir } + let(:models_spec_dir) do + FileUtils.mkdir_p("#{tmpdir}/spec/models").first + end + + after do + FileUtils.rm_rf tmpdir + end + + shared_examples 'rspec namespacing rule' do + context 'under a namespace' do + it 'rejects without change message when argument is a string' do + FileUtils.mkdir_p "#{models_spec_dir}/foo" + File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ Declaring a `module` in a spec can break autoloading because subsequent references to it will not cause it to be loaded from the app. This could cause flaky tests. Remove `module Foo` and fix `Foo::CONST` and `Foo.method` calls accordingly. + #{method} 'SomeClass' do + it "passes" do + expect(true).to be_true + end + end + end + RUBY + end + end + + it 'rejects with change message when argument is a constant' do + FileUtils.mkdir_p "#{models_spec_dir}/foo" + File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ Declaring a `module` in a spec can break autoloading because subsequent references to it will not cause it to be loaded from the app. This could cause flaky tests. Change `#{method} SomeClass do` to `#{method} Foo::SomeClass do`. Remove `module Foo` and fix `Foo::CONST` and `Foo.method` calls accordingly. + #{method} SomeClass do + it "passes" do + expect(true).to be_true + end + end + end + RUBY + end + end + + it 'rejects when within a nested block' do + FileUtils.mkdir_p "#{models_spec_dir}/foo" + File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ Declaring a `module` in a spec can break autoloading because subsequent references to it will not cause it to be loaded from the app. This could cause flaky tests. Change `#{method} SomeClass do` to `#{method} Foo::SomeClass do`. Remove `module Foo` and fix `Foo::CONST` and `Foo.method` calls accordingly. + 1.times do + #{method} SomeClass do + it "passes" do + expect(true).to be_true + end + end + end + end + RUBY + end + end + + it 'rejects when a block is executed prior' do + FileUtils.mkdir_p "#{models_spec_dir}/foo" + File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ Declaring a `module` in a spec can break autoloading because subsequent references to it will not cause it to be loaded from the app. This could cause flaky tests. Change `#{method} SomeClass do` to `#{method} Foo::SomeClass do`. Remove `module Foo` and fix `Foo::CONST` and `Foo.method` calls accordingly. + [1,2,3].each do + something + end + + #{method} SomeClass do + it "passes" do + expect(true).to be_true + end + end + end + RUBY + end + end + end + end + + context 'describe' do + let!(:method) { 'describe' } + + it_behaves_like 'rspec namespacing rule' + end + + context 'RSpec.describe' do + let!(:method) { 'RSpec.describe' } + + it_behaves_like 'rspec namespacing rule' + end + + context 'context' do + let!(:method) { 'context' } + + it_behaves_like 'rspec namespacing rule' + end + + context 'RSpec.context' do + let!(:method) { 'RSpec.context' } + + it_behaves_like 'rspec namespacing rule' + end + + it 'accepts if file is not a spec file' do + FileUtils.mkdir_p "#{models_spec_dir}/foo" + File.open "#{models_spec_dir}/foo/some_class.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo + RSpec.describe "SomeClass" do + it "passes" do + expect(true).to be_true + end + end + end + RUBY + end + end + + it 'accepts if not under a module' do + FileUtils.mkdir_p "#{models_spec_dir}/foo" + File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + RSpec.describe "SomeClass" do + it "passes" do + expect(true).to be_true + end + end + RUBY + end + end + + it 'accepts if not describe or context' do + FileUtils.mkdir_p "#{models_spec_dir}/foo" + File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo + not_describe_or_context "SomeClass" do + it "passes" do + expect(true).to be_true + end + end + end + RUBY + end + end + + it 'accepts an empty module' do + FileUtils.mkdir_p "#{models_spec_dir}/foo" + File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + RSpec.describe "SomeClass" do + module Bar; end + + it "passes" do + expect(true).to be_true + end + end + RUBY + end + end + + it 'accepts a module with code' do + FileUtils.mkdir_p "#{models_spec_dir}/foo" + File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + RSpec.describe "SomeClass" do + module Bar + def self.foo + end + foo + end + + it "passes" do + expect(true).to be_true + end + end + RUBY + end + end + + it 'accepts when describe or context to be called when class or module is not RSpec' do + FileUtils.mkdir_p "#{models_spec_dir}/foo" + File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| + expect_no_offenses(<<~RUBY, file) + module Foo + Bar.describe "SomeClass" do + it "passes" do + expect(true).to be_true + end + end + + Bar.context "SomeClass" do + it "passes" do + expect(true).to be_true + end + end + end + RUBY + end + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_environment_modification_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_environment_modification_spec.rb new file mode 100644 index 0000000..ac27ff2 --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_environment_modification_spec.rb @@ -0,0 +1,54 @@ +describe RuboCop::Cop::Airbnb::RspecEnvironmentModification, :config do + before(:each) do + allow(cop).to receive(:is_spec_file?).and_return(true) + end + + it 'does not allow assignment of Rails.env' do + expect_offense(<<~RUBY) + Rails.env = :production + ^^^^^^^^^^^^^^^^^^^^^^^ Do not stub or set Rails.env in specs. [...] + RUBY + end + + it 'allows assignment of Rails.env when not in spec' do + allow(cop).to receive(:is_spec_file?).and_return(false) + expect_no_offenses(<<~RUBY) + Rails.env = :production + RUBY + end + + it 'rejects allow style stubbing of Rails.env' do + expect_offense(<<~RUBY) + def some_method(a) + allow(Rails.env).to receive(:production?) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not stub or set Rails.env in specs. [...] + end + RUBY + end + + it 'rejects expect style stubbing of Rails.env' do + expect_offense(<<~RUBY) + def some_method(a) + expect(Rails.env).to receive(:production?) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not stub or set Rails.env in specs. [...] + end + RUBY + end + + it 'rejects .stub stubbing of Rails.env' do + expect_offense(<<~RUBY) + def some_method(a) + Rails.env.stub(:production) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not stub or set Rails.env in specs. [...] + end + RUBY + end + + it 'allows stub_env' do + expect_no_offenses(<<~RUBY) + def some_method(a) + stub_env(:production) + end + RUBY + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_modifier_conditional_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_modifier_conditional_spec.rb new file mode 100644 index 0000000..c9be038 --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_modifier_conditional_spec.rb @@ -0,0 +1,98 @@ +describe RuboCop::Cop::Airbnb::SimpleModifierConditional, :config do + context 'multiple conditionals' do + it 'rejects with modifier if with multiple conditionals' do + expect_offense(<<~RUBY) + return true if some_method == 0 || another_method + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + RUBY + end + + it 'rejects with modifier unless with multiple conditionals' do + expect_offense(<<~RUBY) + return true unless true && false + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + RUBY + end + + it 'allows with modifier if operator conditional' do + expect_no_offenses(<<~RUBY) + return true if some_method == 0 + RUBY + end + + it 'allows with modifier if with single conditional' do + expect_no_offenses(<<~RUBY) + return true if some_method == 0 + return true if another_method + RUBY + end + + it 'allows with modifier if and unless with single conditional ' do + expect_no_offenses(<<~RUBY) + return true if some_method + return true unless another_method > 5 + RUBY + end + + it 'allows multiple conditionals in block form' do + expect_no_offenses(<<~RUBY) + if some_method == 0 && another_method > 5 || true || false + return true + end + RUBY + end + end + + context 'multiple lines' do + it 'rejects modifier conditionals that span multiple lines' do + expect_offense(<<~RUBY) + return true if true || + ^^^^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + false + return true unless true || + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + false + RUBY + end + + it 'rejects with modifier if with method that spans multiple lines' do + expect_offense(<<~RUBY) + return true if some_method(param1, + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + param2, + param3) + return true unless some_method(param1, + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + param2, + param3) + RUBY + end + + it 'rejects inline if/unless after a multiline statement' do + expect_offense(<<~RUBY) + return some_method( + ^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + param1, + param2, + param3 + ) if another_method == 0 + return some_method( + ^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + param1, + param2, + param3 + ) unless another_method == 0 + RUBY + end + + it 'allows multline conditionals in block form' do + expect_no_offenses(<<~RUBY) + if some_method(param1, + param2, + parma3) + return true + end + RUBY + end + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_unless_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_unless_spec.rb new file mode 100644 index 0000000..f22fd3f --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_unless_spec.rb @@ -0,0 +1,26 @@ +describe RuboCop::Cop::Airbnb::SimpleUnless, :config do + it 'rejects unless with multiple conditionals' do + expect_offense(<<~RUBY) + unless boolean_condition || another_method + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unless usage is okay when there is only one conditional + return true + end + RUBY + end + + it 'allows if with multiple conditionals' do + expect_no_offenses(<<~RUBY) + if boolean_condition || another_method + return true + end + RUBY + end + + it 'allows with modifier if operator conditional' do + expect_no_offenses(<<~RUBY) + unless boolean_condition + return true + end + RUBY + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/spec_constant_assignment_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/spec_constant_assignment_spec.rb new file mode 100644 index 0000000..582a8eb --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/spec_constant_assignment_spec.rb @@ -0,0 +1,62 @@ +describe RuboCop::Cop::Airbnb::SpecConstantAssignment, :config do + it 'rejects constant definition inside of a describe block' do + expect_offense(<<~RUBY) + describe Someclass do + CONSTANT = 5 + ^^^^^^^^^^^^ Defining constants inside of specs can cause spurious behavior. [...] + end + RUBY + end + + it 'allows constant defined inside of a module' do + expect_no_offenses(<<~RUBY) + module Someclass + CONSTANT = 5 + end + RUBY + end + + it 'rejects constant defined in global space' do + expect_offense(<<~RUBY) + CONSTANT = 5 + ^^^^^^^^^^^^ Defining constants inside of specs can cause spurious behavior. [...] + RUBY + end + + it 'rejects constant assignment inside a before block' do + expect_offense(<<~RUBY) + describe Someclass do + before { CONSTANT = 5 } + ^^^^^^^^^^^^ Defining constants inside of specs can cause spurious behavior. [...] + end + RUBY + end + + it 'rejects namespaced constant assignment inside a before block' do + expect_offense(<<~RUBY) + describe Someclass do + before { MyModule::CONSTANT = 5 } + ^^^^^^^^^^^^^^^^^^^^^^ Defining constants inside of specs can cause spurious behavior. [...] + end + RUBY + end + + it 'rejects constant assignment inside it block' do + expect_offense(<<~RUBY) + describe Someclass do + it "tests stuff" do + CONSTANT = 5 + ^^^^^^^^^^^^ Defining constants inside of specs can cause spurious behavior. [...] + end + end + RUBY + end + + it 'allows let statements that do not assign constants' do + expect_no_offenses(<<~RUBY) + describe Someclass do + let(:constant) { 5 } + end + RUBY + end +end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/unsafe_yaml_marshal_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/unsafe_yaml_marshal_spec.rb new file mode 100644 index 0000000..33737df --- /dev/null +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/unsafe_yaml_marshal_spec.rb @@ -0,0 +1,38 @@ +describe RuboCop::Cop::Airbnb::UnsafeYamlMarshal, :config do + context 'send' do + it 'rejects YAML.load' do + expect_offense(<<~RUBY) + def some_method(a) + YAML.load(a) + ^^^^^^^^^^^^ Using `YAML.load` on untrusted input [...] + end + RUBY + end + + it 'rejects Psych.load' do + expect_offense(<<~RUBY) + def some_method(a) + Psych.load(a) + ^^^^^^^^^^^^^ Using `Psych.load` on untrusted input [...] + end + RUBY + end + + it 'accepts YAML.safe_load' do + expect_no_offenses(<<~RUBY) + def some_method(a) + YAML.safe_load(a) + end + RUBY + end + + it 'rejects on Marshal.load' do + expect_offense(<<~RUBY) + def some_method(a) + Marshal.load(a) + ^^^^^^^^^^^^^^^ Using `Marshal.load` on untrusted input can lead to remote code execution. [...] + end + RUBY + end + end +end diff --git a/rubocop-airbnb/spec/spec_helper.rb b/rubocop-airbnb/spec/spec_helper.rb new file mode 100644 index 0000000..f8d6dca --- /dev/null +++ b/rubocop-airbnb/spec/spec_helper.rb @@ -0,0 +1,37 @@ +require 'rubocop' +require 'rubocop/rspec/support' +require 'pry' + +module SpecHelper + ROOT = Pathname.new(__FILE__).parent.freeze +end + +# Load in Rubocop cops +require File.expand_path('lib/rubocop-airbnb') + +spec_helper_glob = File.expand_path('{support,shared}/*.rb', SpecHelper::ROOT) +Dir.glob(spec_helper_glob).map(&method(:require)) + +RSpec.configure do |config| + config.include RuboCop::RSpec::ExpectOffense + + config.order = :random + + # Define spec metadata for all rspec cop spec files + cop_specs = 'spec/rubocop/cop/rspec/' + config.define_derived_metadata(file_path: /\b#{cop_specs}/) do |metadata| + # Attach metadata that signals the specified code is for an RSpec only cop + metadata[:rspec_cop] = true + end + + config.expect_with :rspec do |expectations| + expectations.syntax = :expect # Disable `should` + end + + config.mock_with :rspec do |mocks| + mocks.syntax = :expect # Disable `should_receive` and `stub` + end +end + +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) +$LOAD_PATH.unshift(File.dirname(__FILE__))