Possible SQL injection Invalid
scope :with_name_or_description, lambda { |body| where("name " + Common.like_operator + " ? OR description " + Common.like_operator + " ?", body, body) }
- Read upRead up
- Exclude checks
Injection is #1 on the 2013 OWASP Top Ten web security risks. SQL injection is when a user is able to manipulate a value which is used unsafely inside a SQL query. This can lead to data leaks, data loss, elevation of privilege, and other unpleasant outcomes.
Brakeman focuses on ActiveRecord methods dealing with building SQL statements.
A basic (Rails 2.x) example looks like this:
User.first(:conditions => "username = '#{params[:username]}'")
Brakeman would produce a warning like this:
Possible SQL injection near line 30: User.first(:conditions => ("username = '#{params[:username]}'"))
The safe way to do this query is to use a parameterized query:
User.first(:conditions => ["username = ?", params[:username]])
Brakeman also understands the new Rails 3.x way of doing things (and local variables and concatenation):
username = params[:user][:name].downcase
password = params[:user][:password]
User.first.where("username = '" + username + "' AND password = '" + password + "'")
This results in this kind of warning:
Possible SQL injection near line 37:
User.first.where((((("username = '" + params[:user][:name].downcase) + "' AND password = '") + params[:user][:password]) + "'"))
See the Ruby Security Guide for more information and Rails-SQLi.org for many examples of SQL injection in Rails.
Mass assignment is not restricted using attr_accessible Open
class Project < ApplicationRecord
- Read upRead up
- Exclude checks
This warning comes up if a model does not limit what attributes can be set through mass assignment.
In particular, this check looks for attr_accessible
inside model definitions. If it is not found, this warning will be issued.
Brakeman also warns on use of attr_protected
- especially since it was found to be vulnerable to bypass. Warnings for mass assignment on models using attr_protected
will be reported, but at a lower confidence level.
Note that disabling mass assignment globally will suppress these warnings.
Possible SQL injection Invalid
scope :with_namepart, lambda { |body| where("name " + Common.like_operator + " ?", body + '%') }
- Read upRead up
- Exclude checks
Injection is #1 on the 2013 OWASP Top Ten web security risks. SQL injection is when a user is able to manipulate a value which is used unsafely inside a SQL query. This can lead to data leaks, data loss, elevation of privilege, and other unpleasant outcomes.
Brakeman focuses on ActiveRecord methods dealing with building SQL statements.
A basic (Rails 2.x) example looks like this:
User.first(:conditions => "username = '#{params[:username]}'")
Brakeman would produce a warning like this:
Possible SQL injection near line 30: User.first(:conditions => ("username = '#{params[:username]}'"))
The safe way to do this query is to use a parameterized query:
User.first(:conditions => ["username = ?", params[:username]])
Brakeman also understands the new Rails 3.x way of doing things (and local variables and concatenation):
username = params[:user][:name].downcase
password = params[:user][:password]
User.first.where("username = '" + username + "' AND password = '" + password + "'")
This results in this kind of warning:
Possible SQL injection near line 37:
User.first.where((((("username = '" + params[:user][:name].downcase) + "' AND password = '") + params[:user][:password]) + "'"))
See the Ruby Security Guide for more information and Rails-SQLi.org for many examples of SQL injection in Rails.
Complex method Project::import (31.5) Open
def self.import(filename, params, user)
count = 0
CSV.foreach(filename, headers: true) do |row|
unless find_by_name_and_user_id row[params[:name].to_i], user.id
project = new
- Read upRead up
- Exclude checks
Flog calculates the ABC score for methods. The ABC score is based on assignments, branches (method calls), and conditions.
You can read more about ABC metrics or the flog tool
Project#self.import has approx 10 statements Open
def self.import(filename, params, user)
- Read upRead up
- Exclude checks
A method with Too Many Statements
is any method that has a large number of lines.
Too Many Statements
warns about any method that has more than 5 statements. Reek's smell detector for Too Many Statements
counts +1 for every simple statement in a method and +1 for every statement within a control structure (if
, else
, case
, when
, for
, while
, until
, begin
, rescue
) but it doesn't count the control structure itself.
So the following method would score +6 in Reek's statement-counting algorithm:
def parse(arg, argv, &error)
if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
return nil, block, nil # +1
end
opt = (val = parse_arg(val, &error))[1] # +2
val = conv_arg(*val) # +3
if opt and !arg
argv.shift # +4
else
val[0] = nil # +5
end
val # +6
end
(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)
Project#self.import calls 'params[:name]' 2 times Open
unless find_by_name_and_user_id row[params[:name].to_i], user.id
project = new
project.name = row[params[:name].to_i]
- Read upRead up
- Exclude checks
Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.
Reek implements a check for Duplicate Method Call.
Example
Here's a very much simplified and contrived example. The following method will report a warning:
def double_thing()
@other.thing + @other.thing
end
One quick approach to silence Reek would be to refactor the code thus:
def double_thing()
thing = @other.thing
thing + thing
end
A slightly different approach would be to replace all calls of double_thing
by calls to @other.double_thing
:
class Other
def double_thing()
thing + thing
end
end
The approach you take will depend on balancing other factors in your code.
Project#self.import calls 'params[:description].to_i' 2 times Open
project.description = row[params[:description].to_i] if row[params[:description].to_i].present?
- Read upRead up
- Exclude checks
Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.
Reek implements a check for Duplicate Method Call.
Example
Here's a very much simplified and contrived example. The following method will report a warning:
def double_thing()
@other.thing + @other.thing
end
One quick approach to silence Reek would be to refactor the code thus:
def double_thing()
thing = @other.thing
thing + thing
end
A slightly different approach would be to replace all calls of double_thing
by calls to @other.double_thing
:
class Other
def double_thing()
thing + thing
end
end
The approach you take will depend on balancing other factors in your code.
Project#self.import calls 'row[params[:description].to_i]' 2 times Open
project.description = row[params[:description].to_i] if row[params[:description].to_i].present?
- Read upRead up
- Exclude checks
Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.
Reek implements a check for Duplicate Method Call.
Example
Here's a very much simplified and contrived example. The following method will report a warning:
def double_thing()
@other.thing + @other.thing
end
One quick approach to silence Reek would be to refactor the code thus:
def double_thing()
thing = @other.thing
thing + thing
end
A slightly different approach would be to replace all calls of double_thing
by calls to @other.double_thing
:
class Other
def double_thing()
thing + thing
end
end
The approach you take will depend on balancing other factors in your code.
Project has no descriptive comment Open
class Project < ApplicationRecord
- Read upRead up
- Exclude checks
Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.
Example
Given
class Dummy
# Do things...
end
Reek would emit the following warning:
test.rb -- 1 warning:
[1]:Dummy has no descriptive comment (IrresponsibleModule)
Fixing this is simple - just an explaining comment:
# The Dummy class is responsible for ...
class Dummy
# Do things...
end
Project#self.import calls 'params[:name].to_i' 2 times Open
unless find_by_name_and_user_id row[params[:name].to_i], user.id
project = new
project.name = row[params[:name].to_i]
- Read upRead up
- Exclude checks
Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.
Reek implements a check for Duplicate Method Call.
Example
Here's a very much simplified and contrived example. The following method will report a warning:
def double_thing()
@other.thing + @other.thing
end
One quick approach to silence Reek would be to refactor the code thus:
def double_thing()
thing = @other.thing
thing + thing
end
A slightly different approach would be to replace all calls of double_thing
by calls to @other.double_thing
:
class Other
def double_thing()
thing + thing
end
end
The approach you take will depend on balancing other factors in your code.
Project#self.import calls 'row[params[:name].to_i]' 2 times Open
unless find_by_name_and_user_id row[params[:name].to_i], user.id
project = new
project.name = row[params[:name].to_i]
- Read upRead up
- Exclude checks
Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.
Reek implements a check for Duplicate Method Call.
Example
Here's a very much simplified and contrived example. The following method will report a warning:
def double_thing()
@other.thing + @other.thing
end
One quick approach to silence Reek would be to refactor the code thus:
def double_thing()
thing = @other.thing
thing + thing
end
A slightly different approach would be to replace all calls of double_thing
by calls to @other.double_thing
:
class Other
def double_thing()
thing + thing
end
end
The approach you take will depend on balancing other factors in your code.
Project#self.import calls 'params[:description]' 2 times Open
project.description = row[params[:description].to_i] if row[params[:description].to_i].present?
- Read upRead up
- Exclude checks
Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.
Reek implements a check for Duplicate Method Call.
Example
Here's a very much simplified and contrived example. The following method will report a warning:
def double_thing()
@other.thing + @other.thing
end
One quick approach to silence Reek would be to refactor the code thus:
def double_thing()
thing = @other.thing
thing + thing
end
A slightly different approach would be to replace all calls of double_thing
by calls to @other.double_thing
:
class Other
def double_thing()
thing + thing
end
end
The approach you take will depend on balancing other factors in your code.
NullProject has no descriptive comment Open
class NullProject
- Read upRead up
- Exclude checks
Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.
Example
Given
class Dummy
# Do things...
end
Reek would emit the following warning:
test.rb -- 1 warning:
[1]:Dummy has no descriptive comment (IrresponsibleModule)
Fixing this is simple - just an explaining comment:
# The Dummy class is responsible for ...
class Dummy
# Do things...
end
Project#cached_note_count is a writable attribute Open
attr_accessor :cached_note_count
- Read upRead up
- Exclude checks
A class that publishes a setter for an instance variable invites client classes to become too intimate with its inner workings, and in particular with its representation of state.
The same holds to a lesser extent for getters, but Reek doesn't flag those.
Example
Given:
class Klass
attr_accessor :dummy
end
Reek would emit the following warning:
reek test.rb
test.rb -- 1 warning:
[2]:Klass declares the writable attribute dummy (Attribute)
Project#needs_review? performs a nil-check Open
return active? && (last_reviewed.nil? ||
- Read upRead up
- Exclude checks
A NilCheck
is a type check. Failures of NilCheck
violate the "tell, don't ask" principle.
Additionally, type checks often mask bigger problems in your source code like not using OOP and / or polymorphism when you should.
Example
Given
class Klass
def nil_checker(argument)
if argument.nil?
puts "argument isn't nil!"
end
end
end
Reek would emit the following warning:
test.rb -- 1 warning:
[3]:Klass#nil_checker performs a nil-check. (NilCheck)
Project#running_time performs a nil-check Open
if completed_at.nil?
- Read upRead up
- Exclude checks
A NilCheck
is a type check. Failures of NilCheck
violate the "tell, don't ask" principle.
Additionally, type checks often mask bigger problems in your source code like not using OOP and / or polymorphism when you should.
Example
Given
class Klass
def nil_checker(argument)
if argument.nil?
puts "argument isn't nil!"
end
end
end
Reek would emit the following warning:
test.rb -- 1 warning:
[3]:Klass#nil_checker performs a nil-check. (NilCheck)
Project#default_context performs a nil-check Open
original_default_context.nil? ? Context.null_object : original_default_context
- Read upRead up
- Exclude checks
A NilCheck
is a type check. Failures of NilCheck
violate the "tell, don't ask" principle.
Additionally, type checks often mask bigger problems in your source code like not using OOP and / or polymorphism when you should.
Example
Given
class Klass
def nil_checker(argument)
if argument.nil?
puts "argument isn't nil!"
end
end
end
Reek would emit the following warning:
test.rb -- 1 warning:
[3]:Klass#nil_checker performs a nil-check. (NilCheck)
Specify a :dependent
option. Open
has_many :recurring_todos
- Read upRead up
- Exclude checks
This cop looks for has_many
or has_one
associations that don't
specify a :dependent
option.
It doesn't register an offense if :through
option was specified.
Example:
# bad
class User < ActiveRecord::Base
has_many :comments
has_one :avatar
end
# good
class User < ActiveRecord::Base
has_many :comments, dependent: :restrict_with_exception
has_one :avatar, dependent: :destroy
has_many :patients, through: :appointments
end
Specify an :inverse_of
option. Open
belongs_to :default_context, :class_name => "Context", :foreign_key => "default_context_id"
- Read upRead up
- Exclude checks
This cop looks for has(one|many) and belongsto associations where
ActiveRecord can't automatically determine the inverse association
because of a scope or the options used. This can result in unnecessary
queries in some circumstances. :inverse_of
must be manually specified
for associations to work in both ways, or set to false
to opt-out.
Example:
# good
class Blog < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
belongs_to :blog
end
Example:
# bad
class Blog < ApplicationRecord
has_many :posts, -> { order(published_at: :desc) }
end
class Post < ApplicationRecord
belongs_to :blog
end
# good
class Blog < ApplicationRecord
has_many(:posts,
-> { order(published_at: :desc) },
inverse_of: :blog
)
end
class Post < ApplicationRecord
belongs_to :blog
end
# good
class Blog < ApplicationRecord
with_options inverse_of: :blog do
has_many :posts, -> { order(published_at: :desc) }
end
end
class Post < ApplicationRecord
belongs_to :blog
end
Example:
# bad
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :pictures, as: :imageable
end
class Product < ApplicationRecord
has_many :pictures, as: :imageable
end
# good
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :pictures, as: :imageable, inverse_of: :imageable
end
class Product < ApplicationRecord
has_many :pictures, as: :imageable, inverse_of: :imageable
end
Example:
# bad
# However, RuboCop can not detect this pattern...
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician
belongs_to :patient
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
# good
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician, inverse_of: :appointments
belongs_to :patient, inverse_of: :appointments
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
@see http://guides.rubyonrails.org/association_basics.html#bi-directional-associations @see http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses
Specify an :inverse_of
option. Open
has_many :todos, -> { order(Arel.sql("todos.due IS NULL, todos.due ASC, todos.created_at ASC")) }, dependent: :delete_all
- Read upRead up
- Exclude checks
This cop looks for has(one|many) and belongsto associations where
ActiveRecord can't automatically determine the inverse association
because of a scope or the options used. This can result in unnecessary
queries in some circumstances. :inverse_of
must be manually specified
for associations to work in both ways, or set to false
to opt-out.
Example:
# good
class Blog < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
belongs_to :blog
end
Example:
# bad
class Blog < ApplicationRecord
has_many :posts, -> { order(published_at: :desc) }
end
class Post < ApplicationRecord
belongs_to :blog
end
# good
class Blog < ApplicationRecord
has_many(:posts,
-> { order(published_at: :desc) },
inverse_of: :blog
)
end
class Post < ApplicationRecord
belongs_to :blog
end
# good
class Blog < ApplicationRecord
with_options inverse_of: :blog do
has_many :posts, -> { order(published_at: :desc) }
end
end
class Post < ApplicationRecord
belongs_to :blog
end
Example:
# bad
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :pictures, as: :imageable
end
class Product < ApplicationRecord
has_many :pictures, as: :imageable
end
# good
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :pictures, as: :imageable, inverse_of: :imageable
end
class Product < ApplicationRecord
has_many :pictures, as: :imageable, inverse_of: :imageable
end
Example:
# bad
# However, RuboCop can not detect this pattern...
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician
belongs_to :patient
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
# good
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician, inverse_of: :appointments
belongs_to :patient, inverse_of: :appointments
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
@see http://guides.rubyonrails.org/association_basics.html#bi-directional-associations @see http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses
Tagging a string as html safe may be a security risk. Open
name.truncate(length, :omission => "...").html_safe
- Read upRead up
- Exclude checks
This cop checks for the use of output safety calls like htmlsafe, raw, and safeconcat. These methods do not escape content. They simply return a SafeBuffer containing the content as is. Instead, use safe_join to join content and escape it and concat to concatenate content and escape it, ensuring its safety.
Example:
user_content = "hi"
# bad
"#{user_content}
".html_safe
# => ActiveSupport::SafeBuffer "hi
"
# good
content_tag(:p, user_content)
# => ActiveSupport::SafeBuffer "<b>hi</b>
"
# bad
out = ""
out << "#{user_content} "
out << "#{user_content} "
out.html_safe
# => ActiveSupport::SafeBuffer "hi
hi "
# good
out = []
out << content_tag(:li, user_content)
out << content_tag(:li, user_content)
safe_join(out)
# => ActiveSupport::SafeBuffer
# "<b>hi</b>
<b>hi</b> "
# bad
out = "trusted content
".html_safe
out.safe_concat(user_content)
# => ActiveSupport::SafeBuffer "trusted_content
hi"
# good
out = "trusted content
".html_safe
out.concat(user_content)
# => ActiveSupport::SafeBuffer
# "trusted_content
<b>hi</b>"
# safe, though maybe not good style
out = "trusted content"
result = out.concat(user_content)
# => String "trusted contenthi"
# because when rendered in ERB the String will be escaped:
# <%= result %>
# => trusted content<b>hi</b>
# bad
(user_content + " " + content_tag(:span, user_content)).html_safe
# => ActiveSupport::SafeBuffer "hi <span><b>hi</b></span>"
# good
safe_join([user_content, " ", content_tag(:span, user_content)])
# => ActiveSupport::SafeBuffer
# "<b>hi</b> <span><b>hi</b></span>"
Specify an :inverse_of
option. Open
has_many :notes, -> { order "created_at DESC" }, dependent: :delete_all
- Read upRead up
- Exclude checks
This cop looks for has(one|many) and belongsto associations where
ActiveRecord can't automatically determine the inverse association
because of a scope or the options used. This can result in unnecessary
queries in some circumstances. :inverse_of
must be manually specified
for associations to work in both ways, or set to false
to opt-out.
Example:
# good
class Blog < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
belongs_to :blog
end
Example:
# bad
class Blog < ApplicationRecord
has_many :posts, -> { order(published_at: :desc) }
end
class Post < ApplicationRecord
belongs_to :blog
end
# good
class Blog < ApplicationRecord
has_many(:posts,
-> { order(published_at: :desc) },
inverse_of: :blog
)
end
class Post < ApplicationRecord
belongs_to :blog
end
# good
class Blog < ApplicationRecord
with_options inverse_of: :blog do
has_many :posts, -> { order(published_at: :desc) }
end
end
class Post < ApplicationRecord
belongs_to :blog
end
Example:
# bad
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :pictures, as: :imageable
end
class Product < ApplicationRecord
has_many :pictures, as: :imageable
end
# good
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :pictures, as: :imageable, inverse_of: :imageable
end
class Product < ApplicationRecord
has_many :pictures, as: :imageable, inverse_of: :imageable
end
Example:
# bad
# However, RuboCop can not detect this pattern...
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician
belongs_to :patient
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
# good
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician, inverse_of: :appointments
belongs_to :patient, inverse_of: :appointments
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
@see http://guides.rubyonrails.org/association_basics.html#bi-directional-associations @see http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses
Use find_by
instead of dynamic find_by_name_and_user_id
. Open
unless find_by_name_and_user_id row[params[:name].to_i], user.id
- Read upRead up
- Exclude checks
This cop checks dynamic find_by_*
methods.
Use find_by
instead of dynamic method.
See. https://github.com/bbatsov/rails-style-guide#find_by
Example:
# bad
User.find_by_name(name)
# bad
User.find_by_name_and_email(name)
# bad
User.find_by_email!(name)
# good
User.find_by(name: name)
# good
User.find_by(name: name, email: email)
# good
User.find_by!(email: email)
TODO found Open
# TODO: test this for eager and not eager loading!!!
- Exclude checks
Use the -> { ... }
lambda literal syntax for single line lambdas. Open
scope :with_name_or_description, lambda { |body| where("name " + Common.like_operator + " ? OR description " + Common.like_operator + " ?", body, body) }
- Read upRead up
- Exclude checks
This cop (by default) checks for uses of the lambda literal syntax for single line lambdas, and the method call syntax for multiline lambdas. It is configurable to enforce one of the styles for both single line and multiline lambdas as well.
Example: EnforcedStyle: linecountdependent (default)
# bad
f = lambda { |x| x }
f = ->(x) do
x
end
# good
f = ->(x) { x }
f = lambda do |x|
x
end
Example: EnforcedStyle: lambda
# bad
f = ->(x) { x }
f = ->(x) do
x
end
# good
f = lambda { |x| x }
f = lambda do |x|
x
end
Example: EnforcedStyle: literal
# bad
f = lambda { |x| x }
f = lambda do |x|
x
end
# good
f = ->(x) { x }
f = ->(x) do
x
end
Redundant return
detected. Open
return !todos.deferred_or_blocked.exists? && !todos.active.exists?
- Read upRead up
- Exclude checks
This cop checks for redundant return
expressions.
Example:
def test
return something
end
def test
one
two
three
return something
end
It should be extended to handle methods whose body is if/else or a case expression with a default branch.
Redundant return
detected. Open
return active? && (last_reviewed.nil? ||
- Read upRead up
- Exclude checks
This cop checks for redundant return
expressions.
Example:
def test
return something
end
def test
one
two
three
return something
end
It should be extended to handle methods whose body is if/else or a case expression with a default branch.
Indent when
as deep as case
. Open
when :completed
- Read upRead up
- Exclude checks
This cop checks how the whens of a case expression are indented in relation to its case or end keyword.
It will register a separate offense for each misaligned when.
Example:
# If Layout/EndAlignment is set to keyword style (default)
# *case* and *end* should always be aligned to same depth,
# and therefore *when* should always be aligned to both -
# regardless of configuration.
# bad for all styles
case n
when 0
x * 2
else
y / 3
end
# good for all styles
case n
when 0
x * 2
else
y / 3
end
Example: EnforcedStyle: case (default)
# if EndAlignment is set to other style such as
# start_of_line (as shown below), then *when* alignment
# configuration does have an effect.
# bad
a = case n
when 0
x * 2
else
y / 3
end
# good
a = case n
when 0
x * 2
else
y / 3
end
Example: EnforcedStyle: end
# bad
a = case n
when 0
x * 2
else
y / 3
end
# good
a = case n
when 0
x * 2
else
y / 3
end
Missing magic comment # frozen_string_literal: true
. Open
class Project < ApplicationRecord
- Read upRead up
- Exclude checks
This cop is designed to help upgrade to Ruby 3.0. It will add the
comment # frozen_string_literal: true
to the top of files to
enable frozen string literals. Frozen string literals may be default
in Ruby 3.0. The comment will be added below a shebang and encoding
comment. The frozen string literal comment is only valid in Ruby 2.3+.
Example: EnforcedStyle: when_needed (default)
# The `when_needed` style will add the frozen string literal comment
# to files only when the `TargetRubyVersion` is set to 2.3+.
# bad
module Foo
# ...
end
# good
# frozen_string_literal: true
module Foo
# ...
end
Example: EnforcedStyle: always
# The `always` style will always add the frozen string literal comment
# to a file, regardless of the Ruby version or if `freeze` or `<<` are
# called on a string literal.
# bad
module Bar
# ...
end
# good
# frozen_string_literal: true
module Bar
# ...
end
Example: EnforcedStyle: never
# The `never` will enforce that the frozen string literal comment does
# not exist in a file.
# bad
# frozen_string_literal: true
module Baz
# ...
end
# good
module Baz
# ...
end
Use a guard clause instead of wrapping the code inside a conditional expression. Open
if completed_at.nil?
- Read upRead up
- Exclude checks
Use a guard clause instead of wrapping the code inside a conditional expression
Example:
# bad
def test
if something
work
end
end
# good
def test
return unless something
work
end
# also good
def test
work if something
end
# bad
if something
raise 'exception'
else
ok
end
# good
raise 'exception' if something
ok
Redundant return
detected. Open
return (completed_at.to_date - created_at.to_date).to_i + 1
- Read upRead up
- Exclude checks
This cop checks for redundant return
expressions.
Example:
def test
return something
end
def test
one
two
three
return something
end
It should be extended to handle methods whose body is if/else or a case expression with a default branch.
Use %i
or %I
for an array of symbols. Open
transitions :to => :active, :from => [:active, :hidden, :completed]
- Read upRead up
- Exclude checks
This cop can check for array literals made up of symbols that are not using the %i() syntax.
Alternatively, it checks for symbol arrays using the %i() syntax on projects which do not want to use that syntax.
Configuration option: MinSize
If set, arrays with fewer elements than this value will not trigger the
cop. For example, a MinSize of
3` will not enforce a style on an array
of 2 or fewer elements.
Example: EnforcedStyle: percent (default)
# good
%i[foo bar baz]
# bad
[:foo, :bar, :baz]
Example: EnforcedStyle: brackets
# good
[:foo, :bar, :baz]
# bad
%i[foo bar baz]
Indent when
as deep as case
. Open
when aasm.current_state
- Read upRead up
- Exclude checks
This cop checks how the whens of a case expression are indented in relation to its case or end keyword.
It will register a separate offense for each misaligned when.
Example:
# If Layout/EndAlignment is set to keyword style (default)
# *case* and *end* should always be aligned to same depth,
# and therefore *when* should always be aligned to both -
# regardless of configuration.
# bad for all styles
case n
when 0
x * 2
else
y / 3
end
# good for all styles
case n
when 0
x * 2
else
y / 3
end
Example: EnforcedStyle: case (default)
# if EndAlignment is set to other style such as
# start_of_line (as shown below), then *when* alignment
# configuration does have an effect.
# bad
a = case n
when 0
x * 2
else
y / 3
end
# good
a = case n
when 0
x * 2
else
y / 3
end
Example: EnforcedStyle: end
# bad
a = case n
when 0
x * 2
else
y / 3
end
# good
a = case n
when 0
x * 2
else
y / 3
end
Use alias
instead of alias_method
in a class body. Open
alias_method :original_default_context, :default_context
- Read upRead up
- Exclude checks
This cop enforces the use of either #alias
or #alias_method
depending on configuration.
It also flags uses of alias :symbol
rather than alias bareword
.
Example: EnforcedStyle: prefer_alias (default)
# bad
alias_method :bar, :foo
alias :bar :foo
# good
alias bar foo
Example: EnforcedStyle: preferaliasmethod
# bad
alias :bar :foo
alias bar foo
# good
alias_method :bar, :foo
Redundant return
detected. Open
return age_in_days
- Read upRead up
- Exclude checks
This cop checks for redundant return
expressions.
Example:
def test
return something
end
def test
one
two
three
return something
end
It should be extended to handle methods whose body is if/else or a case expression with a default branch.
Interpolation in single quoted string detected. Use double quoted strings if you need interpolation. Open
acts_as_list :scope => 'user_id = #{user_id} AND state = \'#{state}\'', :top_of_list => 0
- Read upRead up
- Exclude checks
This cop checks for interpolation in a single quoted string.
Example:
# bad
foo = 'something with #{interpolation} inside'
Example:
# good
foo = "something with #{interpolation} inside"
Line is too long. [123/120] Open
has_many :todos, -> { order(Arel.sql("todos.due IS NULL, todos.due ASC, todos.created_at ASC")) }, dependent: :delete_all
- Exclude checks
Use the return of the conditional for variable assignment and comparison. Open
if value
self[:name] = value.gsub(/\s{2,}/, " ").strip
else
self[:name] = nil
end
- Exclude checks
Redundant return
detected. Open
return
- Read upRead up
- Exclude checks
This cop checks for redundant return
expressions.
Example:
def test
return something
end
def test
one
two
three
return something
end
It should be extended to handle methods whose body is if/else or a case expression with a default branch.
Redundant return
detected. Open
return !todos.deferred_or_blocked.empty? && todos.active.empty?
- Read upRead up
- Exclude checks
This cop checks for redundant return
expressions.
Example:
def test
return something
end
def test
one
two
three
return something
end
It should be extended to handle methods whose body is if/else or a case expression with a default branch.
Use %i
or %I
for an array of symbols. Open
transitions :to => :hidden, :from => [:active, :completed]
- Read upRead up
- Exclude checks
This cop can check for array literals made up of symbols that are not using the %i() syntax.
Alternatively, it checks for symbol arrays using the %i() syntax on projects which do not want to use that syntax.
Configuration option: MinSize
If set, arrays with fewer elements than this value will not trigger the
cop. For example, a MinSize of
3` will not enforce a style on an array
of 2 or fewer elements.
Example: EnforcedStyle: percent (default)
# good
%i[foo bar baz]
# bad
[:foo, :bar, :baz]
Example: EnforcedStyle: brackets
# good
[:foo, :bar, :baz]
# bad
%i[foo bar baz]
Use empty?
instead of size == 0
. Open
return 0 if notes.size == 0
- Read upRead up
- Exclude checks
This cop checks for numeric comparisons that can be replaced by a predicate method, such as receiver.length == 0, receiver.length > 0, receiver.length != 0, receiver.length < 1 and receiver.size == 0 that can be replaced by receiver.empty? and !receiver.empty.
Example:
# bad
[1, 2, 3].length == 0
0 == "foobar".length
array.length < 1
{a: 1, b: 2}.length != 0
string.length > 0
hash.size > 0
# good
[1, 2, 3].empty?
"foobar".empty?
array.empty?
!{a: 1, b: 2}.empty?
!string.empty?
!hash.empty?
Indent when
as deep as case
. Open
when :active
- Read upRead up
- Exclude checks
This cop checks how the whens of a case expression are indented in relation to its case or end keyword.
It will register a separate offense for each misaligned when.
Example:
# If Layout/EndAlignment is set to keyword style (default)
# *case* and *end* should always be aligned to same depth,
# and therefore *when* should always be aligned to both -
# regardless of configuration.
# bad for all styles
case n
when 0
x * 2
else
y / 3
end
# good for all styles
case n
when 0
x * 2
else
y / 3
end
Example: EnforcedStyle: case (default)
# if EndAlignment is set to other style such as
# start_of_line (as shown below), then *when* alignment
# configuration does have an effect.
# bad
a = case n
when 0
x * 2
else
y / 3
end
# good
a = case n
when 0
x * 2
else
y / 3
end
Example: EnforcedStyle: end
# bad
a = case n
when 0
x * 2
else
y / 3
end
# good
a = case n
when 0
x * 2
else
y / 3
end
Use the -> { ... }
lambda literal syntax for single line lambdas. Open
scope :with_namepart, lambda { |body| where("name " + Common.like_operator + " ?", body + '%') }
- Read upRead up
- Exclude checks
This cop (by default) checks for uses of the lambda literal syntax for single line lambdas, and the method call syntax for multiline lambdas. It is configurable to enforce one of the styles for both single line and multiline lambdas as well.
Example: EnforcedStyle: linecountdependent (default)
# bad
f = lambda { |x| x }
f = ->(x) do
x
end
# good
f = ->(x) { x }
f = lambda do |x|
x
end
Example: EnforcedStyle: lambda
# bad
f = ->(x) { x }
f = ->(x) do
x
end
# good
f = lambda { |x| x }
f = lambda do |x|
x
end
Example: EnforcedStyle: literal
# bad
f = lambda { |x| x }
f = lambda do |x|
x
end
# good
f = ->(x) { x }
f = ->(x) do
x
end
Line is too long. [155/120] Open
scope :with_name_or_description, lambda { |body| where("name " + Common.like_operator + " ? OR description " + Common.like_operator + " ?", body, body) }
- Exclude checks
Use notes.size.zero?
instead of notes.size == 0
. Open
return 0 if notes.size == 0
- Read upRead up
- Exclude checks
This cop checks for usage of comparison operators (==
,
>
, <
) to test numbers as zero, positive, or negative.
These can be replaced by their respective predicate methods.
The cop can also be configured to do the reverse.
The cop disregards #nonzero?
as it its value is truthy or falsey,
but not true
and false
, and thus not always interchangeable with
!= 0
.
The cop ignores comparisons to global variables, since they are often
populated with objects which can be compared with integers, but are
not themselves Interger
polymorphic.
Example: EnforcedStyle: predicate (default)
# bad
foo == 0
0 > foo
bar.baz > 0
# good
foo.zero?
foo.negative?
bar.baz.positive?
Example: EnforcedStyle: comparison
# bad
foo.zero?
foo.negative?
bar.baz.positive?
# good
foo == 0
0 > foo
bar.baz > 0
Use %i
or %I
for an array of symbols. Open
transitions :to => :completed, :from => [:active, :hidden]
- Read upRead up
- Exclude checks
This cop can check for array literals made up of symbols that are not using the %i() syntax.
Alternatively, it checks for symbol arrays using the %i() syntax on projects which do not want to use that syntax.
Configuration option: MinSize
If set, arrays with fewer elements than this value will not trigger the
cop. For example, a MinSize of
3` will not enforce a style on an array
of 2 or fewer elements.
Example: EnforcedStyle: percent (default)
# good
%i[foo bar baz]
# bad
[:foo, :bar, :baz]
Example: EnforcedStyle: brackets
# good
[:foo, :bar, :baz]
# bad
%i[foo bar baz]
Indent when
as deep as case
. Open
when :hidden
- Read upRead up
- Exclude checks
This cop checks how the whens of a case expression are indented in relation to its case or end keyword.
It will register a separate offense for each misaligned when.
Example:
# If Layout/EndAlignment is set to keyword style (default)
# *case* and *end* should always be aligned to same depth,
# and therefore *when* should always be aligned to both -
# regardless of configuration.
# bad for all styles
case n
when 0
x * 2
else
y / 3
end
# good for all styles
case n
when 0
x * 2
else
y / 3
end
Example: EnforcedStyle: case (default)
# if EndAlignment is set to other style such as
# start_of_line (as shown below), then *when* alignment
# configuration does have an effect.
# bad
a = case n
when 0
x * 2
else
y / 3
end
# good
a = case n
when 0
x * 2
else
y / 3
end
Example: EnforcedStyle: end
# bad
a = case n
when 0
x * 2
else
y / 3
end
# good
a = case n
when 0
x * 2
else
y / 3
end