diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..f7a5a3c --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,3923 @@ +AllCops: + RubyInterpreters: + - ruby + - macruby + - rake + - jruby + - rbx + # Include common Ruby source files. + Include: + - "**/*.rb" + - "**/*.arb" + - "**/*.axlsx" + - "**/*.builder" + - "**/*.fcgi" + - "**/*.gemfile" + - "**/*.gemspec" + - "**/*.god" + - "**/*.jb" + - "**/*.jbuilder" + - "**/*.mspec" + - "**/*.opal" + - "**/*.pluginspec" + - "**/*.podspec" + - "**/*.rabl" + - "**/*.rake" + - "**/*.rbuild" + - "**/*.rbw" + - "**/*.rbx" + - "**/*.ru" + - "**/*.ruby" + - "**/*.spec" + - "**/*.thor" + - "**/*.watchr" + - "**/.irbrc" + - "**/.pryrc" + - "**/buildfile" + - "**/Appraisals" + - "**/Berksfile" + - "**/Brewfile" + - "**/Buildfile" + - "**/Capfile" + - "**/Cheffile" + - "**/Dangerfile" + - "**/Deliverfile" + - "**/Fastfile" + - "**/*Fastfile" + - "**/Gemfile" + - "**/Guardfile" + - "**/Jarfile" + - "**/Mavenfile" + - "**/Podfile" + - "**/Puppetfile" + - "**/Rakefile" + - "**/Snapfile" + - "**/Thorfile" + - "**/Vagabondfile" + - "**/Vagrantfile" + Exclude: + - "node_modules/**/*" + - "vendor/**/*" + - ".git/**/*" + # Default formatter will be used if no `-f/--format` option is given. + DefaultFormatter: progress + # Cop names are displayed in offense messages by default. Change behavior + # by overriding DisplayCopNames, or by giving the `--no-display-cop-names` + # option. + DisplayCopNames: true + # Style guide URLs are not displayed in offense messages by default. Change + # behavior by overriding `DisplayStyleGuide`, or by giving the + # `-S/--display-style-guide` option. + DisplayStyleGuide: false + # When specifying style guide URLs, any paths and/or fragments will be + # evaluated relative to the base URL. + StyleGuideBaseURL: https://rubystyle.guide + # Extra details are not displayed in offense messages by default. Change + # behavior by overriding ExtraDetails, or by giving the + # `-E/--extra-details` option. + ExtraDetails: false + # Additional cops that do not reference a style guide rule may be enabled by + # default. Change behavior by overriding `StyleGuideCopsOnly`, or by giving + # the `--only-guide-cops` option. + StyleGuideCopsOnly: false + # All cops except the ones configured `Enabled: false` in this file are enabled by default. + # Change this behavior by overriding either `DisabledByDefault` or `EnabledByDefault`. + # When `DisabledByDefault` is `true`, all cops in the default configuration + # are disabled, and only cops in user configuration are enabled. This makes + # cops opt-in instead of opt-out. Note that when `DisabledByDefault` is `true`, + # cops in user configuration will be enabled even if they don't set the + # Enabled parameter. + # When `EnabledByDefault` is `true`, all cops, even those configured `Enabled: false` + # in this file are enabled by default. Cops can still be disabled in user configuration. + # Note that it is invalid to set both EnabledByDefault and DisabledByDefault + # to true in the same configuration. + EnabledByDefault: false + DisabledByDefault: false + # Enables the result cache if `true`. Can be overridden by the `--cache` command + # line option. + UseCache: true + # Threshold for how many files can be stored in the result cache before some + # of the files are automatically removed. + MaxFilesInCache: 20000 + # The cache will be stored in "rubocop_cache" under this directory. If + # CacheRootDirectory is ~ (nil), which it is by default, the root will be + # taken from the environment variable `$XDG_CACHE_HOME` if it is set, or if + # `$XDG_CACHE_HOME` is not set, it will be `$HOME/.cache/`. + CacheRootDirectory: ~ + # It is possible for a malicious user to know the location of RuboCop's cache + # directory by looking at CacheRootDirectory, and create a symlink in its + # place that could cause RuboCop to overwrite unintended files, or read + # malicious input. If you are certain that your cache location is secure from + # this kind of attack, and wish to use a symlinked cache location, set this + # value to "true". + AllowSymlinksInCacheRootDirectory: false + # What MRI version of the Ruby interpreter is the inspected code intended to + # run on? (If there is more than one, set this to the lowest version.) + # If a value is specified for TargetRubyVersion then it is used. Acceptable + # values are specificed as a float (i.e. 2.5); the teeny version of Ruby + # should not be included. If the project specifies a Ruby version in the + # .ruby-version file, Gemfile or gems.rb file, RuboCop will try to determine + # the desired version of Ruby by inspecting the .ruby-version file first, + # followed by the Gemfile.lock or gems.locked file. (Although the Ruby version + # is specified in the Gemfile or gems.rb file, RuboCop reads the final value + # from the lock file.) If the Ruby version is still unresolved, RuboCop will + # use the oldest officially supported Ruby version (currently Ruby 2.3). + TargetRubyVersion: ~ + +#################### Bundler ############################### + +Bundler/DuplicatedGem: + Description: "Checks for duplicate gem entries in Gemfile." + Enabled: true + VersionAdded: "0.46" + Include: + - "**/*.gemfile" + - "**/Gemfile" + - "**/gems.rb" + +Bundler/GemComment: + Description: "Add a comment describing each gem." + Enabled: false + VersionAdded: "0.59" + Include: + - "**/*.gemfile" + - "**/Gemfile" + - "**/gems.rb" + IgnoredGems: [] + +Bundler/InsecureProtocolSource: + Description: >- + The source `:gemcutter`, `:rubygems` and `:rubyforge` are deprecated + because HTTP requests are insecure. Please change your source to + 'https://rubygems.org' if possible, or 'http://rubygems.org' if not. + Enabled: true + VersionAdded: "0.50" + Include: + - "**/*.gemfile" + - "**/Gemfile" + - "**/gems.rb" + +Bundler/OrderedGems: + Description: >- + Gems within groups in the Gemfile should be alphabetically sorted. + Enabled: true + VersionAdded: "0.46" + VersionChanged: "0.47" + TreatCommentsAsGroupSeparators: true + Include: + - "**/*.gemfile" + - "**/Gemfile" + - "**/gems.rb" + +#################### Gemspec ############################### + +Gemspec/DuplicatedAssignment: + Description: "An attribute assignment method calls should be listed only once in a gemspec." + Enabled: true + VersionAdded: "0.52" + Include: + - "**/*.gemspec" + +Gemspec/OrderedDependencies: + Description: >- + Dependencies in the gemspec should be alphabetically sorted. + Enabled: true + VersionAdded: "0.51" + TreatCommentsAsGroupSeparators: true + Include: + - "**/*.gemspec" + +Gemspec/RequiredRubyVersion: + Description: "Checks that `required_ruby_version` of gemspec and `TargetRubyVersion` of .rubocop.yml are equal." + Enabled: true + VersionAdded: "0.52" + Include: + - "**/*.gemspec" + - +Gemspec/RubyVersionGlobalsUsage: + Description: Checks usage of RUBY_VERSION in gemspec. + Enabled: true + VersionAdded: "0.72" + Include: + - "**/*.gemspec" + +#################### Layout ########################### + +Layout/AccessModifierIndentation: + Description: Check indentation of private/protected visibility modifiers. + StyleGuide: "#indent-public-private-protected" + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: indent + SupportedStyles: + - outdent + - indent + # By default, the indentation width from Layout/IndentationWidth is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Layout/ArgumentAlignment: + Description: >- + Align the arguments of a method call if they span more + than one line. + StyleGuide: "#no-double-indent" + Enabled: false + VersionAdded: "0.68" + # Alignment of arguments in multi-line method calls. + # + # The `with_first_argument` style aligns the following lines along the same + # column as the first parameter. + # + # method_call(a, + # b) + # + # The `with_fixed_indentation` style aligns the following lines with one + # level of indentation relative to the start of the line with the method call. + # + # method_call(a, + # b) + EnforcedStyle: with_first_argument + SupportedStyles: + - with_first_argument + - with_fixed_indentation + # By default, the indentation width from Layout/IndentationWidth is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Layout/ArrayAlignment: + Description: >- + Align the elements of an array literal if they span more than + one line. + StyleGuide: "#align-multiline-arrays" + Enabled: false + VersionAdded: "0.49" + +Layout/HashAlignment: + Description: >- + Align the elements of a hash literal if they span more than + one line. + Enabled: false + AllowMultipleStyles: true + VersionAdded: "0.49" + # Alignment of entries using hash rocket as separator. Valid values are: + # + # key - left alignment of keys + # 'a' => 2 + # 'bb' => 3 + # separator - alignment of hash rockets, keys are right aligned + # 'a' => 2 + # 'bb' => 3 + # table - left alignment of keys, hash rockets, and values + # 'a' => 2 + # 'bb' => 3 + EnforcedHashRocketStyle: key + SupportedHashRocketStyles: + - key + - separator + - table + # Alignment of entries using colon as separator. Valid values are: + # + # key - left alignment of keys + # a: 0 + # bb: 1 + # separator - alignment of colons, keys are right aligned + # a: 0 + # bb: 1 + # table - left alignment of keys and values + # a: 0 + # bb: 1 + EnforcedColonStyle: key + SupportedColonStyles: + - key + - separator + - table + # Select whether hashes that are the last argument in a method call should be + # inspected? Valid values are: + # + # always_inspect - Inspect both implicit and explicit hashes. + # Registers an offense for: + # function(a: 1, + # b: 2) + # Registers an offense for: + # function({a: 1, + # b: 2}) + # always_ignore - Ignore both implicit and explicit hashes. + # Accepts: + # function(a: 1, + # b: 2) + # Accepts: + # function({a: 1, + # b: 2}) + # ignore_implicit - Ignore only implicit hashes. + # Accepts: + # function(a: 1, + # b: 2) + # Registers an offense for: + # function({a: 1, + # b: 2}) + # ignore_explicit - Ignore only explicit hashes. + # Accepts: + # function({a: 1, + # b: 2}) + # Registers an offense for: + # function(a: 1, + # b: 2) + EnforcedLastArgumentHashStyle: always_inspect + SupportedLastArgumentHashStyles: + - always_inspect + - always_ignore + - ignore_implicit + - ignore_explicit + +Layout/ParameterAlignment: + Description: >- + Align the parameters of a method definition if they span more + than one line. + StyleGuide: "#no-double-indent" + Enabled: false + VersionAdded: "0.49" + VersionChanged: "0.68" + # Alignment of parameters in multi-line method calls. + # + # The `with_first_parameter` style aligns the following lines along the same + # column as the first parameter. + # + # def method_foo(a, + # b) + # + # The `with_fixed_indentation` style aligns the following lines with one + # level of indentation relative to the start of the line with the method call. + # + # def method_foo(a, + # b) + EnforcedStyle: with_first_parameter + SupportedStyles: + - with_first_parameter + - with_fixed_indentation + # By default, the indentation width from Layout/IndentationWidth is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Layout/BlockAlignment: + Description: "Align block ends correctly." + Enabled: true + VersionAdded: "0.53" + # The value `start_of_block` means that the `end` should be aligned with line + # where the `do` keyword appears. + # The value `start_of_line` means it should be aligned with the whole + # expression's starting line. + # The value `either` means both are allowed. + EnforcedStyleAlignWith: either + SupportedStylesAlignWith: + - either + - start_of_block + - start_of_line + +Layout/BlockEndNewline: + Description: "Put end statement of multiline block on its own line." + Enabled: true + VersionAdded: "0.49" + +Layout/CaseIndentation: + Description: "Indentation of when in a case/when/[else/]end." + StyleGuide: "#indent-when-to-case" + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: case + SupportedStyles: + - case + - end + IndentOneStep: false + # By default, the indentation width from `Layout/IndentationWidth` is used. + # But it can be overridden by setting this parameter. + # This only matters if `IndentOneStep` is `true` + IndentationWidth: ~ + +Layout/ClassStructure: + Description: "Enforces a configured order of definitions within a class body." + StyleGuide: "#consistent-classes" + Enabled: false + VersionAdded: "0.52" + Categories: + module_inclusion: + - include + - prepend + - extend + ExpectedOrder: + - module_inclusion + - constants + - public_class_methods + - initializer + - public_methods + - protected_methods + - private_methods + +Layout/ClosingHeredocIndentation: + Description: "Checks the indentation of here document closings." + Enabled: true + VersionAdded: "0.57" + +Layout/ClosingParenthesisIndentation: + Description: "Checks the indentation of hanging closing parentheses." + Enabled: true + VersionAdded: "0.49" + +Layout/CommentIndentation: + Description: "Indentation of comments." + Enabled: true + VersionAdded: "0.49" + +Layout/ConditionPosition: + Description: >- + Checks for condition placed in a confusing position relative to + the keyword. + StyleGuide: "#same-line-condition" + Enabled: true + VersionAdded: "0.53" + +Layout/DefEndAlignment: + Description: "Align ends corresponding to defs correctly." + Enabled: true + VersionAdded: "0.53" + # The value `def` means that `end` should be aligned with the def keyword. + # The value `start_of_line` means that `end` should be aligned with method + # calls like `private`, `public`, etc, if present in front of the `def` + # keyword on the same line. + EnforcedStyleAlignWith: start_of_line + SupportedStylesAlignWith: + - start_of_line + - def + AutoCorrect: false + Severity: warning + +Layout/DotPosition: + Description: "Checks the position of the dot in multi-line method calls." + StyleGuide: "#consistent-multi-line-chains" + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: leading + SupportedStyles: + - leading + - trailing + +Layout/ElseAlignment: + Description: "Align elses and elsifs correctly." + Enabled: true + VersionAdded: "0.49" + +Layout/EmptyComment: + Description: "Checks empty comment." + Enabled: true + VersionAdded: "0.53" + AllowBorderComment: true + AllowMarginComment: true + +Layout/EmptyLineAfterGuardClause: + Description: "Add empty line after guard clause." + Enabled: true + VersionAdded: "0.56" + VersionChanged: "0.59" + +Layout/EmptyLineAfterMagicComment: + Description: "Add an empty line after magic comments to separate them from code." + StyleGuide: "#separate-magic-comments-from-code" + Enabled: true + VersionAdded: "0.49" + +Layout/EmptyLineBetweenDefs: + Description: "Use empty lines between defs." + StyleGuide: "#empty-lines-between-methods" + Enabled: true + VersionAdded: "0.49" + # If `true`, this parameter means that single line method definitions don't + # need an empty line between them. + AllowAdjacentOneLineDefs: false + # Can be array to specify minimum and maximum number of empty lines, e.g. [1, 2] + NumberOfEmptyLines: 1 + +Layout/EmptyLines: + Description: "Don't use several empty lines in a row." + StyleGuide: "#two-or-more-empty-lines" + Enabled: true + VersionAdded: "0.49" + +Layout/EmptyLinesAroundAccessModifier: + Description: "Keep blank lines around access modifiers." + StyleGuide: "#empty-lines-around-access-modifier" + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: around + SupportedStyles: + - around + - only_before + Reference: + # A reference to `EnforcedStyle: only_before`. + - https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#follow-the-coding-conventions + +Layout/EmptyLinesAroundArguments: + Description: "Keeps track of empty lines around method arguments." + Enabled: true + VersionAdded: "0.52" + +Layout/EmptyLinesAroundBeginBody: + Description: "Keeps track of empty lines around begin-end bodies." + StyleGuide: "#empty-lines-around-bodies" + Enabled: true + VersionAdded: "0.49" + +Layout/EmptyLinesAroundBlockBody: + Description: "Keeps track of empty lines around block bodies." + StyleGuide: "#empty-lines-around-bodies" + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: no_empty_lines + SupportedStyles: + - empty_lines + - no_empty_lines + +Layout/EmptyLinesAroundClassBody: + Description: "Keeps track of empty lines around class bodies." + StyleGuide: "#empty-lines-around-bodies" + Enabled: true + VersionAdded: "0.49" + VersionChanged: "0.53" + EnforcedStyle: no_empty_lines + SupportedStyles: + - empty_lines + - empty_lines_except_namespace + - empty_lines_special + - no_empty_lines + - beginning_only + - ending_only + +Layout/EmptyLinesAroundExceptionHandlingKeywords: + Description: "Keeps track of empty lines around exception handling keywords." + StyleGuide: "#empty-lines-around-bodies" + Enabled: true + VersionAdded: "0.49" + +Layout/EmptyLinesAroundMethodBody: + Description: "Keeps track of empty lines around method bodies." + StyleGuide: "#empty-lines-around-bodies" + Enabled: true + VersionAdded: "0.49" + +Layout/EmptyLinesAroundModuleBody: + Description: "Keeps track of empty lines around module bodies." + StyleGuide: "#empty-lines-around-bodies" + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: no_empty_lines + SupportedStyles: + - empty_lines + - empty_lines_except_namespace + - empty_lines_special + - no_empty_lines + +Layout/EndAlignment: + Description: "Align ends correctly." + Enabled: true + VersionAdded: "0.53" + # 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. + EnforcedStyleAlignWith: keyword + SupportedStylesAlignWith: + - keyword + - variable + - start_of_line + AutoCorrect: false + Severity: warning + +Layout/EndOfLine: + Description: "Use Unix-style line endings." + StyleGuide: "#crlf" + Enabled: true + VersionAdded: "0.49" + # The `native` style means that CR+LF (Carriage Return + Line Feed) is + # enforced on Windows, and LF is enforced on other platforms. The other styles + # mean LF and CR+LF, respectively. + EnforcedStyle: native + SupportedStyles: + - native + - lf + - crlf + +Layout/ExtraSpacing: + Description: "Do not use unnecessary spacing." + Enabled: true + VersionAdded: "0.49" + # When true, allows most uses of extra spacing if the intent is to align + # things with the previous or next line, not counting empty lines or comment + # lines. + AllowForAlignment: true + # When true, allows things like 'obj.meth(arg) # comment', + # rather than insisting on 'obj.meth(arg) # comment'. + # If done for alignment, either this OR AllowForAlignment will allow it. + AllowBeforeTrailingComments: false + # When true, forces the alignment of `=` in assignments on consecutive lines. + ForceEqualSignAlignment: false + +Layout/FirstArrayElementLineBreak: + Description: >- + Checks for a line break before the first element in a + multi-line array. + Enabled: false + VersionAdded: "0.49" + +Layout/FirstHashElementLineBreak: + Description: >- + Checks for a line break before the first element in a + multi-line hash. + Enabled: false + VersionAdded: "0.49" + +Layout/FirstMethodArgumentLineBreak: + Description: >- + Checks for a line break before the first argument in a + multi-line method call. + Enabled: false + VersionAdded: "0.49" + +Layout/FirstMethodParameterLineBreak: + Description: >- + Checks for a line break before the first parameter in a + multi-line method parameter definition. + Enabled: false + VersionAdded: "0.49" + +Layout/HeredocArgumentClosingParenthesis: + Description: >- + Checks for the placement of the closing parenthesis in a + method call that passes a HEREDOC string as an argument. + Enabled: false + StyleGuide: "#heredoc-argument-closing-parentheses" + VersionAdded: "0.68" + +Layout/AssignmentIndentation: + Description: >- + Checks the indentation of the first line of the + right-hand-side of a multi-line assignment. + Enabled: true + VersionAdded: "0.49" + # By default, the indentation width from `Layout/IndentationWidth` is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Layout/FirstArgumentIndentation: + Description: "Checks the indentation of the first argument in a method call." + Enabled: true + VersionAdded: "0.68" + EnforcedStyle: special_for_inner_method_call_in_parentheses + SupportedStyles: + # The first parameter should always be indented one step more than the + # preceding line. + - consistent + # The first parameter should always be indented one level relative to the + # parent that is receiving the parameter + - consistent_relative_to_receiver + # The first parameter should normally be indented one step more than the + # preceding line, but if it's a parameter for a method call that is itself + # a parameter in a method call, then the inner parameter should be indented + # relative to the inner method. + - special_for_inner_method_call + # Same as `special_for_inner_method_call` except that the special rule only + # applies if the outer method call encloses its arguments in parentheses. + - special_for_inner_method_call_in_parentheses + # By default, the indentation width from `Layout/IndentationWidth` is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Layout/FirstArrayElementIndentation: + Description: >- + Checks the indentation of the first element in an array + literal. + Enabled: true + VersionAdded: "0.68" + # The value `special_inside_parentheses` means that array literals with + # brackets that have their opening bracket on the same line as a surrounding + # opening round parenthesis, shall have their first element indented relative + # to the first position inside the parenthesis. + # + # The value `consistent` means that the indentation of the first element shall + # always be relative to the first position of the line where the opening + # bracket is. + # + # The value `align_brackets` means that the indentation of the first element + # shall always be relative to the position of the opening bracket. + EnforcedStyle: special_inside_parentheses + SupportedStyles: + - special_inside_parentheses + - consistent + - align_brackets + # By default, the indentation width from `Layout/IndentationWidth` is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Layout/FirstHashElementIndentation: + Description: "Checks the indentation of the first key in a hash literal." + Enabled: false + VersionAdded: "0.68" + # The value `special_inside_parentheses` means that hash literals with braces + # that have their opening brace on the same line as a surrounding opening + # round parenthesis, shall have their first key indented relative to the + # first position inside the parenthesis. + # + # The value `consistent` means that the indentation of the first key shall + # always be relative to the first position of the line where the opening + # brace is. + # + # The value `align_braces` means that the indentation of the first key shall + # always be relative to the position of the opening brace. + EnforcedStyle: special_inside_parentheses + SupportedStyles: + - special_inside_parentheses + - consistent + - align_braces + # By default, the indentation width from `Layout/IndentationWidth` is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Layout/FirstParameterIndentation: + Description: >- + Checks the indentation of the first parameter in a + method definition. + Enabled: true + VersionAdded: "0.49" + VersionChanged: "0.68" + EnforcedStyle: consistent + SupportedStyles: + - consistent + - align_parentheses + # By default, the indentation width from `Layout/IndentationWidth` is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Layout/HeredocIndentation: + Description: "This cop checks the indentation of the here document bodies." + StyleGuide: "#squiggly-heredocs" + Enabled: true + VersionAdded: "0.49" + VersionChanged: "0.69" + EnforcedStyle: squiggly + SupportedStyles: + - squiggly + - active_support + - powerpack + - unindent + +Layout/IndentationConsistency: + Description: "Keep indentation straight." + StyleGuide: "#spaces-indentation" + Enabled: true + VersionAdded: "0.49" + # The difference between `indented` and `normal` is that the `indented_internal_methods` + # style prescribes that in classes and modules the `protected` and `private` + # modifier keywords shall be indented the same as public methods and that + # protected and private members shall be indented one step more than the + # modifiers. Other than that, both styles mean that entities on the same + # logical depth shall have the same indentation. + EnforcedStyle: normal + SupportedStyles: + - normal + - indented_internal_methods + Reference: + # A reference to `EnforcedStyle: indented_internal_methods`. + - https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#follow-the-coding-conventions + +Layout/IndentationWidth: + Description: "Use 2 spaces for indentation." + StyleGuide: "#spaces-indentation" + Enabled: true + VersionAdded: "0.49" + # Number of spaces for each indentation level. + Width: 2 + IgnoredPatterns: [] + +Layout/InitialIndentation: + Description: >- + Checks the indentation of the first non-blank non-comment line in a file. + Enabled: true + VersionAdded: "0.49" + +Layout/LeadingEmptyLines: + Description: Check for unnecessary blank lines at the beginning of a file. + Enabled: true + VersionAdded: "0.57" + +Layout/LeadingCommentSpace: + Description: "Comments should start with a space." + StyleGuide: "#hash-space" + Enabled: true + VersionAdded: "0.49" + VersionChanged: "0.73" + AllowDoxygenCommentStyle: false + +Layout/MultilineArrayBraceLayout: + Description: >- + Checks that the closing brace in an array literal is + either on the same line as the last array element, or + a new line. + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: symmetrical + SupportedStyles: + # symmetrical: closing brace is positioned in same way as opening brace + # new_line: closing brace is always on a new line + # same_line: closing brace is always on the same line as last element + - symmetrical + - new_line + - same_line + +Layout/MultilineArrayLineBreaks: + Description: >- + Checks that each item in a multi-line array literal + starts on a separate line. + Enabled: false + VersionAdded: "0.67" + +Layout/MultilineAssignmentLayout: + Description: "Check for a newline after the assignment operator in multi-line assignments." + StyleGuide: "#indent-conditional-assignment" + Enabled: false + VersionAdded: "0.49" + # The types of assignments which are subject to this rule. + SupportedTypes: + - block + - case + - class + - if + - kwbegin + - module + EnforcedStyle: new_line + SupportedStyles: + # Ensures that the assignment operator and the rhs are on the same line for + # the set of supported types. + - same_line + # Ensures that the assignment operator and the rhs are on separate lines + # for the set of supported types. + - new_line + +Layout/MultilineBlockLayout: + Description: "Ensures newlines after multiline block do statements." + Enabled: true + VersionAdded: "0.49" + +Layout/MultilineHashBraceLayout: + Description: >- + Checks that the closing brace in a hash literal is + either on the same line as the last hash element, or + a new line. + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: symmetrical + SupportedStyles: + # symmetrical: closing brace is positioned in same way as opening brace + # new_line: closing brace is always on a new line + # same_line: closing brace is always on same line as last element + - symmetrical + - new_line + - same_line + +Layout/MultilineHashKeyLineBreaks: + Description: >- + Checks that each item in a multi-line hash literal + starts on a separate line. + Enabled: false + VersionAdded: "0.67" + +Layout/MultilineMethodArgumentLineBreaks: + Description: >- + Checks that each argument in a multi-line method call + starts on a separate line. + Enabled: false + VersionAdded: "0.67" + +Layout/MultilineMethodCallBraceLayout: + Description: >- + Checks that the closing brace in a method call is + either on the same line as the last method argument, or + a new line. + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: symmetrical + SupportedStyles: + # symmetrical: closing brace is positioned in same way as opening brace + # new_line: closing brace is always on a new line + # same_line: closing brace is always on the same line as last argument + - symmetrical + - new_line + - same_line + +Layout/MultilineMethodCallIndentation: + Description: >- + Checks indentation of method calls with the dot operator + that span more than one line. + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: aligned + SupportedStyles: + - aligned + - indented + - indented_relative_to_receiver + # By default, the indentation width from Layout/IndentationWidth is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Layout/MultilineMethodDefinitionBraceLayout: + Description: >- + Checks that the closing brace in a method definition is + either on the same line as the last method parameter, or + a new line. + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: symmetrical + SupportedStyles: + # symmetrical: closing brace is positioned in same way as opening brace + # new_line: closing brace is always on a new line + # same_line: closing brace is always on the same line as last parameter + - symmetrical + - new_line + - same_line + +Layout/MultilineOperationIndentation: + Description: >- + Checks indentation of binary operations that span more than + one line. + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: aligned + SupportedStyles: + - aligned + - indented + # By default, the indentation width from `Layout/IndentationWidth` is used + # But it can be overridden by setting this parameter + IndentationWidth: ~ + +Layout/RescueEnsureAlignment: + Description: "Align rescues and ensures correctly." + Enabled: true + VersionAdded: "0.49" + +Layout/SpaceAfterColon: + Description: "Use spaces after colons." + StyleGuide: "#spaces-operators" + Enabled: true + VersionAdded: "0.49" + +Layout/SpaceAfterComma: + Description: "Use spaces after commas." + StyleGuide: "#spaces-operators" + Enabled: true + VersionAdded: "0.49" + +Layout/SpaceAfterMethodName: + Description: >- + Do not put a space between a method name and the opening + parenthesis in a method definition. + StyleGuide: "#parens-no-spaces" + Enabled: true + VersionAdded: "0.49" + +Layout/SpaceAfterNot: + Description: Tracks redundant space after the ! operator. + StyleGuide: "#no-space-bang" + Enabled: true + VersionAdded: "0.49" + +Layout/SpaceAfterSemicolon: + Description: "Use spaces after semicolons." + StyleGuide: "#spaces-operators" + Enabled: true + VersionAdded: "0.49" + +Layout/SpaceAroundBlockParameters: + Description: "Checks the spacing inside and after block parameters pipes." + Enabled: true + VersionAdded: "0.49" + EnforcedStyleInsidePipes: no_space + SupportedStylesInsidePipes: + - space + - no_space + +Layout/SpaceAroundEqualsInParameterDefault: + Description: >- + Checks that the equals signs in parameter default assignments + have or don't have surrounding space depending on + configuration. + StyleGuide: "#spaces-around-equals" + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: space + SupportedStyles: + - space + - no_space + +Layout/SpaceAroundKeyword: + Description: "Use a space around keywords if appropriate." + Enabled: true + VersionAdded: "0.49" + +Layout/SpaceAroundOperators: + Description: "Use a single space around operators." + StyleGuide: "#spaces-operators" + Enabled: false + VersionAdded: "0.49" + # When `true`, allows most uses of extra spacing if the intent is to align + # with an operator on the previous or next line, not counting empty lines + # or comment lines. + AllowForAlignment: true + +Layout/SpaceBeforeBlockBraces: + Description: >- + Checks that the left block brace has or doesn't have space + before it. + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: space + SupportedStyles: + - space + - no_space + EnforcedStyleForEmptyBraces: space + SupportedStylesForEmptyBraces: + - space + - no_space + VersionChanged: "0.52.1" + +Layout/SpaceBeforeComma: + Description: "No spaces before commas." + Enabled: true + VersionAdded: "0.49" + +Layout/SpaceBeforeComment: + Description: >- + Checks for missing space between code and a comment on the + same line. + Enabled: true + VersionAdded: "0.49" + +Layout/SpaceBeforeFirstArg: + Description: >- + Checks that exactly one space is used between a method name + and the first argument for method calls without parentheses. + Enabled: true + VersionAdded: "0.49" + # When `true`, allows most uses of extra spacing if the intent is to align + # things with the previous or next line, not counting empty lines or comment + # lines. + AllowForAlignment: true + +Layout/SpaceBeforeSemicolon: + Description: "No spaces before semicolons." + Enabled: true + VersionAdded: "0.49" + +Layout/SpaceInLambdaLiteral: + Description: "Checks for spaces in lambda literals." + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: require_no_space + SupportedStyles: + - require_no_space + - require_space + +Layout/SpaceInsideArrayLiteralBrackets: + Description: "Checks the spacing inside array literal brackets." + Enabled: true + VersionAdded: "0.52" + 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 + +Layout/SpaceInsideArrayPercentLiteral: + Description: "No unnecessary additional spaces between elements in %i/%w literals." + Enabled: true + VersionAdded: "0.49" + +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 + VersionAdded: "0.49" + EnforcedStyle: space + SupportedStyles: + - space + - no_space + EnforcedStyleForEmptyBraces: no_space + SupportedStylesForEmptyBraces: + - space + - no_space + # Space between `{` and `|`. Overrides `EnforcedStyle` if there is a conflict. + SpaceBeforeBlockParameters: true + +Layout/SpaceInsideHashLiteralBraces: + Description: "Use spaces inside hash literal braces - or don't." + StyleGuide: "#spaces-operators" + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: space + SupportedStyles: + - space + - no_space + # 'compact' normally requires a space inside hash braces, with the exception + # that successive left braces or right braces are collapsed together + - compact + EnforcedStyleForEmptyBraces: no_space + SupportedStylesForEmptyBraces: + - space + - no_space + +Layout/SpaceInsideParens: + Description: "No spaces after ( or before )." + StyleGuide: "#spaces-braces" + Enabled: true + VersionAdded: "0.49" + VersionChanged: "0.55" + EnforcedStyle: no_space + SupportedStyles: + - space + - no_space + +Layout/SpaceInsidePercentLiteralDelimiters: + Description: "No unnecessary spaces inside delimiters of %i/%w/%x literals." + Enabled: true + VersionAdded: "0.49" + +Layout/SpaceInsideRangeLiteral: + Description: "No spaces inside range literals." + StyleGuide: "#no-space-inside-range-literals" + Enabled: true + VersionAdded: "0.49" + +Layout/SpaceInsideReferenceBrackets: + Description: "Checks the spacing inside referential brackets." + Enabled: true + VersionAdded: "0.52" + VersionChanged: "0.53" + EnforcedStyle: no_space + SupportedStyles: + - space + - no_space + EnforcedStyleForEmptyBrackets: no_space + SupportedStylesForEmptyBrackets: + - space + - no_space + +Layout/SpaceInsideStringInterpolation: + Description: "Checks for padding/surrounding spaces inside string interpolation." + StyleGuide: "#string-interpolation" + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: no_space + SupportedStyles: + - space + - no_space + +Layout/Tab: + Description: "No hard tabs." + StyleGuide: "#spaces-indentation" + Enabled: false + VersionAdded: "0.49" + VersionChanged: "0.51" + # By default, the indentation width from Layout/IndentationWidth is used + # But it can be overridden by setting this parameter + # It is used during auto-correction to determine how many spaces should + # replace each tab. + IndentationWidth: ~ + AutoCorrect: true + +Layout/TrailingEmptyLines: + Description: "Checks trailing blank lines and final newline." + StyleGuide: "#newline-eof" + Enabled: true + VersionAdded: "0.49" + EnforcedStyle: final_newline + SupportedStyles: + - final_newline + - final_blank_line + +Layout/TrailingWhitespace: + Description: "Avoid trailing whitespace." + StyleGuide: "#no-trailing-whitespace" + Enabled: true + VersionAdded: "0.49" + VersionChanged: "0.55" + AllowInHeredoc: false + AutoCorrect: true + +#################### Lint ################################## +### Warnings + +Lint/RaiseException: + Enabled: false + +Lint/StructNewOverride: + Enabled: false + +Lint/AmbiguousBlockAssociation: + Description: >- + Checks for ambiguous block association with method when param passed without + parentheses. + StyleGuide: "#syntax" + Enabled: true + VersionAdded: "0.48" + +Lint/AmbiguousOperator: + Description: >- + Checks for ambiguous operators in the first argument of a + method invocation without parentheses. + StyleGuide: "#method-invocation-parens" + Enabled: true + VersionAdded: "0.17" + +Lint/AmbiguousRegexpLiteral: + Description: >- + Checks for ambiguous regexp literals in the first argument of + a method invocation without parentheses. + Enabled: true + VersionAdded: "0.17" + +Lint/AssignmentInCondition: + Description: "Don't use assignment in conditions." + StyleGuide: "#safe-assignment-in-condition" + Enabled: true + VersionAdded: "0.9" + AllowSafeAssignment: true + +Lint/BigDecimalNew: + Description: "`BigDecimal.new()` is deprecated. Use `BigDecimal()` instead." + Enabled: true + VersionAdded: "0.53" + +Lint/BooleanSymbol: + Description: "Check for `:true` and `:false` symbols." + Enabled: true + VersionAdded: "0.50" + +Lint/CircularArgumentReference: + Description: "Default values in optional keyword arguments and optional ordinal arguments should not refer back to the name of the argument." + Enabled: true + VersionAdded: "0.33" + +Lint/Debugger: + Description: "Check for debugger calls." + Enabled: true + VersionAdded: "0.14" + VersionChanged: "0.49" + +Lint/DeprecatedClassMethods: + Description: "Check for deprecated class method calls." + Enabled: true + VersionAdded: "0.19" + +Lint/DisjunctiveAssignmentInConstructor: + Description: "In constructor, plain assignment is preferred over disjunctive." + Enabled: true + Safe: false + VersionAdded: "0.62" + +Lint/DuplicateCaseCondition: + Description: "Do not repeat values in case conditionals." + Enabled: true + VersionAdded: "0.45" + +Lint/DuplicateMethods: + Description: "Check for duplicate method definitions." + Enabled: true + VersionAdded: "0.29" + +Lint/DuplicateHashKey: + Description: "Check for duplicate keys in hash literals." + Enabled: true + VersionAdded: "0.34" + +Lint/EachWithObjectArgument: + Description: "Check for immutable argument given to each_with_object." + Enabled: true + VersionAdded: "0.31" + +Lint/ElseLayout: + Description: "Check for odd code arrangement in an else block." + Enabled: true + VersionAdded: "0.17" + +Lint/EmptyEnsure: + Description: "Checks for empty ensure block." + Enabled: true + VersionAdded: "0.10" + VersionChanged: "0.48" + AutoCorrect: false + +Lint/EmptyExpression: + Description: "Checks for empty expressions." + Enabled: true + VersionAdded: "0.45" + +Lint/EmptyInterpolation: + Description: "Checks for empty string interpolation." + Enabled: true + VersionAdded: "0.20" + VersionChanged: "0.45" + +Lint/EmptyWhen: + Description: "Checks for `when` branches with empty bodies." + Enabled: true + VersionAdded: "0.45" + +Lint/EnsureReturn: + Description: "Do not use return in an ensure block." + StyleGuide: "#no-return-ensure" + Enabled: true + VersionAdded: "0.9" + +Lint/ErbNewArguments: + Description: "Use `:trim_mode` and `:eoutvar` keyword arguments to `ERB.new`." + Enabled: true + VersionAdded: "0.56" + +Lint/FlipFlop: + Description: "Checks for flip-flops." + StyleGuide: "#no-flip-flops" + Enabled: true + VersionAdded: "0.16" + +Lint/FloatOutOfRange: + Description: >- + Catches floating-point literals too large or small for Ruby to + represent. + Enabled: true + VersionAdded: "0.36" + +Lint/FormatParameterMismatch: + Description: "The number of parameters to format/sprint must match the fields." + Enabled: true + VersionAdded: "0.33" + +Lint/SuppressedException: + Description: "Don't suppress exception." + StyleGuide: "#dont-hide-exceptions" + Enabled: true + AllowComments: false + VersionAdded: "0.9" + VersionChanged: "0.70" + +Lint/HeredocMethodCallPosition: + Description: >- + Checks for the ordering of a method call where + the receiver of the call is a HEREDOC. + Enabled: false + StyleGuide: "#heredoc-method-calls" + VersionAdded: "0.68" + +Lint/ImplicitStringConcatenation: + Description: >- + Checks for adjacent string literals on the same line, which + could better be represented as a single string literal. + Enabled: true + VersionAdded: "0.36" + +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 + VersionAdded: "0.36" + +Lint/InheritException: + Description: "Avoid inheriting from the `Exception` class." + Enabled: true + VersionAdded: "0.41" + # The default base class in favour of `Exception`. + EnforcedStyle: runtime_error + SupportedStyles: + - runtime_error + - standard_error + +Lint/InterpolationCheck: + Description: "Raise warning for interpolation in single q strs." + Enabled: true + VersionAdded: "0.50" + +Lint/LiteralAsCondition: + Description: "Checks of literals used in conditions." + Enabled: true + VersionAdded: "0.51" + +Lint/LiteralInInterpolation: + Description: "Checks for literals used in interpolation." + Enabled: true + VersionAdded: "0.19" + VersionChanged: "0.32" + +Lint/Loop: + Description: >- + Use Kernel#loop with break rather than begin/end/until or + begin/end/while for post-loop tests. + StyleGuide: "#loop-with-break" + Enabled: true + VersionAdded: "0.9" + +Lint/MissingCopEnableDirective: + Description: "Checks for a `# rubocop:enable` after `# rubocop:disable`." + Enabled: true + VersionAdded: "0.52" + # 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 + +Lint/MultipleComparison: + Description: "Use `&&` operator to compare multiple value." + Enabled: true + VersionAdded: "0.47" + +Lint/NestedMethodDefinition: + Description: "Do not use nested method definitions." + StyleGuide: "#no-nested-methods" + Enabled: true + VersionAdded: "0.32" + +Lint/NestedPercentLiteral: + Description: "Checks for nested percent literals." + Enabled: true + VersionAdded: "0.52" + +Lint/NextWithoutAccumulator: + Description: >- + Do not omit the accumulator when calling `next` + in a `reduce`/`inject` block. + Enabled: true + VersionAdded: "0.36" + +Lint/NonLocalExitFromIterator: + Description: "Do not use return in iterator to cause non-local exit." + Enabled: true + VersionAdded: "0.30" + +Lint/NumberConversion: + Description: "Checks unsafe usage of number conversion methods." + Enabled: false + VersionAdded: "0.53" + VersionChanged: "0.70" + SafeAutoCorrect: false + +Lint/OrderedMagicComments: + Description: "Checks the proper ordering of magic comments and whether a magic comment is not placed before a shebang." + Enabled: true + VersionAdded: "0.53" + +Lint/ParenthesesAsGroupedExpression: + Description: >- + Checks for method calls with a space before the opening + parenthesis. + StyleGuide: "#parens-no-spaces" + Enabled: true + VersionAdded: "0.12" + +Lint/PercentStringArray: + Description: >- + Checks for unwanted commas and quotes in %w/%W literals. + Enabled: true + Safe: false + VersionAdded: "0.41" + +Lint/PercentSymbolArray: + Description: >- + Checks for unwanted commas and colons in %i/%I literals. + Enabled: true + VersionAdded: "0.41" + +Lint/RandOne: + Description: >- + Checks for `rand(1)` calls. Such calls always return `0` + and most likely a mistake. + Enabled: true + VersionAdded: "0.36" + +Lint/RedundantWithIndex: + Description: "Checks for redundant `with_index`." + Enabled: true + VersionAdded: "0.50" + +Lint/RedundantWithObject: + Description: "Checks for redundant `with_object`." + Enabled: true + VersionAdded: "0.51" + +Lint/RegexpAsCondition: + Description: >- + Do not use regexp literal as a condition. + The regexp literal matches `$_` implicitly. + Enabled: true + VersionAdded: "0.51" + +Lint/RequireParentheses: + Description: >- + Use parentheses in the method call to avoid confusion + about precedence. + Enabled: true + VersionAdded: "0.18" + +Lint/RescueException: + Description: "Avoid rescuing the Exception class." + StyleGuide: "#no-blind-rescues" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.27.1" + +Lint/RescueType: + Description: "Avoid rescuing from non constants that could result in a `TypeError`." + Enabled: true + VersionAdded: "0.49" + +Lint/ReturnInVoidContext: + Description: "Checks for return in void context." + Enabled: true + VersionAdded: "0.50" + +Lint/SafeNavigationChain: + Description: "Do not chain ordinary method call after safe navigation operator." + Enabled: true + VersionAdded: "0.47" + VersionChanged: "0.56" + AllowedMethods: + - present? + - blank? + - presence + - try + - try! + +Lint/SafeNavigationConsistency: + Description: >- + Check to make sure that if safe navigation is used for a method + call in an `&&` or `||` condition that safe navigation is used + for all method calls on that same object. + Enabled: true + VersionAdded: "0.55" + AllowedMethods: + - present? + - blank? + - presence + - try + - try! + +Lint/SafeNavigationWithEmpty: + Description: "Avoid `foo&.empty?` in conditionals." + Enabled: true + VersionAdded: "0.62" + +Lint/ScriptPermission: + Description: "Grant script file execute permission." + Enabled: true + VersionAdded: "0.49" + VersionChanged: "0.50" + +Lint/SendWithMixinArgument: + Description: "Checks for `send` method when using mixin." + Enabled: true + VersionAdded: "0.75" + +Lint/ShadowedArgument: + Description: "Avoid reassigning arguments before they were used." + Enabled: true + VersionAdded: "0.52" + IgnoreImplicitReferences: false + +Lint/ShadowedException: + Description: >- + Avoid rescuing a higher level exception + before a lower level exception. + Enabled: true + VersionAdded: "0.41" + +Lint/ShadowingOuterLocalVariable: + Description: >- + Do not use the same name as outer local variable + for block arguments or block local variables. + Enabled: true + VersionAdded: "0.9" + +Lint/RedundantStringCoercion: + Description: "Checks for Object#to_s usage in string interpolation." + StyleGuide: "#no-to-s" + Enabled: true + VersionAdded: "0.19" + VersionChanged: "0.20" + +Lint/Syntax: + Description: "Checks syntax error." + Enabled: true + VersionAdded: "0.9" + +Lint/ToJSON: + Description: "Ensure #to_json includes an optional argument." + Enabled: true + +Lint/UnderscorePrefixedVariableName: + Description: "Do not use prefix `_` for a variable that is used." + Enabled: true + VersionAdded: "0.21" + AllowKeywordBlockArguments: false + +Lint/UnifiedInteger: + Description: "Use Integer instead of Fixnum or Bignum." + Enabled: true + VersionAdded: "0.43" + +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 + VersionAdded: "0.53" + +Lint/RedundantCopEnableDirective: + Description: Checks for rubocop:enable comments that can be removed. + Enabled: true + VersionAdded: "0.53" + +Lint/RedundantRequireStatement: + Description: "Checks for unnecessary `require` statement." + Enabled: true + VersionAdded: "0.51" + +Lint/RedundantSplatExpansion: + Description: "Checks for splat unnecessarily being called on literals." + Enabled: true + VersionAdded: "0.43" + +Lint/UnreachableCode: + Description: "Unreachable code." + Enabled: true + VersionAdded: "0.9" + +Lint/UnusedBlockArgument: + Description: "Checks for unused block arguments." + StyleGuide: "#underscore-unused-vars" + Enabled: true + VersionAdded: "0.21" + VersionChanged: "0.22" + IgnoreEmptyBlocks: true + AllowUnusedKeywordArguments: false + +Lint/UnusedMethodArgument: + Description: "Checks for unused method arguments." + StyleGuide: "#underscore-unused-vars" + Enabled: true + VersionAdded: "0.21" + VersionChanged: "0.35" + AllowUnusedKeywordArguments: false + IgnoreEmptyMethods: true + +Lint/UriEscapeUnescape: + Description: >- + `URI.escape` method is obsolete and should not be used. Instead, use + `CGI.escape`, `URI.encode_www_form` or `URI.encode_www_form_component` + depending on your specific use case. + Also `URI.unescape` method is obsolete and should not be used. Instead, use + `CGI.unescape`, `URI.decode_www_form` or `URI.decode_www_form_component` + depending on your specific use case. + Enabled: true + VersionAdded: "0.50" + +Lint/UriRegexp: + Description: "Use `URI::DEFAULT_PARSER.make_regexp` instead of `URI.regexp`." + Enabled: true + VersionAdded: "0.50" + +Lint/UselessAccessModifier: + Description: "Checks for useless access modifiers." + Enabled: true + VersionAdded: "0.20" + VersionChanged: "0.47" + ContextCreatingMethods: [] + MethodCreatingMethods: [] + +Lint/UselessAssignment: + Description: "Checks for useless assignment to a local variable." + StyleGuide: "#underscore-unused-vars" + Enabled: true + VersionAdded: "0.11" + +Lint/UselessComparison: + Description: "Checks for comparison of something with itself." + Enabled: true + VersionAdded: "0.11" + +Lint/UselessElseWithoutRescue: + Description: "Checks for useless `else` in `begin..end` without `rescue`." + Enabled: true + VersionAdded: "0.17" + +Lint/UselessSetterCall: + Description: "Checks for useless setter call to a local variable." + Enabled: true + VersionAdded: "0.13" + +Lint/Void: + Description: "Possible use of operator/literal/variable in void context." + Enabled: true + VersionAdded: "0.9" + CheckForMethodsWithNoSideEffects: false + +#################### Metrics ############################### + +Metrics/AbcSize: + Description: >- + A calculated magnitude based on number of assignments, + branches, and conditions. + Reference: + - http://c2.com/cgi/wiki?AbcMetric + - https://en.wikipedia.org/wiki/ABC_Software_Metric + Enabled: false + VersionAdded: "0.27" + VersionChanged: "0.66" + # The ABC size is a calculated magnitude, so this number can be an Integer or + # a Float. + Max: 15 + +Metrics/BlockLength: + Description: "Avoid long blocks with many lines." + Enabled: true + VersionAdded: "0.44" + VersionChanged: "0.66" + CountComments: false # count full line comments? + Max: 25 + ExcludedMethods: + # By default, exclude the `#refine` method, as it tends to have larger + # associated blocks. + - refine + Exclude: + - "**/*.gemspec" + +Metrics/BlockNesting: + Description: "Avoid excessive block nesting." + StyleGuide: "#three-is-the-number-thou-shalt-count" + Enabled: true + VersionAdded: "0.25" + VersionChanged: "0.47" + CountBlocks: false + Max: 3 + +Metrics/ClassLength: + Description: "Avoid classes longer than 100 lines of code." + Enabled: false + VersionAdded: "0.25" + CountComments: false # count full line comments? + Max: 100 + +# Avoid complex methods. +Metrics/CyclomaticComplexity: + Description: >- + A complexity metric that is strongly correlated to the number + of test cases needed to validate a method. + Enabled: true + VersionAdded: "0.25" + Max: 6 + +Metrics/MethodLength: + Description: "Avoid methods longer than 10 lines of code." + StyleGuide: "#short-methods" + Enabled: false + VersionAdded: "0.25" + VersionChanged: "0.59.2" + CountComments: false # count full line comments? + Max: 10 + ExcludedMethods: [] + +Layout/LineLength: + Description: "Limit lines to 80 characters." + StyleGuide: "#80-character-limits" + Enabled: false + VersionAdded: "0.25" + VersionChanged: "0.68" + AutoCorrect: false + Max: 80 + # To make it possible to copy or click on URIs in the code, we allow lines + # containing a URI to be longer than Max. + AllowHeredoc: true + AllowURI: true + URISchemes: + - http + - https + # The IgnoreCopDirectives option causes the LineLength rule to ignore cop + # directives like '# rubocop: enable ...' when calculating a line's length. + IgnoreCopDirectives: false + # The IgnoredPatterns option is a list of !ruby/regexp and/or string + # elements. Strings will be converted to Regexp objects. A line that matches + # any regular expression listed in this option will be ignored by LineLength. + IgnoredPatterns: [] + +Metrics/ModuleLength: + Description: "Avoid modules longer than 100 lines of code." + Enabled: true + VersionAdded: "0.31" + CountComments: false # count full line comments? + Max: 100 + +Metrics/ParameterLists: + Description: "Avoid parameter lists longer than three or four parameters." + StyleGuide: "#too-many-params" + Enabled: true + VersionAdded: "0.25" + Max: 5 + CountKeywordArgs: true + +Metrics/PerceivedComplexity: + Description: >- + A complexity metric geared towards measuring complexity for a + human reader. + Enabled: false + VersionAdded: "0.25" + Max: 7 + +################## Migration ############################# + +Migration/DepartmentName: + Description: >- + Check that cop names in rubocop:disable (etc) comments are + given with department name. + Enabled: false + +#################### Naming ############################## + +Naming/PredicateName: + Description: "Check the names of predicate methods." + StyleGuide: "#bool-methods-qmark" + Enabled: false + VersionAdded: "0.50" + VersionChanged: "0.51" + # Predicate name prefixes. + NamePrefix: + - is_ + - has_ + - have_ + # Predicate name prefixes that should be removed. + ForbiddenPrefixes: + - is_ + - has_ + - have_ + # Predicate names which, despite having a blacklisted prefix, or no `?`, + # should still be accepted + AllowedMethods: + - is_a? + # Method definition macros for dynamically generated methods. + MethodDefinitionMacros: + - define_method + - define_singleton_method + # Exclude Rspec specs because there is a strong convention to write spec + # helpers in the form of `have_something` or `be_something`. + Exclude: + - "spec/**/*" + +Naming/AccessorMethodName: + Description: Check the naming of accessor methods for get_/set_. + StyleGuide: "#accessor_mutator_method_names" + Enabled: true + VersionAdded: "0.50" + +Naming/AsciiIdentifiers: + Description: "Use only ascii symbols in identifiers." + StyleGuide: "#english-identifiers" + Enabled: true + VersionAdded: "0.50" + +Naming/BinaryOperatorParameterName: + Description: "When defining binary operators, name the argument other." + StyleGuide: "#other-arg" + Enabled: true + VersionAdded: "0.50" + +Naming/ClassAndModuleCamelCase: + Description: "Use CamelCase for classes and modules." + StyleGuide: "#camelcase-classes" + Enabled: true + VersionAdded: "0.50" + +Naming/ConstantName: + Description: "Constants should use SCREAMING_SNAKE_CASE." + StyleGuide: "#screaming-snake-case" + Enabled: true + VersionAdded: "0.50" + +Naming/FileName: + Description: "Use snake_case for source file names." + StyleGuide: "#snake-case-files" + Enabled: false + VersionAdded: "0.50" + # Camel case file names listed in `AllCops:Include` and all file names listed + # in `AllCops:Exclude` are excluded by default. Add extra excludes here. + Exclude: [] + # When `true`, requires that each source file should define a class or module + # with a name which matches the file name (converted to ... case). + # It further expects it to be nested inside modules which match the names + # of subdirectories in its path. + ExpectMatchingDefinition: false + # If non-`nil`, expect all source file names to match the following regex. + # Only the file name itself is matched, not the entire file path. + # Use anchors as necessary if you want to match the entire name rather than + # just a part of it. + Regex: ~ + # With `IgnoreExecutableScripts` set to `true`, this cop does not + # report offending filenames for executable scripts (i.e. source + # files with a shebang in the first line). + IgnoreExecutableScripts: true + AllowedAcronyms: + - CLI + - DSL + - ACL + - API + - ASCII + - CPU + - CSS + - DNS + - EOF + - GUID + - HTML + - HTTP + - HTTPS + - ID + - IP + - JSON + - LHS + - QPS + - RAM + - RHS + - RPC + - SLA + - SMTP + - SQL + - SSH + - TCP + - TLS + - TTL + - UDP + - UI + - UID + - UUID + - URI + - URL + - UTF8 + - VM + - XML + - XMPP + - XSRF + - XSS + +Naming/HeredocDelimiterCase: + Description: "Use configured case for heredoc delimiters." + StyleGuide: "#heredoc-delimiters" + Enabled: true + VersionAdded: "0.50" + EnforcedStyle: uppercase + SupportedStyles: + - lowercase + - uppercase + +Naming/HeredocDelimiterNaming: + Description: "Use descriptive heredoc delimiters." + StyleGuide: "#heredoc-delimiters" + Enabled: true + VersionAdded: "0.50" + ForbiddenDelimiters: + - !ruby/regexp '/(^|\s)(EO[A-Z]{1}|END)(\s|$)/' + +Naming/MemoizedInstanceVariableName: + Description: >- + Memoized method name should match memo instance variable name. + Enabled: true + VersionAdded: "0.53" + VersionChanged: "0.58" + EnforcedStyleForLeadingUnderscores: disallowed + SupportedStylesForLeadingUnderscores: + - disallowed + - required + - optional + +Naming/MethodName: + Description: "Use the configured style when naming methods." + StyleGuide: "#snake-case-symbols-methods-vars" + Enabled: true + VersionAdded: "0.50" + EnforcedStyle: snake_case + SupportedStyles: + - snake_case + - camelCase + # Method names matching patterns are always allowed. + # + # IgnoredPatterns: + # - '\A\s*onSelectionBulkChange\s*' + # - '\A\s*onSelectionCleared\s*' + # + IgnoredPatterns: [] + +Naming/RescuedExceptionsVariableName: + Description: "Use consistent rescued exceptions variables naming." + Enabled: true + VersionAdded: "0.67" + VersionChanged: "0.68" + PreferredName: e + +Naming/BlockParameterName: + Description: >- + Checks for block parameter names that contain capital letters, + end in numbers, or do not meet a minimal length. + Enabled: true + VersionAdded: "0.53" + # Parameter names may be equal to or greater than this value + MinNameLength: 1 + AllowNamesEndingInNumbers: true + # Whitelisted names that will not register an offense + AllowedNames: [] + # Blacklisted names that will register an offense + ForbiddenNames: [] + +Naming/MethodParameterName: + Description: >- + Checks for method parameter names that contain capital letters, + end in numbers, or do not meet a minimal length. + Enabled: true + VersionAdded: "0.53" + VersionChanged: "0.59" + # Parameter names may be equal to or greater than this value + MinNameLength: 3 + AllowNamesEndingInNumbers: true + # Whitelisted names that will not register an offense + AllowedNames: + - io + - id + - to + - by + - "on" + - in + - at + - ip + - db + # Blacklisted names that will register an offense + ForbiddenNames: [] + +Naming/VariableName: + Description: "Use the configured style when naming variables." + StyleGuide: "#snake-case-symbols-methods-vars" + Enabled: true + VersionAdded: "0.50" + EnforcedStyle: snake_case + SupportedStyles: + - snake_case + - camelCase + +Naming/VariableNumber: + Description: "Use the configured style when numbering variables." + Enabled: true + VersionAdded: "0.50" + EnforcedStyle: normalcase + SupportedStyles: + - snake_case + - normalcase + - non_integer + +#################### Security ############################## + +Security/Eval: + Description: "The use of eval represents a serious security risk." + Enabled: true + VersionAdded: "0.47" + +Security/JSONLoad: + Description: >- + Prefer usage of `JSON.parse` over `JSON.load` due to potential + security issues. See reference for more information. + Reference: "https://ruby-doc.org/stdlib-2.3.0/libdoc/json/rdoc/JSON.html#method-i-load" + Enabled: true + VersionAdded: "0.43" + VersionChanged: "0.44" + # Autocorrect here will change to a method that may cause crashes depending + # on the value of the argument. + AutoCorrect: false + SafeAutoCorrect: false + +Security/MarshalLoad: + Description: >- + Avoid using of `Marshal.load` or `Marshal.restore` due to potential + security issues. See reference for more information. + Reference: "https://ruby-doc.org/core-2.3.3/Marshal.html#module-Marshal-label-Security+considerations" + Enabled: true + VersionAdded: "0.47" + +Security/Open: + Description: "The use of Kernel#open represents a serious security risk." + Enabled: true + VersionAdded: "0.53" + Safe: false + +Security/YAMLLoad: + Description: >- + Prefer usage of `YAML.safe_load` over `YAML.load` due to potential + security issues. See reference for more information. + Reference: "https://ruby-doc.org/stdlib-2.3.3/libdoc/yaml/rdoc/YAML.html#module-YAML-label-Security" + Enabled: true + VersionAdded: "0.47" + SafeAutoCorrect: false + +#################### Style ############################### + +Style/AccessModifierDeclarations: + Description: "Checks style of how access modifiers are used." + Enabled: true + VersionAdded: "0.57" + EnforcedStyle: group + SupportedStyles: + - inline + - group + +Style/Alias: + Description: "Use alias instead of alias_method." + StyleGuide: "#alias-method-lexically" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.36" + EnforcedStyle: prefer_alias + SupportedStyles: + - prefer_alias + - prefer_alias_method + +Style/AndOr: + Description: "Use &&/|| instead of and/or." + StyleGuide: "#no-and-or-or" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.25" + # Whether `and` and `or` are banned only in conditionals (conditionals) + # or completely (always). + EnforcedStyle: always + SupportedStyles: + - always + - conditionals + +Style/ArrayJoin: + Description: "Use Array#join instead of Array#*." + StyleGuide: "#array-join" + Enabled: true + VersionAdded: "0.20" + VersionChanged: "0.31" + +Style/AsciiComments: + Description: "Use only ascii symbols in comments." + StyleGuide: "#english-comments" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.52" + AllowedChars: [] + +Style/Attr: + Description: "Checks for uses of Module#attr." + StyleGuide: "#attr" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.12" + +Style/AutoResourceCleanup: + Description: "Suggests the usage of an auto resource cleanup version of a method (if available)." + Enabled: false + VersionAdded: "0.30" + +Style/BarePercentLiterals: + Description: "Checks if usage of %() or %Q() matches configuration." + StyleGuide: "#percent-q-shorthand" + Enabled: true + VersionAdded: "0.25" + EnforcedStyle: bare_percent + SupportedStyles: + - percent_q + - bare_percent + +Style/BeginBlock: + Description: "Avoid the use of BEGIN blocks." + StyleGuide: "#no-BEGIN-blocks" + Enabled: true + VersionAdded: "0.9" + +Style/BlockComments: + Description: "Do not use block comments." + StyleGuide: "#no-block-comments" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.23" + +Style/BlockDelimiters: + Description: >- + Avoid using {...} for multi-line blocks (multiline chaining is + always ugly). + Prefer {...} over do...end for single-line blocks. + StyleGuide: "#single-line-blocks" + Enabled: true + VersionAdded: "0.30" + VersionChanged: "0.35" + 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 + # multi-line procedural blocks, where the primary purpose of the block is + # its side-effects. Single-line procedural blocks may only use do-end, + # unless AllowBracesOnProceduralOneLiners has a truthy value (see below). + # + # 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 + # The `always_braces` style always enforces braces. + - always_braces + 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 + IgnoredMethods: + # 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 + # The AllowBracesOnProceduralOneLiners option is ignored unless the + # EnforcedStyle is set to `semantic`. If so: + # + # If AllowBracesOnProceduralOneLiners is unspecified, or set to any + # falsey value, then semantic purity is maintained, so one-line + # procedural blocks must use do-end, not braces. + # + # # bad + # collection.each { |element| puts element } + # + # # good + # collection.each do |element| puts element end + # + # If AllowBracesOnProceduralOneLiners is set to any truthy value, + # then one-line procedural blocks may use either style. + # + # # good + # collection.each { |element| puts element } + # + # # also good + # collection.each do |element| puts element end + AllowBracesOnProceduralOneLiners: false + +Style/CaseEquality: + Description: "Avoid explicit use of the case equality operator(===)." + StyleGuide: "#no-case-equality" + Enabled: true + VersionAdded: "0.9" + +Style/CharacterLiteral: + Description: "Checks for uses of character literals." + StyleGuide: "#no-character-literals" + Enabled: true + VersionAdded: "0.9" + +Style/ClassAndModuleChildren: + Description: "Checks style of children classes and modules." + StyleGuide: "#namespace-definition" + # Moving from compact to nested children requires knowledge of whether the + # outer parent is a module or a class. Moving from nested to compact requires + # verification that the outer parent is defined elsewhere. Rubocop does not + # have the knowledge to perform either operation safely and thus requires + # manual oversight. + SafeAutoCorrect: false + AutoCorrect: false + Enabled: false + VersionAdded: "0.19" + # + # Basically there are two different styles: + # + # `nested` - have each child on a separate line + # class Foo + # class Bar + # end + # end + # + # `compact` - combine definitions as much as possible + # class Foo::Bar + # end + # + # The compact style is only forced, for classes or modules with one child. + EnforcedStyle: nested + SupportedStyles: + - nested + - compact + +Style/ClassCheck: + Description: "Enforces consistent use of `Object#is_a?` or `Object#kind_of?`." + Enabled: true + VersionAdded: "0.24" + EnforcedStyle: is_a? + SupportedStyles: + - is_a? + - kind_of? + +Style/ClassMethods: + Description: "Use self when defining module/class methods." + StyleGuide: "#def-self-class-methods" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.20" + +Style/ClassVars: + Description: "Avoid the use of class variables." + StyleGuide: "#no-class-vars" + Enabled: true + VersionAdded: "0.13" + +# Align with the style guide. +Style/CollectionMethods: + Description: "Preferred collection methods." + StyleGuide: "#map-find-select-reduce-size" + Enabled: false + VersionAdded: "0.9" + VersionChanged: "0.27" + Safe: false + # Mapping from undesired method to desired method + # e.g. to use `detect` over `find`: + # + # Style/CollectionMethods: + # PreferredMethods: + # find: detect + PreferredMethods: + collect: "map" + collect!: "map!" + inject: "reduce" + detect: "find" + find_all: "select" + +Style/ColonMethodCall: + Description: "Do not use :: for method call." + StyleGuide: "#double-colons" + Enabled: true + VersionAdded: "0.9" + +Style/ColonMethodDefinition: + Description: "Do not use :: for defining class methods." + StyleGuide: "#colon-method-definition" + Enabled: true + VersionAdded: "0.52" + +Style/CommandLiteral: + Description: "Use `` or %x around command literals." + StyleGuide: "#percent-x" + Enabled: true + VersionAdded: "0.30" + EnforcedStyle: backticks + # backticks: Always use backticks. + # percent_x: Always use `%x`. + # mixed: Use backticks on single-line commands, and `%x` on multi-line commands. + SupportedStyles: + - backticks + - percent_x + - mixed + # If `false`, the cop will always recommend using `%x` if one or more backticks + # are found in the command string. + AllowInnerBackticks: false + +# Checks formatting of special comments +Style/CommentAnnotation: + Description: >- + Checks formatting of special comments + (TODO, FIXME, OPTIMIZE, HACK, REVIEW). + StyleGuide: "#annotate-keywords" + Enabled: true + VersionAdded: "0.10" + VersionChanged: "0.31" + Keywords: + - TODO + - FIXME + - OPTIMIZE + - HACK + - REVIEW + +Style/CommentedKeyword: + Description: "Do not place comments on the same line as certain keywords." + Enabled: true + VersionAdded: "0.51" + +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: true + VersionAdded: "0.36" + VersionChanged: "0.47" + EnforcedStyle: assign_to_condition + SupportedStyles: + - assign_to_condition + - assign_inside_condition + # When configured to `assign_to_condition`, `SingleLineConditionsOnly` + # will only register an offense when all branches of a condition are + # a single line. + # When configured to `assign_inside_condition`, `SingleLineConditionsOnly` + # will only register an offense for assignment to a condition that has + # at least one multiline branch. + SingleLineConditionsOnly: true + IncludeTernaryExpressions: true + +Style/ConstantVisibility: + Description: >- + Check that class- and module constants have + visibility declarations. + Enabled: false + VersionAdded: "0.66" + +# Checks that you have put a copyright in a comment before any code. +# +# You can override the default Notice in your .rubocop.yml file. +# +# In order to use autocorrect, you must supply a value for the +# `AutocorrectNotice` key that matches the regexp Notice. A blank +# `AutocorrectNotice` will cause an error during autocorrect. +# +# Autocorrect will add a copyright notice in a comment at the top +# of the file immediately after any shebang or encoding comments. +# +# Example rubocop.yml: +# +# Style/Copyright: +# Enabled: true +# Notice: 'Copyright (\(c\) )?2015 Yahoo! Inc' +# AutocorrectNotice: '# Copyright (c) 2015 Yahoo! Inc.' +# +Style/Copyright: + Description: "Include a copyright notice in each file before any code." + Enabled: false + VersionAdded: "0.30" + Notice: '^Copyright (\(c\) )?2[0-9]{3} .+' + AutocorrectNotice: "" + +Style/DateTime: + Description: "Use Time over DateTime." + StyleGuide: "#date--time" + Enabled: false + VersionAdded: "0.51" + VersionChanged: "0.59" + AllowCoercion: false + +Style/DefWithParentheses: + Description: "Use def with parentheses when there are arguments." + StyleGuide: "#method-parens" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.12" + +Style/Dir: + Description: >- + Use the `__dir__` method to retrieve the canonicalized + absolute path to the current file. + Enabled: true + VersionAdded: "0.50" + +Style/Documentation: + Description: "Document classes and non-namespace modules." + Enabled: false + VersionAdded: "0.9" + Exclude: + - "spec/**/*" + - "test/**/*" + +Style/DocumentationMethod: + Description: "Checks for missing documentation comment for public methods." + Enabled: false + VersionAdded: "0.43" + Exclude: + - "spec/**/*" + - "test/**/*" + RequireForNonPublicMethods: false + +Style/DoubleCopDisableDirective: + Description: "Checks for double rubocop:disable comments on a single line." + Enabled: true + VersionAdded: "0.73" + +Style/DoubleNegation: + Description: "Checks for uses of double negation (!!)." + StyleGuide: "#no-bang-bang" + Enabled: true + VersionAdded: "0.19" + +Style/EachForSimpleLoop: + Description: >- + Use `Integer#times` for a simple loop which iterates a fixed + number of times. + Enabled: true + VersionAdded: "0.41" + +Style/EachWithObject: + Description: "Prefer `each_with_object` over `inject` or `reduce`." + Enabled: true + VersionAdded: "0.22" + VersionChanged: "0.42" + +Style/EmptyBlockParameter: + Description: "Omit pipes for empty block parameters." + Enabled: true + VersionAdded: "0.52" + +Style/EmptyCaseCondition: + Description: "Avoid empty condition in case statements." + Enabled: true + VersionAdded: "0.40" + +Style/EmptyElse: + Description: "Avoid empty else-clauses." + Enabled: true + VersionAdded: "0.28" + VersionChanged: "0.32" + EnforcedStyle: both + # empty - warn only on empty `else` + # nil - warn on `else` with nil in it + # both - warn on empty `else` and `else` with `nil` in it + SupportedStyles: + - empty + - nil + - both + +Style/EmptyLambdaParameter: + Description: "Omit parens for empty lambda parameters." + Enabled: true + VersionAdded: "0.52" + +Style/EmptyLiteral: + Description: "Prefer literals to Array.new/Hash.new/String.new." + StyleGuide: "#literal-array-hash" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.12" + +Style/EmptyMethod: + Description: "Checks the formatting of empty method definitions." + StyleGuide: "#no-single-line-methods" + Enabled: true + VersionAdded: "0.46" + EnforcedStyle: compact + SupportedStyles: + - compact + - expanded + +Style/Encoding: + Description: "Use UTF-8 as the source file encoding." + StyleGuide: "#utf-8" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.50" + +Style/EndBlock: + Description: "Avoid the use of END blocks." + StyleGuide: "#no-END-blocks" + Enabled: true + VersionAdded: "0.9" + +Style/EvalWithLocation: + Description: "Pass `__FILE__` and `__LINE__` to `eval` method, as they are used by backtraces." + Enabled: true + VersionAdded: "0.52" + +Style/EvenOdd: + Description: "Favor the use of `Integer#even?` && `Integer#odd?`." + StyleGuide: "#predicate-methods" + Enabled: true + VersionAdded: "0.12" + VersionChanged: "0.29" + +Style/ExpandPathArguments: + Description: "Use `expand_path(__dir__)` instead of `expand_path('..', __FILE__)`." + Enabled: true + VersionAdded: "0.53" + +Style/FloatDivision: + Description: "For performing float division, coerce one side only." + StyleGuide: "#float-division" + Reference: "https://github.com/rubocop-hq/ruby-style-guide/issues/628" + Enabled: true + VersionAdded: "0.72" + EnforcedStyle: single_coerce + SupportedStyles: + - left_coerce + - right_coerce + - single_coerce + - fdiv + +Style/For: + Description: "Checks use of for or each in multiline loops." + StyleGuide: "#no-for-loops" + Enabled: true + VersionAdded: "0.13" + VersionChanged: "0.59" + EnforcedStyle: each + SupportedStyles: + - each + - for + +Style/FormatString: + Description: "Enforce the use of Kernel#sprintf, Kernel#format or String#%." + StyleGuide: "#sprintf" + Enabled: true + VersionAdded: "0.19" + VersionChanged: "0.49" + EnforcedStyle: format + SupportedStyles: + - format + - sprintf + - percent + +Style/FormatStringToken: + Description: "Use a consistent style for format string tokens." + Enabled: true + EnforcedStyle: annotated + SupportedStyles: + # Prefer tokens which contain a sprintf like type annotation like + # `%s`, `%d`, `%f` + - annotated + # Prefer simple looking "template" style tokens like `%{name}`, `%{age}` + - template + - unannotated + VersionAdded: "0.49" + VersionChanged: "0.75" + +Style/FrozenStringLiteralComment: + Description: >- + Add the frozen_string_literal comment to the top of files + to help transition to frozen string literals by default. + Enabled: false + VersionAdded: "0.36" + VersionChanged: "0.69" + EnforcedStyle: always + SupportedStyles: + # `always` 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. If you run code against multiple versions of Ruby, it is + # possible that this will create errors in Ruby 2.3.0+. + - always + # `never` will enforce that the frozen string literal comment does not + # exist in a file. + - never + +Style/GlobalVars: + Description: "Do not introduce global variables." + StyleGuide: "#instance-vars" + Reference: "https://www.zenspider.com/ruby/quickref.html" + Enabled: true + VersionAdded: "0.13" + # Built-in global variables are allowed by default. + AllowedVariables: [] + +Style/GuardClause: + Description: "Check for conditionals that can be replaced with guard clauses." + StyleGuide: "#no-nested-conditionals" + Enabled: true + VersionAdded: "0.20" + VersionChanged: "0.22" + # `MinBodyLength` defines the number of lines of the a body of an `if` or `unless` + # needs to have to trigger this cop + MinBodyLength: 1 + +Style/HashSyntax: + Description: >- + Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax + { :a => 1, :b => 2 }. + StyleGuide: "#hash-literals" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.43" + EnforcedStyle: ruby19 + SupportedStyles: + # checks for 1.9 syntax (e.g. {a: 1}) for all symbol keys + - ruby19 + # checks for hash rocket syntax for all hashes + - hash_rockets + # forbids mixed key syntaxes (e.g. {a: 1, :b => 2}) + - no_mixed_keys + # enforces both ruby19 and no_mixed_keys styles + - ruby19_no_mixed_keys + # Force hashes that have a symbol value to use hash rockets + UseHashRocketsWithSymbolValues: false + # Do not suggest { a?: 1 } over { :a? => 1 } in ruby19 style + PreferHashRocketsForNonAlnumEndingSymbols: 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: true + VersionAdded: "0.36" + +Style/IfInsideElse: + Description: "Finds if nodes inside else, which can be converted to elsif." + Enabled: true + AllowIfModifier: false + VersionAdded: "0.36" + +Style/IfUnlessModifier: + Description: >- + Favor modifier if/unless usage when you have a + single-line body. + StyleGuide: "#if-as-a-modifier" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.30" + +Style/IfUnlessModifierOfIfUnless: + Description: >- + Avoid modifier if/unless usage on conditionals. + Enabled: true + VersionAdded: "0.39" + +Style/IfWithSemicolon: + Description: "Do not use if x; .... Use the ternary operator instead." + StyleGuide: "#no-semicolon-ifs" + Enabled: true + VersionAdded: "0.9" + +Style/ImplicitRuntimeError: + Description: >- + Use `raise` or `fail` with an explicit exception class and + message, rather than just a message. + Enabled: false + VersionAdded: "0.41" + +Style/InfiniteLoop: + Description: "Use Kernel#loop for infinite loops." + StyleGuide: "#infinite-loop" + Enabled: true + VersionAdded: "0.26" + VersionChanged: "0.61" + SafeAutoCorrect: true + +Style/InlineComment: + Description: "Avoid trailing inline comments." + Enabled: false + VersionAdded: "0.23" + +Style/InverseMethods: + Description: >- + Use the inverse method instead of `!.method` + if an inverse method is defined. + Enabled: true + Safe: false + VersionAdded: "0.48" + # `InverseMethods` are methods that can be inverted by a not (`not` or `!`) + # The relationship of inverse methods only needs to be defined in one direction. + # Keys and values both need to be defined as symbols. + InverseMethods: + :any?: :none? + :even?: :odd? + :==: :!= + :=~: :!~ + :<: :>= + :>: :<= + # `ActiveSupport` defines some common inverse methods. They are listed below, + # and not enabled by default. + #:present?: :blank?, + #:include?: :exclude? + # `InverseBlocks` are methods that are inverted by inverting the return + # of the block that is passed to the method + InverseBlocks: + :select: :reject + :select!: :reject! + +Style/IpAddresses: + Description: "Don't include literal IP addresses in code." + Enabled: false + VersionAdded: "0.58" + # Allow strings to be whitelisted + AllowedAddresses: + - "::" + # :: is a valid IPv6 address, but could potentially be legitimately in code + +Style/Lambda: + Description: "Use the new lambda literal syntax for single-line blocks." + StyleGuide: "#lambda-multi-line" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.40" + EnforcedStyle: line_count_dependent + SupportedStyles: + - line_count_dependent + - lambda + - literal + +Style/LambdaCall: + Description: "Use lambda.call(...) instead of lambda.(...)." + StyleGuide: "#proc-call" + Enabled: true + VersionAdded: "0.13.1" + VersionChanged: "0.14" + EnforcedStyle: call + SupportedStyles: + - call + - braces + +Style/LineEndConcatenation: + Description: >- + Use \ instead of + or << to concatenate two string literals at + line end. + Enabled: true + SafeAutoCorrect: false + VersionAdded: "0.18" + VersionChanged: "0.64" + +Style/MethodCallWithArgsParentheses: + Description: "Use parentheses for method calls with arguments." + StyleGuide: "#method-invocation-parens" + Enabled: false + VersionAdded: "0.47" + VersionChanged: "0.61" + IgnoreMacros: true + IgnoredMethods: [] + IgnoredPatterns: [] + IncludedMacros: [] + AllowParenthesesInMultilineCall: false + AllowParenthesesInChaining: false + AllowParenthesesInCamelCaseMethod: false + EnforcedStyle: require_parentheses + SupportedStyles: + - require_parentheses + - omit_parentheses + +Style/MethodCallWithoutArgsParentheses: + Description: "Do not use parentheses for method calls with no arguments." + StyleGuide: "#method-invocation-parens" + Enabled: true + IgnoredMethods: [] + VersionAdded: "0.47" + VersionChanged: "0.55" + +Style/MethodCalledOnDoEndBlock: + Description: "Avoid chaining a method call on a do...end block." + StyleGuide: "#single-line-blocks" + Enabled: false + VersionAdded: "0.14" + +Style/MethodDefParentheses: + Description: >- + Checks if the method definitions have or don't have + parentheses. + StyleGuide: "#method-parens" + Enabled: true + VersionAdded: "0.16" + VersionChanged: "0.35" + EnforcedStyle: require_parentheses + SupportedStyles: + - require_parentheses + - require_no_parentheses + - require_no_parentheses_except_multiline + +Style/MethodMissingSuper: + Description: Checks for `method_missing` to call `super`. + StyleGuide: "#no-method-missing" + Enabled: true + VersionAdded: "0.56" + +Style/MinMax: + Description: >- + Use `Enumerable#minmax` instead of `Enumerable#min` + and `Enumerable#max` in conjunction. + Enabled: true + VersionAdded: "0.50" + +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 + VersionAdded: "0.30" + VersionChanged: "0.38" + EnforcedStyle: both + SupportedStyles: + # if - warn when an if expression is missing an else branch + # case - warn when a case expression is missing an else branch + # both - warn when an if or case expression is missing an else branch + - if + - case + - both + +Style/MissingRespondToMissing: + Description: >- + Checks if `method_missing` is implemented + without implementing `respond_to_missing`. + StyleGuide: "#no-method-missing" + Enabled: true + VersionAdded: "0.56" + +Style/MixinGrouping: + Description: "Checks for grouping of mixins in `class` and `module` bodies." + StyleGuide: "#mixin-grouping" + Enabled: true + VersionAdded: "0.48" + VersionChanged: "0.49" + EnforcedStyle: separated + SupportedStyles: + # separated: each mixed in module goes in a separate statement. + # grouped: mixed in modules are grouped into a single statement. + - separated + - grouped + +Style/MixinUsage: + Description: "Checks that `include`, `extend` and `prepend` exists at the top level." + Enabled: true + VersionAdded: "0.51" + +Style/ModuleFunction: + Description: "Checks for usage of `extend self` in modules." + StyleGuide: "#module-function" + Enabled: true + VersionAdded: "0.11" + VersionChanged: "0.65" + EnforcedStyle: module_function + SupportedStyles: + - module_function + - extend_self + Autocorrect: false + SafeAutoCorrect: false + +Style/MultilineBlockChain: + Description: "Avoid multi-line chains of blocks." + StyleGuide: "#single-line-blocks" + Enabled: true + VersionAdded: "0.13" + +Style/MultilineIfModifier: + Description: "Only use if/unless modifiers on single line statements." + StyleGuide: "#no-multiline-if-modifiers" + Enabled: true + VersionAdded: "0.45" + +Style/MultilineIfThen: + Description: "Do not use then for multi-line if/unless." + StyleGuide: "#no-then" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.26" + +Style/MultilineMemoization: + Description: "Wrap multiline memoizations in a `begin` and `end` block." + Enabled: true + VersionAdded: "0.44" + VersionChanged: "0.48" + EnforcedStyle: keyword + SupportedStyles: + - keyword + - braces + +Style/MultilineMethodSignature: + Description: "Avoid multi-line method signatures." + Enabled: false + VersionAdded: "0.59" + +Style/MultilineTernaryOperator: + Description: >- + Avoid multi-line ?: (the ternary operator); + use if/unless instead. + StyleGuide: "#no-multiline-ternary" + Enabled: true + VersionAdded: "0.9" + +Style/MultilineWhenThen: + Description: "Do not use then for multi-line when statement." + StyleGuide: "#no-then" + Enabled: true + VersionAdded: "0.73" + +Style/MultipleComparison: + Description: >- + Avoid comparing a variable with multiple items in a conditional, + use Array#include? instead. + Enabled: true + VersionAdded: "0.49" + +Style/MutableConstant: + Description: "Do not assign mutable objects to constants." + Enabled: true + VersionAdded: "0.34" + VersionChanged: "0.65" + EnforcedStyle: literals + SupportedStyles: + # literals: freeze literals assigned to constants + # strict: freeze all constants + # Strict mode is considered an experimental feature. It has not been updated + # with an exhaustive list of all methods that will produce frozen objects so + # there is a decent chance of getting some false positives. Luckily, there is + # no harm in freezing an already frozen object. + - literals + - strict + +Style/NegatedIf: + Description: >- + Favor unless over if for negative conditions + (or control flow or). + StyleGuide: "#unless-for-negatives" + Enabled: true + VersionAdded: "0.20" + VersionChanged: "0.48" + EnforcedStyle: both + SupportedStyles: + # both: prefix and postfix negated `if` should both use `unless` + # prefix: only use `unless` for negated `if` statements positioned before the body of the statement + # postfix: only use `unless` for negated `if` statements positioned after the body of the statement + - both + - prefix + - postfix + +Style/NegatedUnless: + Description: "Favor if over unless for negative conditions." + StyleGuide: "#if-for-negatives" + Enabled: true + VersionAdded: "0.69" + EnforcedStyle: both + SupportedStyles: + # both: prefix and postfix negated `unless` should both use `if` + # prefix: only use `if` for negated `unless` statements positioned before the body of the statement + # postfix: only use `if` for negated `unless` statements positioned after the body of the statement + - both + - prefix + - postfix + +Style/NegatedWhile: + Description: "Favor until over while for negative conditions." + StyleGuide: "#until-for-negatives" + Enabled: true + VersionAdded: "0.20" + +Style/NestedModifier: + Description: "Avoid using nested modifiers." + StyleGuide: "#no-nested-modifiers" + Enabled: true + VersionAdded: "0.35" + +Style/NestedParenthesizedCalls: + Description: >- + Parenthesize method calls which are nested inside the + argument list of another parenthesized method call. + Enabled: true + VersionAdded: "0.36" + VersionChanged: "0.50" + AllowedMethods: + - be + - be_a + - be_an + - be_between + - be_falsey + - be_kind_of + - be_instance_of + - be_truthy + - be_within + - eq + - eql + - end_with + - include + - match + - raise_error + - respond_to + - start_with + +Style/NestedTernaryOperator: + Description: "Use one expression per branch in a ternary operator." + StyleGuide: "#no-nested-ternary" + Enabled: true + VersionAdded: "0.9" + +Style/Next: + Description: "Use `next` to skip iteration instead of a condition at the end." + StyleGuide: "#no-nested-conditionals" + Enabled: true + VersionAdded: "0.22" + VersionChanged: "0.35" + # With `always` all conditions at the end of an iteration needs to be + # replaced by next - with `skip_modifier_ifs` the modifier if like this one + # are ignored: [1, 2].each { |a| return 'yes' if a == 1 } + EnforcedStyle: skip_modifier_ifs + # `MinBodyLength` defines the number of lines of the a body of an `if` or `unless` + # needs to have to trigger this cop + MinBodyLength: 3 + SupportedStyles: + - skip_modifier_ifs + - always + +Style/NilComparison: + Description: "Prefer x.nil? to x == nil." + StyleGuide: "#predicate-methods" + Enabled: true + VersionAdded: "0.12" + VersionChanged: "0.59" + EnforcedStyle: predicate + SupportedStyles: + - predicate + - comparison + +Style/NonNilCheck: + Description: "Checks for redundant nil checks." + StyleGuide: "#no-non-nil-checks" + Enabled: true + VersionAdded: "0.20" + VersionChanged: "0.22" + # With `IncludeSemanticChanges` set to `true`, this cop reports offenses for + # `!x.nil?` and autocorrects that and `x != nil` to solely `x`, which is + # **usually** OK, but might change behavior. + # + # With `IncludeSemanticChanges` set to `false`, this cop does not report + # offenses for `!x.nil?` and does no changes that might change behavior. + IncludeSemanticChanges: false + +Style/Not: + Description: "Use ! instead of not." + StyleGuide: "#bang-not-not" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.20" + +Style/NumericLiteralPrefix: + Description: "Use smallcase prefixes for numeric literals." + StyleGuide: "#numeric-literal-prefixes" + Enabled: true + VersionAdded: "0.41" + EnforcedOctalStyle: zero_with_o + SupportedOctalStyles: + - zero_with_o + - zero_only + +Style/NumericLiterals: + Description: >- + Add underscores to large numeric literals to improve their + readability. + StyleGuide: "#underscores-in-numerics" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.48" + MinDigits: 5 + Strict: false + +Style/NumericPredicate: + Description: >- + Checks for the use of predicate- or comparison methods for + numeric comparisons. + StyleGuide: "#predicate-methods" + Safe: false + # This will change to a new method call which isn't guaranteed to be on the + # object. Switching these methods has to be done with knowledge of the types + # of the variables which rubocop doesn't have. + SafeAutoCorrect: false + AutoCorrect: false + Enabled: true + VersionAdded: "0.42" + VersionChanged: "0.59" + EnforcedStyle: predicate + SupportedStyles: + - predicate + - comparison + IgnoredMethods: [] + # Exclude RSpec specs because assertions like `expect(1).to be > 0` cause + # false positives. + Exclude: + - "spec/**/*" + +Style/OneLineConditional: + Description: >- + Favor the ternary operator(?:) over + if/then/else/end constructs. + StyleGuide: "#ternary-operator" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.38" + +Style/OptionHash: + Description: "Don't use option hashes when you can use keyword arguments." + Enabled: false + VersionAdded: "0.33" + VersionChanged: "0.34" + # A list of parameter names that will be flagged by this cop. + 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: "#optional-arguments" + Enabled: true + VersionAdded: "0.33" + +Style/OrAssignment: + Description: "Recommend usage of double pipe equals (||=) where applicable." + StyleGuide: "#double-pipe-for-uninit" + Enabled: true + VersionAdded: "0.50" + +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: "#parallel-assignment" + Enabled: true + VersionAdded: "0.32" + +Style/ParenthesesAroundCondition: + Description: >- + Don't use parentheses around the condition of an + if/unless/while. + StyleGuide: "#no-parens-around-condition" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.56" + AllowSafeAssignment: true + AllowInMultilineConditions: false + +Style/PercentLiteralDelimiters: + Description: "Use `%`-literal delimiters consistently." + StyleGuide: "#percent-literal-braces" + Enabled: false + VersionAdded: "0.19" + # Specify the default preferred delimiter for all types with the 'default' key + # Override individual delimiters (even with default specified) by specifying + # an individual key + PreferredDelimiters: + default: () + "%i": "[]" + "%I": "[]" + "%r": "{}" + "%w": "[]" + "%W": "[]" + VersionChanged: "0.48.1" + +Style/PercentQLiterals: + Description: "Checks if uses of %Q/%q match the configured preference." + Enabled: true + VersionAdded: "0.25" + EnforcedStyle: lower_case_q + SupportedStyles: + - lower_case_q # Use `%q` when possible, `%Q` when necessary + - upper_case_q # Always use `%Q` + +Style/PerlBackrefs: + Description: "Avoid Perl-style regex back references." + StyleGuide: "#no-perl-regexp-last-matchers" + Enabled: true + VersionAdded: "0.13" + +Style/PreferredHashMethods: + Description: "Checks use of `has_key?` and `has_value?` Hash methods." + StyleGuide: "#hash-key" + Enabled: true + Safe: false + VersionAdded: "0.41" + VersionChanged: "0.70" + EnforcedStyle: short + SupportedStyles: + - short + - verbose + +Style/Proc: + Description: "Use proc instead of Proc.new." + StyleGuide: "#proc" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.18" + +Style/RaiseArgs: + Description: "Checks the arguments passed to raise/fail." + StyleGuide: "#exception-class-messages" + Enabled: true + VersionAdded: "0.14" + VersionChanged: "0.40" + EnforcedStyle: exploded + SupportedStyles: + - compact # raise Exception.new(msg) + - exploded # raise Exception, msg + +Style/RandomWithOffset: + Description: >- + Prefer to use ranges when generating random numbers instead of + integers with offsets. + StyleGuide: "#random-numbers" + Enabled: true + VersionAdded: "0.52" + +Style/RedundantBegin: + Description: "Don't use begin blocks when they are not needed." + StyleGuide: "#begin-implicit" + Enabled: true + VersionAdded: "0.10" + VersionChanged: "0.21" + +Style/RedundantConditional: + Description: "Don't return true/false from a conditional." + Enabled: true + VersionAdded: "0.50" + +Style/RedundantException: + Description: "Checks for an obsolete RuntimeException argument in raise/fail." + StyleGuide: "#no-explicit-runtimeerror" + Enabled: true + VersionAdded: "0.14" + VersionChanged: "0.29" + +Style/RedundantFreeze: + Description: "Checks usages of Object#freeze on immutable objects." + Enabled: true + VersionAdded: "0.34" + VersionChanged: "0.66" + +Style/RedundantParentheses: + Description: "Checks for parentheses that seem not to serve any purpose." + Enabled: true + VersionAdded: "0.36" + +Style/RedundantReturn: + Description: "Don't use return where it's not required." + StyleGuide: "#no-explicit-return" + Enabled: true + VersionAdded: "0.10" + VersionChanged: "0.14" + # When `true` allows code like `return x, y`. + AllowMultipleReturnValues: false + +Style/RedundantSelf: + Description: "Don't use self where it's not needed." + StyleGuide: "#no-self-unless-required" + Enabled: true + VersionAdded: "0.10" + VersionChanged: "0.13" + +Style/RedundantSortBy: + Description: "Use `sort` instead of `sort_by { |x| x }`." + Enabled: true + VersionAdded: "0.36" + +Style/RegexpLiteral: + Description: "Use / or %r around regular expressions." + StyleGuide: "#percent-r" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.30" + EnforcedStyle: slashes + # slashes: Always use slashes. + # percent_r: Always use `%r`. + # mixed: Use slashes on single-line regexes, and `%r` on multi-line regexes. + SupportedStyles: + - slashes + - percent_r + - mixed + # If `false`, the cop will always recommend using `%r` if one or more slashes + # are found in the regexp string. + AllowInnerSlashes: false + +Style/RescueModifier: + Description: "Avoid using rescue in its modifier form." + StyleGuide: "#no-rescue-modifiers" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.34" + +Style/RescueStandardError: + Description: "Avoid rescuing without specifying an error class." + Enabled: true + VersionAdded: "0.52" + EnforcedStyle: explicit + # implicit: Do not include the error class, `rescue` + # explicit: Require an error class `rescue StandardError` + SupportedStyles: + - implicit + - explicit + +Style/ReturnNil: + Description: "Use return instead of return nil." + Enabled: false + EnforcedStyle: return + SupportedStyles: + - return + - return_nil + VersionAdded: "0.50" + +Style/SafeNavigation: + Description: >- + This cop transforms usages of a method call safeguarded by + a check for the existence of the object to + safe navigation (`&.`). + Enabled: true + VersionAdded: "0.43" + VersionChanged: "0.56" + # Safe navigation may cause a statement to start returning `nil` in addition + # to whatever it used to return. + ConvertCodeThatCanStartToReturnNil: false + AllowedMethods: + - present? + - blank? + - presence + - try + - try! + +Style/Sample: + Description: >- + Use `sample` instead of `shuffle.first`, + `shuffle.last`, and `shuffle[Integer]`. + Reference: "https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code" + Enabled: true + VersionAdded: "0.30" + +Style/SelfAssignment: + Description: >- + Checks for places where self-assignment shorthand should have + been used. + StyleGuide: "#self-assignment" + Enabled: true + VersionAdded: "0.19" + VersionChanged: "0.29" + +Style/Semicolon: + Description: "Don't use semicolons to terminate expressions." + StyleGuide: "#no-semicolon" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.19" + # Allow `;` to separate several expressions on the same line. + AllowAsExpressionSeparator: false + +Style/Send: + Description: "Prefer `Object#__send__` or `Object#public_send` to `send`, as `send` may overlap with existing methods." + StyleGuide: "#prefer-public-send" + Enabled: false + VersionAdded: "0.33" + +Style/SignalException: + Description: "Checks for proper usage of fail and raise." + StyleGuide: "#prefer-raise-over-fail" + Enabled: true + VersionAdded: "0.11" + VersionChanged: "0.37" + EnforcedStyle: only_raise + SupportedStyles: + - only_raise + - only_fail + - semantic + +Style/SingleLineBlockParams: + Description: "Enforces the names of some block params." + Enabled: false + VersionAdded: "0.16" + VersionChanged: "0.47" + Methods: + - reduce: + - acc + - elem + - inject: + - acc + - elem + +Style/SingleLineMethods: + Description: "Avoid single-line methods." + StyleGuide: "#no-single-line-methods" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.19" + AllowIfMethodIsEmpty: true + +Style/SpecialGlobalVars: + Description: "Avoid Perl-style global variables." + StyleGuide: "#no-cryptic-perlisms" + Enabled: true + VersionAdded: "0.13" + VersionChanged: "0.36" + SafeAutoCorrect: false + EnforcedStyle: use_english_names + SupportedStyles: + - use_perl_names + - use_english_names + +Style/StabbyLambdaParentheses: + Description: "Check for the usage of parentheses around stabby lambda arguments." + StyleGuide: "#stabby-lambda-with-args" + Enabled: true + VersionAdded: "0.35" + EnforcedStyle: require_parentheses + SupportedStyles: + - require_parentheses + - require_no_parentheses + +Style/StderrPuts: + Description: "Use `warn` instead of `$stderr.puts`." + StyleGuide: "#warn" + Enabled: true + VersionAdded: "0.51" + +Style/StringHashKeys: + Description: "Prefer symbols instead of strings as hash keys." + StyleGuide: "#symbols-as-keys" + Enabled: false + VersionAdded: "0.52" + VersionChanged: "0.75" + Safe: false + +Style/StringLiterals: + Description: "Checks if uses of quotes match the configured preference." + StyleGuide: "#consistent-string-literals" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.36" + EnforcedStyle: single_quotes + SupportedStyles: + - single_quotes + - double_quotes + # If `true`, strings which span multiple lines using `\` for continuation must + # use the same type of quotes on each line. + ConsistentQuotesInMultiline: false + +Style/StringLiteralsInInterpolation: + Description: >- + Checks if uses of quotes inside expressions in interpolated + strings match the configured preference. + Enabled: true + VersionAdded: "0.27" + EnforcedStyle: single_quotes + SupportedStyles: + - single_quotes + - double_quotes + +Style/StringMethods: + Description: "Checks if configured preferred methods are used over non-preferred." + Enabled: false + VersionAdded: "0.34" + VersionChanged: "0.34.2" + # Mapping from undesired method to desired_method + # e.g. to use `to_sym` over `intern`: + # + # StringMethods: + # PreferredMethods: + # intern: to_sym + PreferredMethods: + intern: to_sym + +Style/Strip: + Description: "Use `strip` instead of `lstrip.rstrip`." + Enabled: true + VersionAdded: "0.36" + +Style/StructInheritance: + Description: "Checks for inheritance from Struct.new." + StyleGuide: "#no-extend-struct-new" + Enabled: true + VersionAdded: "0.29" + +Style/SymbolArray: + Description: "Use %i or %I for arrays of symbols." + StyleGuide: "#percent-i" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.49" + EnforcedStyle: percent + MinSize: 2 + SupportedStyles: + - percent + - brackets + +Style/SymbolLiteral: + Description: "Use plain symbols instead of string symbols when possible." + Enabled: true + VersionAdded: "0.30" + +Style/SymbolProc: + Description: "Use symbols as procs instead of blocks when possible." + Enabled: true + SafeAutoCorrect: false + VersionAdded: "0.26" + VersionChanged: "0.64" + # A list of method names to be ignored by the check. + # The names should be fairly unique, otherwise you'll end up ignoring lots of code. + IgnoredMethods: + - respond_to + - define_method + +Style/TernaryParentheses: + Description: "Checks for use of parentheses around ternary conditions." + Enabled: true + VersionAdded: "0.42" + VersionChanged: "0.46" + EnforcedStyle: require_no_parentheses + SupportedStyles: + - require_parentheses + - require_no_parentheses + - require_parentheses_when_complex + AllowSafeAssignment: true + +Style/TrailingBodyOnClass: + Description: "Class body goes below class statement." + Enabled: true + VersionAdded: "0.53" + +Style/TrailingBodyOnMethodDefinition: + Description: "Method body goes below definition." + Enabled: true + VersionAdded: "0.52" + +Style/TrailingBodyOnModule: + Description: "Module body goes below module statement." + Enabled: true + VersionAdded: "0.53" + +Style/TrailingCommaInArguments: + Description: "Checks for trailing comma in argument lists." + StyleGuide: "#no-trailing-params-comma" + Enabled: true + VersionAdded: "0.36" + # If `comma`, the cop requires a comma after the last argument, but only for + # parenthesized method calls where each argument is on its own line. + # If `consistent_comma`, the cop requires a comma after the last argument, + # for all parenthesized method calls with arguments. + EnforcedStyleForMultiline: no_comma + SupportedStylesForMultiline: + - comma + - consistent_comma + - no_comma + +Style/TrailingCommaInArrayLiteral: + Description: "Checks for trailing comma in array literals." + StyleGuide: "#no-trailing-array-commas" + Enabled: true + VersionAdded: "0.53" + # but only when each item is on its own line. + # If `consistent_comma`, the cop requires a comma after the last item of all + # non-empty array literals. + EnforcedStyleForMultiline: no_comma + SupportedStylesForMultiline: + - comma + - consistent_comma + - no_comma + +Style/TrailingCommaInHashLiteral: + Description: "Checks for trailing comma in hash literals." + Enabled: true + # If `comma`, the cop requires a comma after the last item in a hash, + # but only when each item is on its own line. + # If `consistent_comma`, the cop requires a comma after the last item of all + # non-empty hash literals. + EnforcedStyleForMultiline: no_comma + SupportedStylesForMultiline: + - comma + - consistent_comma + - no_comma + VersionAdded: "0.53" + +Style/TrailingMethodEndStatement: + Description: "Checks for trailing end statement on line of method body." + Enabled: true + VersionAdded: "0.52" + +Style/TrailingUnderscoreVariable: + Description: >- + Checks for the usage of unneeded trailing underscores at the + end of parallel variable assignment. + AllowNamedUnderscoreVariables: true + Enabled: true + VersionAdded: "0.31" + VersionChanged: "0.35" + +# `TrivialAccessors` requires exact name matches and doesn't allow +# predicated methods by default. +Style/TrivialAccessors: + Description: "Prefer attr_* methods to trivial readers/writers." + StyleGuide: "#attr_family" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.38" + # When set to `false` the cop will suggest the use of accessor methods + # in situations like: + # + # def name + # @other_name + # end + # + # This way you can uncover "hidden" attributes in your code. + ExactNameMatch: true + AllowPredicates: true + # Allows trivial writers that don't end in an equal sign. e.g. + # + # def on_exception(action) + # @on_exception=action + # end + # on_exception :restart + # + # Commonly used in DSLs + AllowDSLWriters: false + IgnoreClassMethods: false + AllowedMethods: + - to_ary + - to_a + - to_c + - to_enum + - to_h + - to_hash + - to_i + - to_int + - to_io + - to_open + - to_path + - to_proc + - to_r + - to_regexp + - to_str + - to_s + - to_sym + +Style/UnlessElse: + Description: >- + Do not use unless with else. Rewrite these with the positive + case first. + StyleGuide: "#no-else-with-unless" + Enabled: true + VersionAdded: "0.9" + +Style/RedundantCapitalW: + Description: "Checks for %W when interpolation is not needed." + Enabled: true + VersionAdded: "0.21" + +Style/RedundantCondition: + Description: "Checks for unnecessary conditional expressions." + Enabled: true + VersionAdded: "0.57" + +Style/RedundantInterpolation: + Description: "Checks for strings that are just an interpolated expression." + Enabled: true + VersionAdded: "0.36" + +Style/RedundantPercentQ: + Description: "Checks for %q/%Q when single quotes or double quotes would do." + StyleGuide: "#percent-q" + Enabled: false + VersionAdded: "0.24" + +Style/RedundantSort: + Description: >- + Use `min` instead of `sort.first`, + `max_by` instead of `sort_by...last`, etc. + Enabled: true + VersionAdded: "0.55" + +Style/UnpackFirst: + Description: >- + Checks for accessing the first element of `String#unpack` + instead of using `unpack1`. + Enabled: true + VersionAdded: "0.54" + +Style/VariableInterpolation: + Description: >- + Don't interpolate global, instance and class variables + directly in strings. + StyleGuide: "#curlies-interpolate" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.20" + +Style/WhenThen: + Description: "Use when x then ... for one-line cases." + StyleGuide: "#one-line-cases" + Enabled: true + VersionAdded: "0.9" + +Style/WhileUntilDo: + Description: "Checks for redundant do after while or until." + StyleGuide: "#no-multiline-while-do" + Enabled: true + VersionAdded: "0.9" + +Style/WhileUntilModifier: + Description: >- + Favor modifier while/until usage when you have a + single-line body. + StyleGuide: "#while-as-a-modifier" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.30" + +Style/WordArray: + Description: "Use %w or %W for arrays of words." + StyleGuide: "#percent-w" + Enabled: true + VersionAdded: "0.9" + VersionChanged: "0.36" + EnforcedStyle: percent + SupportedStyles: + # percent style: %w(word1 word2) + - percent + # bracket style: ['word1', 'word2'] + - brackets + # The `MinSize` option causes the `WordArray` rule to be ignored for arrays + # smaller than a certain size. The rule is only applied to arrays + # whose element count is greater than or equal to `MinSize`. + MinSize: 2 + # The regular expression `WordRegex` decides what is considered a word. + WordRegex: !ruby/regexp '/\A(?:\p{Word}|\p{Word}-\p{Word}|\n|\t)+\z/' + +Style/YodaCondition: + Description: "Forbid or enforce yoda conditions." + Reference: "https://en.wikipedia.org/wiki/Yoda_conditions" + Enabled: true + EnforcedStyle: forbid_for_all_comparison_operators + SupportedStyles: + # check all comparison operators + - forbid_for_all_comparison_operators + # check only equality operators: `!=` and `==` + - forbid_for_equality_operators_only + # enforce yoda for all comparison operators + - require_for_all_comparison_operators + # enforce yoda only for equality operators: `!=` and `==` + - require_for_equality_operators_only + Safe: false + VersionAdded: "0.49" + VersionChanged: "0.75" + +Style/ZeroLengthPredicate: + Description: "Use #empty? when testing for objects of length 0." + Enabled: true + Safe: false + VersionAdded: "0.37" + VersionChanged: "0.39" + +Style/HashTransformValues: + Enabled: false + +Style/HashEachMethods: + Enabled: false + +Style/HashTransformKeys: + Enabled: false diff --git a/README.md b/README.md index c7447c0..eb52a87 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Exponent Server SDK Ruby + [![Build Status](https://travis-ci.org/expo/expo-server-sdk-ruby.svg?branch=master)](https://travis-ci.org/expo/expo-server-sdk-ruby) [![Gem Version](https://badge.fury.io/rb/exponent-server-sdk.svg)](https://badge.fury.io/rb/exponent-server-sdk) @@ -28,10 +29,9 @@ $ gem install exponent-server-sdk ### Client -The push client is the preferred way. This hits the latest version of the api. - -Optional arguments: `gzip: true` +The push client is the preferred way. This hits the latest version of the api. +Optional arguments: `gzip: true` ```ruby client = Exponent::Push::Client.new @@ -47,9 +47,23 @@ messages = [{ body: "You've got mail" }] -client.publish messages +# @Deprecated +# client.publish(messages) + +# MAX 100 messages at a time +handler = client.send_messages(messages) + +# Array of all errors returned from the API +# puts handler.errors + +# you probably want to delay calling this because the service might take a few moments to send +# I would recommend reading the expo documentation regarding delivery delays +client.verify_deliveries(handler.receipt_ids) + ``` +See the getting started example. If you clone this repo, you can also use it to test locally by entering your ExponentPushToken. Otherwise it serves as a good copy pasta example to get you going. + The complete format of the messages can be found [here.](https://docs.expo.io/versions/latest/guides/push-notifications#message-format) ## Contributing diff --git a/Rakefile b/Rakefile index 5c8e578..3598a82 100644 --- a/Rakefile +++ b/Rakefile @@ -1,19 +1,19 @@ -require "bundler/gem_tasks" -require "rake/testtask" +require 'bundler/gem_tasks' +require 'rake/testtask' -def load_libs(t) - t.libs << "test" - t.libs << "lib" +def load_libs(rake_task) + rake_task.libs << 'test' + rake_task.libs << 'lib' end -Rake::TestTask.new(:test) do |t| - load_libs t - t.test_files = FileList['test/**/*-test.rb'] +Rake::TestTask.new(:test) do |rake_task| + load_libs rake_task + rake_task.test_files = FileList['test/**/*-test.rb'] end -Rake::TestTask.new(:manual_test) do |t| - load_libs t - t.test_files = FileList['manual_test.rb'] +Rake::TestTask.new(:getting_started) do |rake_task| + load_libs rake_task + rake_task.test_files = FileList['examples/getting_started.rb'] end -task :default => :test +task default: :test diff --git a/examples/getting_started.rb b/examples/getting_started.rb new file mode 100644 index 0000000..9ef0360 --- /dev/null +++ b/examples/getting_started.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'exponent-server-sdk' + +class Test + def initialize + # @client = Exponent::Push::Client.new + + # OR use GZIP to be AWESOME + @client = Exponent::Push::Client.new(gzip: true) + end + + def too_many_messages + (0..101).map { create_message } + end + + def create_message + { + # REPLACE WITH YOUR EXPONENT PUSH TOKEN LIKE: + # to: 'ExponentPushToken[g5sIEbOm2yFdzn5VdSSy9n]', + to: "ExponentPushToken[#{(0...22).map { ('a'..'z').to_a[rand(26)] }.join}]", + sound: 'default', + title: 'Hello World', + subtitle: 'This is a Push Notification', + body: 'Here\'s a little message for you...', + data: { + user_id: 1, + points: 23_434 + }, + ttl: 10, + expiration: 1_886_207_332, + priority: 'default', + badge: 0, + channelId: 'game' + } + end + + def test + # messages = too_many_messages + messages = [create_message] + + response_handler = @client.send_messages(messages) + puts response_handler.response.response_body + end +end + +Test.new.test diff --git a/exponent-server-sdk.gemspec b/exponent-server-sdk.gemspec index f364b22..8887558 100644 --- a/exponent-server-sdk.gemspec +++ b/exponent-server-sdk.gemspec @@ -1,5 +1,4 @@ -# coding: utf-8 -lib = File.expand_path('../lib', __FILE__) +lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'exponent-server-sdk/version' @@ -21,6 +20,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'typhoeus' spec.add_development_dependency 'bundler' - spec.add_development_dependency 'rake' spec.add_development_dependency 'minitest' + spec.add_development_dependency 'rake' + spec.add_development_dependency 'rubocop' end diff --git a/lib/exponent-server-sdk.rb b/lib/exponent-server-sdk.rb index bab5f82..d5f9a2b 100644 --- a/lib/exponent-server-sdk.rb +++ b/lib/exponent-server-sdk.rb @@ -1,34 +1,84 @@ require 'exponent-server-sdk/version' +require 'exponent-server-sdk/too_many_messages_error' require 'typhoeus' require 'json' +# Basic Usage: +# +# Create new client +# client = Exponent::Push::Client.new(**args) +# +# Send UPTO ~~100~~ messages per call, +# https://docs.expo.io/versions/latest/guides/push-notifications/#message-format +# response_handler = client.send_messages([list of formatted messages]) +# +# Check the response to see if any errors were re +# response_handler.errors? +# +# To process each error, iterate over the errors array +# which contains each Error class instance +# response_handler.errors +# +# There is an array of invalid ExponentPushTokens that were found in the initial /send call +# response_handler.invalid_push_tokens['ExponentPushToken[1212121212121212]'] +# +# You can use the handler to get receipt_ids +# response_handler.receipt_ids +# +# You can pass an array of receipt_ids to verify_deliveries method and +# it will populate a new ResponseHandler with any errors +# receipt_response = client.verify_deliveries(receipt_ids) + module Exponent def self.is_exponent_push_token?(token) token.start_with?('ExponentPushToken') end module Push - class Client def initialize(**args) - @http_client = args[:http_client] || Typhoeus + @http_client = args[:http_client] || Typhoeus + @error_builder = ErrorBuilder.new + # future versions will deprecate this @response_handler = args[:response_handler] || ResponseHandler.new - @gzip = args[:gzip] == true + @gzip = args[:gzip] == true end + # returns a string response with parsed success json or error + # @deprecated def publish(messages) - response_handler.handle(push_notifications(messages)) + warn '[DEPRECATION] `publish` is deprecated. Please use `send_messages` instead.' + @response_handler.handle(push_notifications(messages)) end - private + # returns response handler that provides access to errors? and other response inspection methods + def send_messages(messages, **args) + # https://docs.expo.io/versions/latest/guides/push-notifications/#message-format + raise TooManyMessagesError, 'Only 100 message objects at a time allowed.' if messages.length > 100 - attr_reader :http_client, :response_handler + response = push_notifications(messages) + + # each call to send_messages will return a new instance of ResponseHandler + handler = args[:response_handler] || ResponseHandler.new + handler.process_response(response) + handler + end + + def verify_deliveries(receipt_ids, **args) + response = get_receipts(receipt_ids) + handler = args[:response_handler] || ResponseHandler.new + handler.process_response(response) + handler + end + + private def push_notifications(messages) - http_client.post( + @http_client.post( push_url, body: messages.to_json, - headers: headers + headers: headers, + accept_encoding: @gzip ) end @@ -36,69 +86,171 @@ def push_url 'https://exp.host/--/api/v2/push/send' end + def get_receipts(receipt_ids) + @http_client.post( + receipts_url, + body: { ids: receipt_ids }.to_json, + headers: headers, + accept_encoding: @gzip + ) + end + + def receipts_url + 'https://exp.host/--/api/v2/push/getReceipts' + end + def headers headers = { 'Content-Type' => 'application/json', - 'Accept' => 'application/json' + 'Accept' => 'application/json' } - headers['Accept-Encoding'] = 'gzip, deflate' if @gzip headers end end class ResponseHandler + attr_reader :response, :invalid_push_tokens, :receipt_ids, :errors + def initialize(error_builder = ErrorBuilder.new) - @error_builder = error_builder + @error_builder = error_builder + @response = nil + @receipt_ids = [] + @invalid_push_tokens = [] + @errors = [] + end + + def process_response(response) + @response = response + + case response.code.to_s + when /(^4|^5)/ + raise @error_builder.parse_response(response) + else + sort_results + end end + def errors? + @errors.any? + end + + # @deprecated def handle(response) + warn '[DEPRECATION] `handle` is deprecated. Please use `process_response` instead.' + @response = response case response.code.to_s when /(^4|^5)/ - raise build_error_from_failure(parse_json(response)) + raise build_error_from_failure else - handle_success(parse_json(response)) + extract_data end end private - attr_reader :error_builder + def sort_results + data = body&.fetch('data', nil) || nil + + # something is definitely wrong + return if data.nil? + + # Array indicates a response from the /send endpoint + # Hash indicates a response from the /getReceipts endpoint + if data.is_a? Array + data.each do |push_ticket| + receipt_id = push_ticket.fetch('id', nil) + if push_ticket.fetch('status', nil) == 'ok' + @receipt_ids.push(receipt_id) unless receipt_id.nil? + else + process_error(push_ticket) + end + end + else + process_receipts(data) + end + end + + def process_receipts(receipts) + receipts.each do |receipt_id, receipt| + @receipt_ids.push(receipt_id) unless receipt_id.nil? + process_error(receipt) unless receipt.fetch('status') == 'ok' + end + end + + def process_error(push_ticket) + message = push_ticket.fetch('message') + matches = message.match(/ExponentPushToken\[(...*)\]/) + error_class = @error_builder.parse_push_ticket(push_ticket) - def parse_json(response) - JSON.parse(response.body) + @invalid_push_tokens.push(matches[0]) unless matches.nil? + + @errors.push(error_class) unless @errors.include?(error_class) end - def build_error_from_failure(response) - error_builder.build_from_erroneous(response) + def body + # memoization FTW! + @body ||= JSON.parse(@response.body) + rescue SyntaxError + # Sometimes the server returns an empty string. + # It must be escaped before we can process it. + @body = JSON.parse(@response.body.to_json) + rescue StandardError + # Prevent nil errors in old version of ruby when using fetch + @body = {} end - def handle_success(response) - extract_data(response) + ##### DEPRECATED METHODS ##### + + # @deprecated + def build_error_from_failure + @error_builder.build_from_erroneous(body) end - def extract_data(response) - data = response.fetch('data') + # @deprecated + def extract_data + data = body.fetch('data') if data.is_a? Hash - validate_status(data.fetch('status'), response) + validate_status(data.fetch('status'), body) data - else + elsif data.is_a? Array data.map do |receipt| - validate_status(receipt.fetch('status'), response) + validate_status(receipt.fetch('status'), body) receipt end + else + {} end end + # @deprecated def validate_status(status, response) raise build_error_from_success(response) unless status == 'ok' end + # @deprecated def build_error_from_success(response) - error_builder.build_from_successful(response) + @error_builder.build_from_successful(response) end end class ErrorBuilder + def parse_response(response) + with_error_handling(response) do + error = response.fetch('errors') + error_name = error.fetch('code') + message = error.fetch('message') + + get_error_class(error_name).new(message) + end + end + + def parse_push_ticket(push_ticket) + with_error_handling(push_ticket) do + message = push_ticket.fetch('message') + get_error_class(push_ticket.fetch('details').fetch('error')).new(message) + end + end + %i[erroneous successful].each do |selector| define_method(:"build_from_#{selector}") do |response| with_error_handling(response) do @@ -111,25 +263,10 @@ class ErrorBuilder def with_error_handling(response) yield(response) - rescue KeyError + rescue KeyError, NoMethodError unknown_error_format(response) end - def from_erroneous_response(response) - error = response.fetch('errors').first - error_name = error.fetch('code') - message = error.fetch('message') - - get_error_class(error_name).new(message) - end - - def from_successful_response(response) - data = response.fetch('data').first - message = data.fetch('message') - - get_error_class(data.fetch('details').fetch('error')).new(message) - end - def validate_error_name(condition) condition ? yield : Exponent::Push::UnknownError end @@ -143,6 +280,24 @@ def get_error_class(error_name) def unknown_error_format(response) Exponent::Push::UnknownError.new("Unknown error format: #{response}") end + + ##### DEPRECATED METHODS ##### + + # @deprecated + def from_erroneous_response(response) + error = response.fetch('errors').first + error_name = error.fetch('code') + message = error.fetch('message') + + get_error_class(error_name).new(message) + end + + # @deprecated + def from_successful_response(response) + delivery_result = response.fetch('data').first + message = delivery_result.fetch('message') + get_error_class(delivery_result.fetch('details').fetch('error')).new(message) + end end Error = Class.new(StandardError) diff --git a/lib/exponent-server-sdk/too_many_messages_error.rb b/lib/exponent-server-sdk/too_many_messages_error.rb new file mode 100644 index 0000000..e21193d --- /dev/null +++ b/lib/exponent-server-sdk/too_many_messages_error.rb @@ -0,0 +1,2 @@ +class TooManyMessagesError < StandardError +end diff --git a/lib/exponent-server-sdk/version.rb b/lib/exponent-server-sdk/version.rb index 5479bba..6329158 100644 --- a/lib/exponent-server-sdk/version.rb +++ b/lib/exponent-server-sdk/version.rb @@ -1,3 +1,3 @@ module Exponent - VERSION = '0.0.7'.freeze + VERSION = '0.1.0'.freeze end diff --git a/manual_test.rb b/manual_test.rb deleted file mode 100644 index d9edc81..0000000 --- a/manual_test.rb +++ /dev/null @@ -1,19 +0,0 @@ -require_relative 'lib/exponent-server-sdk' - -client = Exponent::Push::Client.new - -# OR use GZIP to be AWESOME -# client = Exponent::Push::Client.new(gzip: true) - -messages = [{ - to: 'ExponentPushToken[HbuLbNFwb5_ENuljvAePLs]', - sound: "default", - title: "You're Winning!", - body: "You just won EVERTHING!", - data: { - type: 'WINNINGS', - message: "You just won EVERTHING!" - - }.to_json - }] -client.publish messages \ No newline at end of file diff --git a/test/exponent-server-sdk-test.rb b/test/exponent-server-sdk-test.rb index 26d0f2e..c7dd17d 100644 --- a/test/exponent-server-sdk-test.rb +++ b/test/exponent-server-sdk-test.rb @@ -1,21 +1,352 @@ +# frozen_string_literal: true + require 'minitest/autorun' require 'exponent-server-sdk' +require 'exponent-server-sdk/too_many_messages_error' class ExponentServerSdkTest < Minitest::Test def setup - @mock = MiniTest::Mock.new + @mock = MiniTest::Mock.new @response_mock = MiniTest::Mock.new - @exponent = Exponent::Push::Client.new(http_client: @mock) - @exponent_gzip = Exponent::Push::Client.new(http_client: @mock, gzip: true) + @client = Exponent::Push::Client.new(http_client: @mock) + @client_gzip = Exponent::Push::Client.new(http_client: @mock, gzip: true) + end + + def test_send_messages_with_success + @response_mock.expect(:code, 200) + @response_mock.expect(:body, success_body.to_json) + + @mock.expect(:post, @response_mock, client_args) + + response = @client.send_messages(messages) + assert_equal(response.errors?, false) + + @mock.verify + end + + def test_send_messages_alternate_message_format_with_success + @response_mock.expect(:code, 200) + @response_mock.expect(:body, success_body.to_json) + + alternate_messages = alternate_format_messages + @mock.expect(:post, @response_mock, alternative_client_args(alternate_messages)) + + response = @client.send_messages(alternate_messages) + assert_equal(response.errors?, false) + + @mock.verify end + def test_send_messages_with_gzip_success + @response_mock.expect(:code, 200) + @response_mock.expect(:body, success_body.to_json) + + @mock.expect(:post, @response_mock, gzip_client_args) + + response = @client_gzip.send_messages(messages) + assert_equal(response.errors?, false) + + @mock.verify + end + + def test_send_messages_with_empty_string_response_body + @response_mock.expect(:code, 400) + @response_mock.expect(:body, '') + + @mock.expect(:post, @response_mock, client_args) + + exception = assert_raises Exponent::Push::UnknownError do + handler = @client.send_messages(messages) + # this first assertion is just stating that errors will be false when + # an exception is thrown on the request, not the content of the request + # 400/500 level errors are not delivery errors, they are functionality errors + assert_equal(handler.response.errors?, false) + assert_equal(handler.response.body, {}) + assert_equal(handler.response.code, 400) + end + + assert_match(/Unknown error format/, exception.message) + + @mock.verify + end + + def test_send_messages_with_nil_response_body + @response_mock.expect(:code, 400) + @response_mock.expect(:body, nil) + + @mock.expect(:post, @response_mock, client_args) + + exception = assert_raises Exponent::Push::UnknownError do + handler = @client.send_messages(messages) + # this first assertion is just stating that errors will be false when + # an exception is thrown on the request, not the content of the request + # 400/500 level errors are not delivery errors, they are functionality errors + assert_equal(handler.response.errors?, false) + assert_equal(handler.response.body, {}) + assert_equal(handler.response.code, 400) + end + + assert_match(/Unknown error format/, exception.message) + + @mock.verify + end + + def test_send_messages_with_gzip_empty_string_response + @response_mock.expect(:code, 400) + @response_mock.expect(:body, '') + + @mock.expect(:post, @response_mock, gzip_client_args) + + exception = assert_raises Exponent::Push::UnknownError do + handler = @client_gzip.send_messages(messages) + # this first assertion is just stating that errors will be false when + # an exception is thrown on the request, not the content of the request + # 400/500 level errors are not delivery errors, they are functionality errors + assert_equal(handler.response.errors?, false) + assert_equal(handler.response.body, {}) + assert_equal(handler.response.code, 400) + end + + assert_match(/Unknown error format/, exception.message) + + @mock.verify + end + + def test_send_messages_with_gzip_nil_response_body + @response_mock.expect(:code, 400) + @response_mock.expect(:body, nil) + + @mock.expect(:post, @response_mock, gzip_client_args) + + exception = assert_raises Exponent::Push::UnknownError do + handler = @client_gzip.send_messages(messages) + # this first assertion is just stating that errors will be false when + # an exception is thrown on the request, not the content of the request + # 400/500 level errors are not delivery errors, they are functionality errors + assert_equal(handler.response.errors?, false) + assert_equal(handler.response.body, {}) + assert_equal(handler.response.code, 400) + end + + assert_match(/Unknown error format/, exception.message) + + @mock.verify + end + + def test_send_messages_with_unknown_error + @response_mock.expect(:code, 400) + @response_mock.expect(:body, error_body.to_json) + + @mock.expect(:post, @response_mock, client_args) + + exception = assert_raises Exponent::Push::UnknownError do + @client.send_messages(messages) + end + + assert_match(/Unknown error format/, exception.message) + + @mock.verify + end + + def test_send_messages_with_gzip_unknown_error + @response_mock.expect(:code, 400) + @response_mock.expect(:body, error_body.to_json) + + @mock.expect(:post, @response_mock, gzip_client_args) + + exception = assert_raises Exponent::Push::UnknownError do + @client_gzip.send_messages(messages) + end + + assert_match(/Unknown error format/, exception.message) + + @mock.verify + end + + def test_send_messages_with_device_not_registered_error + @response_mock.expect(:code, 200) + @response_mock.expect(:body, not_registered_device_error_body.to_json) + token = 'ExponentPushToken[42]' + message = "\"#{token}\" is not a registered push notification recipient" + + @mock.expect(:post, @response_mock, client_args) + + response_handler = @client.send_messages(messages) + assert_equal(message, response_handler.errors.first.message) + assert(response_handler.errors.first.instance_of?(Exponent::Push::DeviceNotRegisteredError)) + assert(response_handler.invalid_push_tokens.include?(token)) + assert(response_handler.errors?) + + @mock.verify + end + + def test_send_messages_too_many_messages + message = 'Only 100 message objects at a time allowed.' + + e = assert_raises TooManyMessagesError do + @client.send_messages(too_many_messages) + end + + assert_equal(e.message, message) + end + + def test_send_messages_with_message_too_big_error + @response_mock.expect(:code, 200) + @response_mock.expect(:body, message_too_big_error_body.to_json) + message = 'Message too big' + + @mock.expect(:post, @response_mock, client_args) + + response_handler = @client.send_messages(messages) + assert(response_handler.errors.first.instance_of?(Exponent::Push::MessageTooBigError)) + assert_equal(message, response_handler.errors.first.message) + assert(response_handler.errors?) + + @mock.verify + end + + def test_send_messages_with_message_rate_exceeded_error + @response_mock.expect(:code, 200) + @response_mock.expect(:body, message_rate_exceeded_error_body.to_json) + message = 'Message rate exceeded' + + @mock.expect(:post, @response_mock, client_args) + + response_handler = @client.send_messages(messages) + assert(response_handler.errors.first.instance_of?(Exponent::Push::MessageRateExceededError)) + assert_equal(message, response_handler.errors.first.message) + + @mock.verify + end + + def test_send_messages_with_invalid_credentials_error + @response_mock.expect(:code, 200) + @response_mock.expect(:body, invalid_credentials_error_body.to_json) + message = 'Invalid credentials' + + @mock.expect(:post, @response_mock, client_args) + + response_handler = @client.send_messages(messages) + assert(response_handler.errors.first.instance_of?(Exponent::Push::InvalidCredentialsError)) + assert_equal(message, response_handler.errors.first.message) + + @mock.verify + end + + def test_send_messages_with_apn_error + @response_mock.expect(:code, 200) + @response_mock.expect(:body, apn_error_body.to_json) + + @mock.expect(:post, @response_mock, client_args) + + response_handler = @client.send_messages(messages) + assert(response_handler.errors.first.instance_of?(Exponent::Push::UnknownError)) + assert_match(/Unknown error format/, response_handler.errors.first.message) + + @mock.verify + end + + def test_get_receipts_with_success_receipt + @response_mock.expect(:code, 200) + @response_mock.expect(:body, receipt_success_body.to_json) + receipt_ids = [success_receipt] + + @mock.expect(:post, @response_mock, receipt_client_args(receipt_ids)) + + response_handler = @client.verify_deliveries(receipt_ids) + assert_match(success_receipt, response_handler.receipt_ids.first) + + @mock.verify + end + + def test_get_receipts_with_error_receipt + @response_mock.expect(:code, 200) + @response_mock.expect(:body, receipt_error_body.to_json) + receipt_ids = [error_receipt] + + @mock.expect(:post, @response_mock, receipt_client_args(receipt_ids)) + + response_handler = @client.verify_deliveries(receipt_ids) + assert_match(error_receipt, response_handler.receipt_ids.first) + assert_equal(true, response_handler.errors?) + assert_equal(1, response_handler.errors.count) + assert(response_handler.errors.first.instance_of?(Exponent::Push::DeviceNotRegisteredError)) + + @mock.verify + end + + def test_get_receipts_with_variable_success_receipts + @response_mock.expect(:code, 200) + @response_mock.expect(:body, multiple_receipts.to_json) + receipt_ids = [error_receipt, success_receipt] + + @mock.expect(:post, @response_mock, receipt_client_args(receipt_ids)) + + response_handler = @client.verify_deliveries(receipt_ids) + assert_match(error_receipt, response_handler.receipt_ids.first) + assert_match(success_receipt, response_handler.receipt_ids.last) + assert_equal(true, response_handler.errors?) + assert_equal(1, response_handler.errors.count) + assert(response_handler.errors.first.instance_of?(Exponent::Push::DeviceNotRegisteredError)) + + @mock.verify + end + + def test_get_receipts_with_gzip_success_receipt + @response_mock.expect(:code, 200) + @response_mock.expect(:body, receipt_success_body.to_json) + receipt_ids = [success_receipt] + + @mock.expect(:post, @response_mock, gzip_receipt_client_args(receipt_ids)) + + response_handler = @client_gzip.verify_deliveries(receipt_ids) + assert_match(success_receipt, response_handler.receipt_ids.first) + + @mock.verify + end + + def test_get_receipts_with_gzip_error_receipt + @response_mock.expect(:code, 200) + @response_mock.expect(:body, receipt_error_body.to_json) + receipt_ids = [error_receipt] + + @mock.expect(:post, @response_mock, gzip_receipt_client_args(receipt_ids)) + + response_handler = @client_gzip.verify_deliveries(receipt_ids) + assert_match(error_receipt, response_handler.receipt_ids.first) + assert_equal(true, response_handler.errors?) + assert_equal(1, response_handler.errors.count) + assert(response_handler.errors.first.instance_of?(Exponent::Push::DeviceNotRegisteredError)) + + @mock.verify + end + + def test_get_receipts_with_gzip_variable_success_receipts + @response_mock.expect(:code, 200) + @response_mock.expect(:body, multiple_receipts.to_json) + receipt_ids = [error_receipt, success_receipt] + + @mock.expect(:post, @response_mock, gzip_receipt_client_args(receipt_ids)) + + response_handler = @client_gzip.verify_deliveries(receipt_ids) + assert_match(error_receipt, response_handler.receipt_ids.first) + assert_match(success_receipt, response_handler.receipt_ids.last) + assert_equal(true, response_handler.errors?) + assert_equal(1, response_handler.errors.count) + assert(response_handler.errors.first.instance_of?(Exponent::Push::DeviceNotRegisteredError)) + + @mock.verify + end + + # DEPRECATED -- TESTS BELOW HERE RELATE TO CODE THAT WILL BE REMOVED + def test_publish_with_success @response_mock.expect(:code, 200) @response_mock.expect(:body, success_body.to_json) @mock.expect(:post, @response_mock, client_args) - @exponent.publish(messages) + @client.publish(messages) @mock.verify end @@ -26,7 +357,7 @@ def test_publish_with_gzip_success @mock.expect(:post, @response_mock, gzip_client_args) - @exponent_gzip.publish(messages) + @client_gzip.publish(messages) @mock.verify end @@ -37,7 +368,7 @@ def test_publish_with_gzip @mock.expect(:post, @response_mock, gzip_client_args) - @exponent_gzip.publish(messages) + @client_gzip.publish(messages) @mock.verify end @@ -50,7 +381,7 @@ def test_publish_with_unknown_error @mock.expect(:post, @response_mock, client_args) exception = assert_raises Exponent::Push::UnknownError do - @exponent.publish(messages) + @client.publish(messages) end assert_equal(message, exception.message) @@ -66,7 +397,7 @@ def test_publish_with_device_not_registered_error @mock.expect(:post, @response_mock, client_args) exception = assert_raises Exponent::Push::DeviceNotRegisteredError do - @exponent.publish(messages) + @client.publish(messages) end assert_equal(message, exception.message) @@ -82,7 +413,7 @@ def test_publish_with_message_too_big_error @mock.expect(:post, @response_mock, client_args) exception = assert_raises Exponent::Push::MessageTooBigError do - @exponent.publish(messages) + @client.publish(messages) end assert_equal(message, exception.message) @@ -98,7 +429,7 @@ def test_publish_with_message_rate_exceeded_error @mock.expect(:post, @response_mock, client_args) exception = assert_raises Exponent::Push::MessageRateExceededError do - @exponent.publish(messages) + @client.publish(messages) end assert_equal(message, exception.message) @@ -114,7 +445,7 @@ def test_publish_with_invalid_credentials_error @mock.expect(:post, @response_mock, client_args) exception = assert_raises Exponent::Push::InvalidCredentialsError do - @exponent.publish(messages) + @client.publish(messages) end assert_equal(message, exception.message) @@ -129,7 +460,7 @@ def test_publish_with_apn_error @mock.expect(:post, @response_mock, client_args) exception = assert_raises Exponent::Push::UnknownError do - @exponent.publish(messages) + @client.publish(messages) end assert_match(/Unknown error format/, exception.message) @@ -143,6 +474,55 @@ def success_body { 'data' => [{ 'status' => 'ok' }] } end + def success_receipt + 'YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY' + end + + def error_receipt + 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' + end + + def receipt_success_body + { + 'data' => { + 'YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY' => { + 'status' => 'ok' + } + } + } + end + + def receipt_error_body + { + 'data' => { + 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' => { + 'status' => 'error', + 'message' => 'The Apple Push Notification service failed to send the notification', + 'details' => { + 'error' => 'DeviceNotRegistered' + } + } + } + } + end + + def multiple_receipts + { + 'data' => { + 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' => { + 'status' => 'error', + 'message' => 'The Apple Push Notification service failed to send the notification', + 'details' => { + 'error' => 'DeviceNotRegistered' + } + }, + 'YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY' => { + 'status' => 'ok' + } + } + } + end + def error_body { 'errors' => [{ @@ -176,7 +556,7 @@ def apn_error_body 'data' => [{ 'status' => 'error', 'message' => - 'Could not find APNs credentials for you (your_app). Check whether you are trying to send a notification to a detached app.' + 'Could not find APNs credentials for you (your_app). Check whether you are trying to send a notification to a detached app.' }] } end @@ -189,7 +569,22 @@ def client_args headers: { 'Content-Type' => 'application/json', 'Accept' => 'application/json' - } + }, + accept_encoding: false + } + ] + end + + def alternative_client_args(messages) + [ + 'https://exp.host/--/api/v2/push/send', + { + body: messages.to_json, + headers: { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' + }, + accept_encoding: false } ] end @@ -201,13 +596,53 @@ def gzip_client_args body: messages.to_json, headers: { 'Content-Type' => 'application/json', - 'Accept' => 'application/json', - 'Accept-Encoding' => 'gzip, deflate' - } + 'Accept' => 'application/json' + }, + accept_encoding: true } ] end + def receipt_client_args(receipt_ids) + [ + 'https://exp.host/--/api/v2/push/getReceipts', + { + body: { ids: receipt_ids }.to_json, + headers: { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' + }, + accept_encoding: false + } + ] + end + + def gzip_receipt_client_args(receipt_ids) + [ + 'https://exp.host/--/api/v2/push/getReceipts', + { + body: { ids: receipt_ids }.to_json, + headers: { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' + }, + accept_encoding: true + } + ] + end + + def alternate_format_messages + [{ + to: [ + 'ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]', + 'ExponentPushToken[yyyyyyyyyyyyyyyyyyyyyy]' + ], + badge: 1, + sound: 'default', + body: 'You got a completely unique message from us! /s' + }] + end + def messages [{ to: 'ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]', @@ -220,6 +655,19 @@ def messages }] end + def too_many_messages + (0..101).map { create_message } + end + + def create_message + id = (0...22).map { ('a'..'z').to_a[rand(26)] }.join + { + to: "ExponentPushToken[#{id}]", + sound: 'default', + body: 'Hello world!' + } + end + def build_error_body(error_code, message) { 'data' => [{