├── .github ├── ISSUE_TEMPLATE │ ├── new-issue.md │ └── update-dependencies.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── main.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE.txt ├── Makefile ├── README.md ├── RELEASING.md ├── Rakefile ├── bin ├── console └── setup ├── config ├── base.yml ├── graphql.yml ├── rails-locales.yml ├── rails.yml ├── rspec.yml └── ruby.yml ├── datarockets-style.gemspec ├── doc ├── STYLE_GUIDE.md ├── STYLE_GUIDE_GRAPHQL.md ├── STYLE_GUIDE_RAILS.md └── STYLE_GUIDE_RSPEC.md ├── lib ├── datarockets_style.rb └── datarockets_style │ ├── cop │ ├── layout │ │ └── array_alignment_extended.rb │ ├── rspec │ │ └── prefer_before_over_setup.rb │ └── style │ │ └── nested_interpolation.rb │ ├── formatter │ ├── todo_list_formatter.rb │ └── todo_list_formatter │ │ └── report_summary.rb │ └── version.rb ├── manual └── cops_layout.md └── spec ├── datarockets_style ├── cop │ ├── layout │ │ └── array_alignment_extended_spec.rb │ ├── rspec │ │ └── prefer_before_over_setup_spec.rb │ └── style │ │ └── nested_interpolation_spec.rb ├── formatter │ ├── todo_list_formatter │ │ └── report_summary_spec.rb │ └── todo_list_formatter_spec.rb └── version_spec.rb ├── rubocop_examples ├── cop │ └── layout │ │ └── array_alignment_extended_spec.rb └── hash_styles.rb └── spec_helper.rb /.github/ISSUE_TEMPLATE/new-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New issue or feature request 3 | about: Suggest a new feature or enhancement, ask a question, report bug, etc. 4 | title: "Title goes here" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. 12 | 13 | **Additional context.** 14 | Add any other context or screenshots about the feature request here. 15 | 16 | **Add label.** 17 | Add label for easier navigation by issues and understanding your expectations. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/update-dependencies.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Update Dependencies 3 | about: Updating dependencies issue 4 | title: "Update *dependency* to *version*" 5 | labels: dependencies 6 | assignees: '' 7 | 8 | --- 9 | 10 | Link to the new release: *link* 11 | 12 | To-Do list: 13 | 14 | - [ ] update dependencies to the latest version 15 | - [ ] investigate how it changes and affect our current styles and cops 16 | - [ ] check the required ruby version 17 | - [ ] enable pending cops 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Before you submit a pull request, please make sure you have to follow: 2 | 3 | - [ ] read and know items from the [Contributing Guide](https://github.com/datarockets/datarockets-style/blob/master/CONTRIBUTING.md#pull-requests) 4 | - [ ] add a description of the problem you're trying to solve (short summary from related issue) 5 | - [ ] verified that cops are ordered by alphabet 6 | - [ ] add a note to the style guide docs (if it needs) 7 | - [ ] add a note to the changelog file 8 | - [ ] the commit message contains the number of the related issue (if it presents) 9 | and word `Fix` if this PR closes related issue 10 | - [ ] squash all commits before submitting to review 11 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - 'master' 7 | push: 8 | branches: 9 | - 'master' 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | 18 | - name: Set up Ruby 3.0 19 | uses: ruby/setup-ruby@v1 20 | with: 21 | ruby-version: '3.0' 22 | bundler-cache: true 23 | 24 | - name: Run linter 25 | run: make lint 26 | 27 | - name: Run tests 28 | run: make test 29 | 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /bundler/ 3 | /.yardoc 4 | /_yardoc/ 5 | /pkg/ 6 | /tmp/ 7 | 8 | .byebug_history 9 | .idea 10 | Gemfile.lock 11 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: 2 | - config/ruby.yml 3 | - config/rspec.yml 4 | 5 | inherit_mode: 6 | merge: 7 | - Exclude 8 | 9 | AllCops: 10 | TargetRubyVersion: 3.0 11 | 12 | # for checking cops with interpolation 13 | Lint/InterpolationCheck: 14 | Exclude: 15 | - 'spec/**/*.rb' 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | The format is described in [Contributing notes](CONTRIBUTING.md#changelog-entry-format). 4 | 5 | ## master 6 | 7 | ## 1.6.0 (2024-08-27) 8 | 9 | ### Changed 10 | 11 | * **(Breaking)** Support Ruby >= 3.0.0 12 | 13 | * Update rubocop to `1.65.1`. ([@Set27]) 14 | * Update rubocop-graphql to `1.5.4`. ([@Set27]) 15 | * Update rubocop-rails to `2.26`. ([@Set27]) 16 | * Update rubocop-spec to `3.0.4`. ([@Set27]) 17 | 18 | ## 1.5.0 (2023-09-11) 19 | 20 | ### Added 21 | 22 | * Added GraphQL cops. ([@Anastastasia-B][]) 23 | 24 | ### Changed 25 | 26 | * **(Breaking)** Support Ruby >= 2.7.0 27 | 28 | * Update rubocop to `1.56.3`. ([@Set27]) 29 | * Update rubocop-rails to `2.21.0`. ([@Set27]) 30 | * Update rubocop-spec to `2.24.0`. ([@Set27]) 31 | 32 | ## 1.4.0 (2023-02-11) 33 | 34 | ### Changed 35 | 36 | * Update rubocop to `1.45.1`. ([@r.dubrovsky][]) 37 | * Update rubocop-rails to `2.17.4`. ([@r.dubrovsky][]) 38 | * Update rubocop-rspec to `2.18.1`. ([@r.dubrovsky][]) 39 | 40 | ## 1.3.0 (2022-12-02) 41 | 42 | ### Added 43 | 44 | * [#261](https://github.com/datarockets/ruby-style/issues/261) Add new config `rails-locales`. ([@r.dubrovsky][]) 45 | 46 | ### Changed 47 | 48 | * **(Breaking)** Drop support for Ruby 2.4 and Ruby 2.5. ([@r.dubrovsky][]) 49 | 50 | * [#260](https://github.com/datarockets/ruby-style/issues/260) Update rubocop to `1.39`. ([@r.dubrovsky][]) 51 | * Setup `Layout/CaseIndentation` rule. ([@r.dubrovsky][]) 52 | * [#261](https://github.com/datarockets/ruby-style/issues/261) Update rubocop-rails to `2.17.3`. ([@r.dubrovsky][]) 53 | * [#262](https://github.com/datarockets/ruby-style/issues/262) Update rubocop-rspec to `2.15`. ([@r.dubrovsky][]) 54 | * [#32](https://github.com/datarockets/ruby-style/issues/32) Enable back `Style/FrozenStringLiteralComment` cop. ([@r.dubrovsky][]) 55 | * [#176](https://github.com/datarockets/ruby-style/issues/176) Fix hash alignment via changing `Layout/HashAlignment` cop. ([@r.dubrovsky][]) 56 | * [#263](https://github.com/datarockets/ruby-style/issues/263) Setup `EnforcedStyle` for `Layout/LineEndStringConcatenationIndentation` cop. ([@r.dubrovsky][]) 57 | * [#258](https://github.com/datarockets/ruby-style/issues/258) Setup `EnforcedStyle` for `Layout/FirstArrayElementIndentation` cop. ([@r.dubrovsky][]) 58 | * [#179](https://github.com/datarockets/ruby-style/issues/179) Setup `EnforcedStyleAlignWith` rule for `Layout/BlockAlignment` cop. ([@r.dubrovsky][]) 59 | 60 | ### Fixed 61 | 62 | * [#196](https://github.com/datarockets/ruby-style/issues/196) Fix `Layout/ArrayAlignmentExtended` cop. ([@r.dubrovsky][]) 63 | * [#258](https://github.com/datarockets/ruby-style/issues/258) Fix conflict between `Layout/ArrayAlignmentExtended` and `Layout/FirstArrayElementIndentation` cops. ([@r.dubrovsky][]) 64 | 65 | ## 1.2.0 (2021-02-24) 66 | 67 | ### Added 68 | 69 | * [#219](https://github.com/datarockets/datarockets-style/issues/219) Add `RSpec/PreferBeforeOverSetup` cop. ([@paydaylight][]) 70 | 71 | ### Changed 72 | 73 | * [#233](https://github.com/datarockets/datarockets-style/issues/233) Setup `EnforcedStyleForMultiline` for `Style/TrailingCommaInArguments` and `Style/TrailingCommaInArrayLiteral` rules. ([@paydaylight][]) 74 | * [#124](https://github.com/datarockets/datarockets-style/issues/124) Move gem dependencies to `./datarockets-style.gemspec` and drop `Gemfile.lock` tracking. ([@paydaylight][]) 75 | * [#253](https://github.com/datarockets/datarockets-style/issues/253) Update rubocop to `1.10`. ([@paydaylight][]) 76 | * [#124](https://github.com/datarockets/datarockets-style/issues/124) Update rubocop-rails to `2.9`. ([@paydaylight][]) 77 | 78 | ### Fixed 79 | 80 | * [#251](https://github.com/datarockets/datarockets-style/issues/251) Update documentation to match `1.1.0` release changes. ([@paydaylight][]) 81 | 82 | ## 1.1.0 (2021-02-09) 83 | 84 | ### Changed 85 | 86 | * Setup `EnforcedStyleForMultiline` for `Style/TrailingCommaInHashLiteral` rule. ([@r.dubrovsky][]) 87 | * Update rubocop to `1.9.1`. ([@paydaylight][]) 88 | * Update rubocop-rails requirement to `>= 2.8.0, < 2.10.0`. ([@paydaylight][]) 89 | * Update rubocop-rspec to `2.2.0`. ([@paydaylight][]) 90 | * Change `Datarockets::Style` module to `DatarocketsStyle`. ([@paydaylight][]) 91 | 92 | ### Fixed 93 | 94 | * [#177](https://github.com/datarockets/datarockets-style/issues/177) set `Layout/MultilineOperationIndentation` to indented ([@paydaylight][]) 95 | 96 | ## 1.0.0 (2020-11-10) 97 | 98 | ### Changed 99 | 100 | * Update rubocop to `1.2.0`. 101 | * Update rubocop-rails to `2.8.1`. 102 | * Update rubocop-rspec tp `2.0.0`. 103 | 104 | ## 0.11.0 (2020-11-07) 105 | 106 | ### Changed 107 | 108 | * Update rubocop to '0.93.1'. ([@r.dubrovsky][]) 109 | * Enable new cops `Lint/BinaryOperatorWithIdenticalOperands`, `Lint/DuplicateRescueException`, `Lint/EmptyConditionalBody`, `Lint/FloatComparison`, `Lint/MissingSuper 110 | `, `Lint/OutOfRangeRegexpRef`, `Lint/SelfAssignment`, `Lint/TopLevelReturnWithArgument`, `Lint/UnreachableLoop`, `Style/ExplicitBlockArgument`, `Style/GlobalStdStream`, `Style/OptionalBooleanParameter`, `Style/SingleArgumentDig`, `Style/SoleNestedConditional` and `Style/StringConcatenation` in version `0.89`. 111 | * Allow to add all new lints automatically. 112 | * Add auto-correctable count notification. 113 | * Fix tests 114 | * Update rubocop-rails to `2.7.1`. 115 | * Update rubocop-rspec tp `1.43.2`. 116 | * Disable `RSpec/MultipleMemoizedHelpers` cop. 117 | 118 | ## 0.10.0 (2020-07-17) 119 | 120 | ### Changed 121 | 122 | * Update rubocop to `0.88.0`. ([@r.dubrovsky][]) 123 | * Enable new cops `Lint/MixedRegexpCaptureTypes`, `Style/RedundantRegexpCharacterClass` and `Style/RedundantRegexpEscape`. Cops were added in version `0.85`. 124 | * Enable new cop `Style/RedundantFetchBlock`. Cop was added in version `0.86`. 125 | * Enable new cops `Style/AccessorGrouping`, `Style/BisectedAttrAccessor` and `Style/RedundantAssignment`. Cops were added in version `0.87`. 126 | * Enable new cops `Lint/DuplicateElsifCondition`, `Style/ArrayCoercion`, `Style/CaseLikeIf`, `Style/HashAsLastArrayItem`, `Style/HashLikeCase` and `Style/RedundantFileExtensionInRequire`. Cops were added in version `0.88`. 127 | * Update rubocop-rails to `2.6.0`. 128 | * Update rubocop-rspec to `1.42.0`. 129 | 130 | * Setup `no_braces` rule for `Style/HashAsLastArrayItem` cop which added in the rubocop version `0.88`. 131 | 132 | ## 0.9.0 (2020-05-27) 133 | 134 | ### Changed 135 | 136 | * **(Breaking)** Drop support for Ruby 2.3. ([@r.dubrovsky][]) 137 | 138 | * Update rubocop to `0.84.0`. ([@r.dubrovsky][]) 139 | * Enable new cops `Lint/RaiseException` and `Lint/StructNewOverride`. Cops were added in version `0.81`. 140 | * Enable new cops `Layout/SpaceAroundMethodCallOperator` and `Style/ExponentialNotation`. Cops were added in version `0.82`. 141 | * Enable new cops `Layout/EmptyLinesAroundAttributeAccessor` and `Style/SlicingWithRange`. Cops were added in version `0.83`. 142 | * Enable new cop `Lint/DeprecatedOpenSSLConstant`. Cop was added in version `0.84`. 143 | 144 | * Update rubocop-rails to `2.5.2`. 145 | * Update rubocop-rspec to `1.39`. 146 | * Update `activesupport` for fixing security issues. 147 | 148 | ## 0.8.1 (2020-03-02) 149 | 150 | ### Changed 151 | 152 | * Update rubocop to `0.80.1`. 153 | 154 | ## 0.8.0 (2020-02-20) 155 | 156 | ### Added 157 | 158 | * Add `Style/NestedInterpolation` cop. ([@r.dubrovsky][]) 159 | 160 | ### Changed 161 | 162 | * Update rubocop to `0.80.0`. ([@r.dubrovsky][]) 163 | * Add `Style/HashEachMethods`, `Style/HashTransformKey`, `Style/HashTransformValues` cops. 164 | * [#7641](https://github.com/rubocop-hq/rubocop/issues/7641): Remove `Style/BracesAroundHashParameters` cop. 165 | 166 | * Update rubocop-rspec to `1.38.1`. 167 | * Add RSpec/RepeatedExampleGroupBody cop. ([@ula][]) 168 | * Add RSpec/RepeatedExampleGroupDescription cop. ([@ula][]) 169 | 170 | * Enable `rubocop-rails` cops for rails config. ([@ula][]) 171 | * Setup `Style/Documentation` for rails config. ([@r.dubrovsky][]) 172 | * Setup `Style/ClassAndModuleChildren` cop. ([@r.dubrovsky][]) 173 | * Enable `RSpec/LetSetup` cop. ([@r.dubrovsky][]) 174 | 175 | ## 0.7.0 (2020-01-27) 176 | 177 | ### Added 178 | 179 | * [#130](https://github.com/datarockets/datarockets-style/issues/130): Add Layout/ArrayAlignmentExtended cop ([@nikitasakov][]) 180 | 181 | ### Changed 182 | 183 | * Update rubocop to `0.79.0`. 184 | * Update rubocop-rspec to `1.37.1`. 185 | * Add notes for setting up Rspec configuration for fixing Rspec internal style issues. ([@r.dubrovsky][]) 186 | * [#58](https://github.com/datarockets/datarockets-style/issues/58): Disable `RSpec/LetSetup` cop by default. ([@ula][]) 187 | 188 | ### Fixed 189 | 190 | * [#80](https://github.com/datarockets/datarockets-style/issues/80): Allows adding additional files and directories to excluding block for rubocop. ([@nikitasakov][]) 191 | * Fix documentation issues. ([@ula][]) 192 | 193 | 194 | ## 0.6.2 (2019-12-05) 195 | 196 | ### Changed 197 | 198 | * Update rubocop to `0.77.0`. 199 | 200 | ## Fixed 201 | 202 | * [#137](https://github.com/datarockets/datarockets-style/issues/137): Usage of rubocop 0.77.0 version causes errors. ([@d.kachur][]) 203 | 204 | ## 0.6.1 (2019-11-06) 205 | 206 | ### Fixed 207 | 208 | * [#126](https://github.com/datarockets/datarockets-style/issues/126): Result for ToDo list formatter. ([@r.dubrovsky][]) 209 | 210 | ## 0.6.0 (2019-11-06) 211 | 212 | ### Added 213 | 214 | * [#107](https://github.com/datarockets/datarockets-style/issues/107): Add ToDo list formatter. ([@r.dubrovsky][]) 215 | 216 | ### Changed 217 | 218 | * Update rubocop to `0.76`. ([@r.dubrovsky][]) 219 | * Change default value for `RSpec/ExampleLength` cop. ([@a.branzeanu][]) 220 | * Disable `RSpec/NestedGroups` cop by default. ([@r.dubrovsky][]) 221 | 222 | ## 0.5.0 (2019-10-26) 223 | 224 | ### Changed 225 | 226 | * Split single rubocop config to smaller (for ruby, rails and rspec). ([@r.dubrovsky][]) 227 | * Update rubocop to `0.75.1`. 228 | * Update rubocop-rspec to `1.36.0`. 229 | * Use block style for `RSpec/ExpectChange` cop. ([@r.dubrovsky][]) 230 | * Add `RSpec/MessageSpies` cop to style guide. ([@r.dubrovsky][]) 231 | 232 | ### Fixed 233 | 234 | * Fix `RSpec/DescribedClass`'s error when `described_class` is used as part of a constant. This is part of rubocop-rspec changes. 235 | 236 | ## 0.4.0 (2019-08-13) 237 | 238 | ### Changed 239 | 240 | * Update rubocop-rspec to `1.35.0`. ([@r.dubrovsky][]) 241 | * Use context-dependent style for curly braces around hash params. ([@v.kuzmik][]) 242 | * Use leading underscores in cached instance variable name (cop: `Naming/MemoizedInstanceVariableName`). ([@ula][]) 243 | * Allow use `for` with `RSpec/ContextWording` cop. ([@r.dubrovsky][]) 244 | * Change `Layout/AlignArguments` and `Layout/IndentFirstHashElement` cops for aligning arguments with fixed indentation. ([@r.dubrovsky][]) 245 | * Enable `Layout/MultilineMethodCallIndentation` cop for aligning arguments with fixed indentation. ([@r.dubrovsky][], [@ula][]) 246 | 247 | ## 0.3.0 (2019-08-02) 248 | 249 | ### Changed 250 | 251 | * Update rubocop to `0.74.0`. ([@r.dubrovsky][]) 252 | * Update rubocop-rspec to `1.34.1`. ([@r.dubrovsky][]) 253 | * Move shared rubocop config into `config` directory. ([@r.dubrovsky][]) 254 | * Allow writing empty methods in two lines. ([@v.kuzmik][]) 255 | * Disable `Style/FrozenStringLiteralComment` cop by default. ([@r.dubrovsky][]) 256 | 257 | ### Fixed 258 | 259 | * [#4222](https://github.com/rubocop-hq/rubocop/issues/4222): Disable `Lint/AmbiguousBlockAssociation` for Rspec directory. ([@r.dubrovsky][]) 260 | * [#65](https://github.com/datarockets/datarockets-style/issues/65): Exclude `node_modules` from rubocop scope. ([@r.dubrovsky][]) 261 | 262 | ## 0.2.0 (2019-07-17) 263 | 264 | ### Changed 265 | 266 | * Update rubocop to `0.73.0`. ([@r.dubrovsky][]) 267 | * Use preferred variable name in rescued exceptions (cop: `Naming/RescuedExceptionsVariableName`). ([@ula][]) 268 | * Disable `RSpec/ImplicitSubject` cop for rspec files. ([@r.dubrovsky][]) 269 | 270 | ## 0.1.0 (2019-06-27) 271 | 272 | ### Added 273 | 274 | * Base config with community rules and some basic override rules. ([@r.dubrovsky][], [@aleks][]) 275 | * Config is based on rubocop version 0.72.0. ([@r.dubrovsky][]) 276 | * Enable `rubocop-spec` cops by default. ([@r.dubrovsky][]) 277 | * Enable `Bundler/DuplicatedGem` cop by default. ([@r.dubrovsky][]) 278 | * Enable `Bundler/OrderedGems` cop with allowing ordering by groups. ([@r.dubrovsky][]) 279 | 280 | 281 | ### Changed 282 | 283 | * Change the limit for size of line to 120 symbols. ([@r.dubrovsky][]) 284 | * Disable `Metrics/BlockLength` cop for rspec files. ([@r.dubrovsky][]) 285 | * Exclude rubocop checking for some config directories. ([@r.dubrovsky][]) 286 | * Enable preferring double quotes for string literals. ([@r.dubrovsky][]) 287 | * Do not add spaces between hash literal braces (cop `Layout/SpaceInsideHashLiteralBraces`). ([@r.dubrovsky][]) 288 | * Prefer normal style for `Layout/IndentationConsistency` cop for Rails apps too. ([@r.dubrovsky][]) 289 | * Change style to `variable` for `Layout/EndAlignment` cop. ([@r.dubrovsky][]) 290 | * Change style to `with_fixed_indentation` with indentation width 2 for `Layout/AlignParameter` cop. ([@r.dubrovsky][]) 291 | * Always ignore hash aligning for key word arguments. (cop: `Layout/AlignHash`) ([@r.dubrovsky][]) 292 | 293 | [@r.dubrovsky]: https://github.com/roman-dubrovsky 294 | [@aleks]: https://github.com/AleksSenkou 295 | [@ula]: https://github.com/lazycoder9 296 | [@v.kuzmik]: https://github.com/TheBlackArroVV/ 297 | [@a.branzeanu]: https://github.com/texpert 298 | [@nikitasakov]: https://github.com/nikitasakov 299 | [@paydaylight]: https://github.com/paydaylight 300 | [@Anastastasia-B]: https://github.com/Anastastasia-B 301 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at hello@datarockets.com All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | If you discover issues, want to add or change some code-style rules, 4 | have ideas for improvements or new features, 5 | please report them to the [issue tracker][1] of the repository or 6 | submit a pull request. Please, try to follow these guidelines when you 7 | do so. 8 | 9 | ## Issue reporting 10 | 11 | * Check that the issue has not already been reported. 12 | * Check that the issue has not already been fixed in the latest code 13 | (a.k.a. `master`). 14 | * Be clear, concise and precise in your description of the problem. 15 | * Open an issue with a descriptive title and a summary in grammatically correct, 16 | complete sentences. 17 | * Include any relevant code to the issue summary 18 | 19 | ## Pull requests 20 | 21 | * Read [how to properly contribute to open source projects on GitHub][2]. 22 | * Fork the project (if it's necessary and you don't have writing access). 23 | * Use a topic/feature branch to easily amend a pull request later, if necessary. 24 | * Write [good commit messages][3]. 25 | * Use the same coding conventions as the rest of the project. 26 | * Commit and push until you are happy with your contribution. 27 | * If your change has a corresponding open GitHub issue, prefix the commit message with `[Fix #github-issue-number]`. 28 | * Make sure to add tests for it. This is important so we don't break it 29 | in a future version unintentionally. 30 | * If you add new code-style rule or cop, add some words about it in the [Code Style Notes](doc/STYLE_GUIDE.md) or related files. 31 | * Add an entry to the [Changelog](CHANGELOG.md) accordingly. See [changelog entry format](#changelog-entry-format). 32 | * If you deliver new cop, try to suggest and deliver it to the community [rubocop][7] gem. 33 | * [Squash related commits together][5]. 34 | * Open a [pull request][4] that relates to *only* one subject with a clear title 35 | and description in grammatically correct, complete sentences. 36 | 37 | ### Changelog entry format 38 | 39 | Here are a few examples: 40 | 41 | ``` 42 | * [#716](https://github.com/rubocop-hq/rubocop/issues/716): Fixed a regression in the auto-correction logic of `MethodDefParentheses`. ([@bbatsov][]) 43 | * New cop `ElseLayout` checks for odd arrangement of code in the `else` branch of a conditional expression. ([@bbatsov][]) 44 | ``` 45 | 46 | * There are four categories for each release: added, changed, bug fixed and removed 47 | * Mark it up in [Markdown syntax][6]. 48 | * The entry line should start with `* ` (an asterisk and a space). 49 | * If the change has a related GitHub issue (e.g. a bug fix for a reported issue), put a link to the issue as `[#123](https://github.com/rubocop-hq/rubocop/issues/123): `. 50 | * Describe the brief of the change. The sentence should end with a punctuation. 51 | * At the end of the entry, add an implicit link to your GitHub user page as `([@username][])`. 52 | * If this is your first contribution to RuboCop project, add a link definition for the implicit link to the bottom of the changelog as `[@username]: https://github.com/username`. 53 | 54 | [1]: https://github.com/datarockets/datarockets-style/issues 55 | [2]: https://www.gun.io/blog/how-to-github-fork-branch-and-pull-request 56 | [3]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 57 | [4]: https://help.github.com/articles/about-pull-requests 58 | [5]: http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html 59 | [6]: https://daringfireball.net/projects/markdown/syntax 60 | [7]: https://github.com/rubocop-hq/rubocop 61 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } 6 | 7 | gemspec 8 | 9 | gem "pry-byebug" 10 | gem "rake", "~> 13.0" 11 | gem "rspec", "~> 3.12.0" 12 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 datarockets 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | bundle install 3 | 4 | test: 5 | bundle exec rspec spec 6 | 7 | lint: 8 | bundle exec rubocop 9 | 10 | .PHONY: test 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Datarockets::Style [![Gem Version](https://badge.fury.io/rb/datarockets-style.svg)](https://badge.fury.io/rb/datarockets-style) 2 | 3 | Datarockets shared style configs and notes of code-style conventions. Based on the [Rubocop](https://github.com/rubocop-hq/rubocop) util. 4 | 5 | This config enforces many of the guidelines outlined in the datarockets [Ruby Style Guide](doc/STYLE_GUIDE.md). 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | group :test, :development do 13 | gem 'datarockets-style', '~> 1.6.0' 14 | end 15 | ``` 16 | 17 | Or, for a Ruby library, add this to your gemspec: 18 | 19 | ```ruby 20 | spec.add_development_dependency 'datarockets-style', '~> 1.6.0' 21 | ``` 22 | 23 | And then execute: 24 | 25 | ```bash 26 | $ bundle install 27 | ``` 28 | 29 | ## Usage 30 | 31 | Create a `.rubocop.yml` with the following directives: 32 | 33 | 34 | ### Ruby application 35 | 36 | This config includes specific rules for Ruby application. It works for Ruby gems and no-Rails applications. 37 | 38 | ```yaml 39 | inherit_gem: 40 | datarockets-style: 41 | - config/ruby.yml 42 | ``` 43 | 44 | ### Rails application 45 | 46 | For Rails applications, you can use a specific Rails instead of Ruby config 47 | 48 | ```yaml 49 | inherit_gem: 50 | datarockets-style: 51 | - config/rails.yml 52 | ``` 53 | 54 | By default, it doesn't include rules for I18n. For enabling them, add the next styles 55 | 56 | ```yaml 57 | inherit_gem: 58 | datarockets-style: 59 | - config/rails.yml 60 | - config/rails-locales.yml 61 | ``` 62 | 63 | ### GraphQL config 64 | 65 | To include specific rules for GraphQL, you can add the following config 66 | 67 | ```yaml 68 | inherit_gem: 69 | datarockets-style: 70 | - config/graphql.yml 71 | ``` 72 | 73 | ### Rspec config 74 | 75 | For Rspec tests, you can add a special rubocop config 76 | 77 | ```yaml 78 | inherit_gem: 79 | datarockets-style: 80 | - config/ruby.yml 81 | - config/rspec.yml 82 | ``` 83 | 84 | Now, run: 85 | 86 | ```bash 87 | $ bundle exec rubocop 88 | ``` 89 | 90 | You do not need to include rubocop directly in your application's dependencies. Datarockets-style will include a specific version of `rubocop` and `rubocop-rspec` that is shared across all projects. 91 | 92 | ### Configurable cops 93 | 94 | There are some areas in which there is no clear consensus in datarockets team regarding a particular style (like string literal quoting). 95 | In such scenarios, all popular styles are acknowledged and it’s up to you to pick one and apply it consistently. 96 | For that just set up these cops before starting of usage. 97 | 98 | #### RSpec/LetSetup 99 | 100 | [This cop](https://rubocop-rspec.readthedocs.io/en/latest/cops_rspec/#rspecletsetup) is enabled by default and we suggest not to use unreferenced `let` variables in your test cases. 101 | However, if it feels like the cop should be enabled and tests can't be written w/o them, please create an issue with your cases so that we can re-thinking our solution about enabling this cop. 102 | 103 | #### Style/StringLiterals 104 | 105 | There are two popular styles in the Ruby community, both of which are considered good - single quotes by default and double quotes by default. 106 | There is no clear consensus about this style in the Ruby community and in the datarockets team. 107 | So we suggest just to set up a [preferable style](https://rubocop.readthedocs.io/en/latest/cops_style/#stylestringliterals) before running this gem. 108 | 109 | P.S. The string literals in this gem are using double quotes by default. 110 | 111 | ##### Tips 112 | 113 | For an existing project, we suggest running rubocop with both styles and choose which has fewer offenses (which is more popular in the current project). 114 | 115 | ### Custom cops 116 | 117 | We have custom cops. You can find specification for them [here](manual). 118 | 119 | ## Formatters 120 | 121 | ### ToDo list formatter 122 | 123 | This formatter allows us to get list of files for some offense and with number of offenses in each file. This file can be useful if you need to fix a large some cop step by step. 124 | 125 | Result of the formatter is compatible with rubocop config or rubocop todo file. 126 | 127 | For running that cop, just print in your command like 128 | 129 | ```bash 130 | $ bundle exec rubocop -f TodoListFormatter -r datarockets_style 131 | Inspecting 10 files 132 | ...CC.CC.. 133 | 10 files inspected, 7 offenses detected 134 | 135 | Layout/IndentationConsistency: 136 | Exclude: 137 | - 'spec/datarockets_style/formatter/todo_list_formatter_spec.rb' # 1 138 | 139 | Naming/MemoizedInstanceVariableName: 140 | Exclude: 141 | - 'lib/datarockets_style/formatter/todo_list_formatter/report_summary.rb' # 1 142 | 143 | RSpec/ExampleLength: 144 | Exclude: 145 | - 'spec/datarockets_style/formatter/todo_list_formatter/report_summary_spec.rb' # 1 146 | - 'spec/datarockets_style/formatter/todo_list_formatter_spec.rb' # 2 147 | 148 | Style/Documentation: 149 | Exclude: 150 | - 'lib/datarockets_style/formatter/todo_list_formatter/report_summary.rb' # 1 151 | - 'lib/datarockets_style/formatter/todo_list_formatter.rb' # 1 152 | ``` 153 | 154 | #### Autocorrection 155 | 156 | If you run the formatter with autocorrection options, the formatter skip corrected cop and does not include it to the result. 157 | 158 | ```bash 159 | $ bundle exec rubocop -f TodoListFormatter -r datarockets_style -a 160 | Inspecting 10 files 161 | ...CC.CC.. 162 | 10 files inspected, 7 offenses detected, 1 offenses corrected 163 | 164 | Naming/MemoizedInstanceVariableName: 165 | Exclude: 166 | - 'lib/datarockets_style/formatter/todo_list_formatter/report_summary.rb' # 1 167 | 168 | RSpec/ExampleLength: 169 | Exclude: 170 | - 'spec/datarockets_style/formatter/todo_list_formatter/report_summary_spec.rb' # 1 171 | - 'spec/datarockets_style/formatter/todo_list_formatter_spec.rb' # 2 172 | 173 | Style/Documentation: 174 | Exclude: 175 | - 'lib/datarockets_style/formatter/todo_list_formatter/report_summary.rb' # 1 176 | - 'lib/datarockets_style/formatter/todo_list_formatter.rb' # 1 177 | ``` 178 | 179 | ## Non-goals of RuboCop 180 | 181 | ### Rspec configuration 182 | 183 | RSpec-core library provides some configuration rules which provides some linting rules itself. Read more about it in [RuboCop Rspec non-goals topic](https://github.com/rubocop-hq/rubocop-rspec#non-goals-of-rubocop-rspec). 184 | 185 | ## Changelog 186 | 187 | Datarockets Style's changelog is available [here](CHANGELOG.md). 188 | 189 | ## Contributing 190 | 191 | Bug reports and pull requests are welcome on GitHub at https://github.com/datarockets/datarockets-style. If you'd like to contribute to our RuboCop config and code-style rules, please take the time to go through our short [contribution guidelines](CONTRIBUTING.md). 192 | 193 | 194 | ## License 195 | 196 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 197 | 198 | ## Code of Conduct 199 | 200 | Everyone interacting in the DatarocketsStyle project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md). 201 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Releasing 2 | 3 | * Update `version.rb` file accordingly. 4 | * Update `README.mb` file. 5 | * Update `CHANGELOG.md` file. 6 | * Build and publish (and verify that everything ok): 7 | 8 | ```bash 9 | bundle exec rake build 10 | gem push *built_file* 11 | ``` 12 | * Tag the release: `git tag vVERSION`. 13 | * Push changes: `git push --tags`. 14 | * Update the release notes on GitHub.com. 15 | * Announce the new release, 16 | making sure to say "thank you" to the contributors 17 | who helped shape this version! 18 | 19 | ## Versioning stragety 20 | 21 | As versioning strategy, we're using SemVer: `MAJOR.MINOR.PATCH` 22 | 23 | A `MAJOR` version must be incremented if there are any backward-incompatible breaking changes included in a release. This has the benefit of making it easy for anyone to quickly identify if a new version will work differently than a previous one. 24 | 25 | Usually, we'll increment it on updating major versions of rubocop dependencies. Ideally, it should be synchronized with the major verion of `rubocop`. 26 | 27 | The `MINOR` version must be incremented if backward-compatible functionality is introduced. In the strictest sense, this means you should be able to upgrade to a new minor version without experiencing any breaking changes. 28 | 29 | Usually, we'll increment it on changing the list of cops, updating minor versions of dependencies. 30 | 31 | The `PATCH` version is meant for backward-compatible bug fixes. You should expect no new functionality with a new patch version, only improvements. 32 | 33 | Usually, we'll use it for updating patch versions of dependencies or fixing bugs of the last version. Changed rules of cops in most cases should be added to minor version. 34 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | # require "rspec/core/rake_task" 3 | 4 | # RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "datarockets_style" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install --path=bundler 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /config/base.yml: -------------------------------------------------------------------------------- 1 | require: datarockets_style 2 | 3 | AllCops: 4 | NewCops: enable 5 | 6 | Bundler/DuplicatedGem: 7 | Enabled: true 8 | 9 | Bundler/OrderedGems: 10 | TreatCommentsAsGroupSeparators: true 11 | 12 | Layout/ArgumentAlignment: 13 | EnforcedStyle: with_fixed_indentation 14 | 15 | Layout/ArrayAlignment: 16 | Enabled: false 17 | 18 | Layout/ArrayAlignmentExtended: 19 | Description: >- 20 | Align the elements of an array literal if they span more than 21 | one line. 22 | EnforcedStyle: with_fixed_indentation 23 | SupportedStyles: 24 | - with_first_parameter 25 | - with_fixed_indentation 26 | IndentationWidth: ~ 27 | 28 | Layout/BlockAlignment: 29 | EnforcedStyleAlignWith: start_of_block 30 | 31 | Layout/CaseIndentation: 32 | EnforcedStyle: end 33 | 34 | Layout/FirstArrayElementIndentation: 35 | EnforcedStyle: consistent 36 | 37 | Layout/HashAlignment: 38 | EnforcedColonStyle: key 39 | EnforcedHashRocketStyle: key 40 | 41 | Layout/ParameterAlignment: 42 | EnforcedStyle: with_fixed_indentation 43 | IndentationWidth: 2 44 | 45 | Layout/EmptyLinesAroundAttributeAccessor: 46 | Enabled: true 47 | 48 | Layout/EndAlignment: 49 | EnforcedStyleAlignWith: variable 50 | 51 | Layout/IndentationConsistency: 52 | EnforcedStyle: normal 53 | 54 | Layout/LineEndStringConcatenationIndentation: 55 | EnforcedStyle: indented 56 | 57 | Layout/LineLength: 58 | Max: 120 59 | 60 | Layout/FirstHashElementIndentation: 61 | EnforcedStyle: consistent 62 | 63 | Layout/MultilineMethodCallIndentation: 64 | EnforcedStyle: indented 65 | 66 | Layout/MultilineOperationIndentation: 67 | EnforcedStyle: indented 68 | 69 | Layout/SpaceAroundMethodCallOperator: 70 | Enabled: true 71 | 72 | Layout/SpaceInsideHashLiteralBraces: 73 | EnforcedStyle: no_space 74 | EnforcedStyleForEmptyBraces: no_space 75 | 76 | Lint/BinaryOperatorWithIdenticalOperands: 77 | Enabled: true 78 | 79 | Lint/DeprecatedOpenSSLConstant: 80 | Enabled: true 81 | 82 | Lint/DuplicateElsifCondition: 83 | Enabled: true 84 | 85 | Lint/DuplicateRescueException: 86 | Enabled: true 87 | 88 | Lint/EmptyConditionalBody: 89 | Enabled: true 90 | 91 | Lint/FloatComparison: 92 | Enabled: true 93 | 94 | Lint/MissingSuper: 95 | Enabled: true 96 | 97 | Lint/MixedRegexpCaptureTypes: 98 | Enabled: true 99 | 100 | Lint/OutOfRangeRegexpRef: 101 | Enabled: true 102 | 103 | Lint/RaiseException: 104 | Enabled: true 105 | 106 | Lint/SelfAssignment: 107 | Enabled: true 108 | 109 | Lint/StructNewOverride: 110 | Enabled: true 111 | 112 | Lint/TopLevelReturnWithArgument: 113 | Enabled: true 114 | 115 | Lint/UnreachableLoop: 116 | Enabled: true 117 | 118 | Naming/MemoizedInstanceVariableName: 119 | EnforcedStyleForLeadingUnderscores: required 120 | 121 | Naming/RescuedExceptionsVariableName: 122 | PreferredName: error 123 | 124 | Style/AccessorGrouping: 125 | Enabled: true 126 | 127 | Style/ArrayCoercion: 128 | Enabled: true 129 | 130 | Style/BisectedAttrAccessor: 131 | Enabled: true 132 | 133 | Style/CaseLikeIf: 134 | Enabled: true 135 | 136 | Style/ClassAndModuleChildren: 137 | EnforcedStyle: nested 138 | 139 | Style/EmptyMethod: 140 | EnforcedStyle: expanded 141 | 142 | Style/ExplicitBlockArgument: 143 | Enabled: true 144 | 145 | Style/ExponentialNotation: 146 | Enabled: true 147 | 148 | Style/GlobalStdStream: 149 | Enabled: true 150 | 151 | Style/HashAsLastArrayItem: 152 | Enabled: true 153 | EnforcedStyle: no_braces 154 | 155 | Style/HashEachMethods: 156 | Enabled: true 157 | 158 | Style/HashLikeCase: 159 | Enabled: true 160 | 161 | Style/HashTransformKeys: 162 | Enabled: true 163 | 164 | Style/HashTransformValues: 165 | Enabled: true 166 | 167 | Style/FrozenStringLiteralComment: 168 | Enabled: true 169 | 170 | Style/NestedInterpolation: 171 | Enabled: true 172 | 173 | Style/OptionalBooleanParameter: 174 | Enabled: true 175 | 176 | Style/RedundantAssignment: 177 | Enabled: true 178 | 179 | Style/RedundantFetchBlock: 180 | Enabled: true 181 | 182 | Style/RedundantFileExtensionInRequire: 183 | Enabled: true 184 | 185 | Style/RedundantRegexpCharacterClass: 186 | Enabled: true 187 | 188 | Style/RedundantRegexpEscape: 189 | Enabled: true 190 | 191 | Style/SingleArgumentDig: 192 | Enabled: true 193 | 194 | Style/SlicingWithRange: 195 | Enabled: true 196 | 197 | Style/SoleNestedConditional: 198 | Enabled: true 199 | 200 | Style/StringConcatenation: 201 | Enabled: true 202 | 203 | Style/StringLiterals: 204 | EnforcedStyle: double_quotes 205 | 206 | Style/TrailingCommaInArguments: 207 | EnforcedStyleForMultiline: comma 208 | 209 | Style/TrailingCommaInArrayLiteral: 210 | EnforcedStyleForMultiline: comma 211 | 212 | Style/TrailingCommaInHashLiteral: 213 | EnforcedStyleForMultiline: comma 214 | -------------------------------------------------------------------------------- /config/graphql.yml: -------------------------------------------------------------------------------- 1 | require: rubocop-graphql 2 | 3 | GraphQL/ExtractInputType: 4 | Enabled: false 5 | 6 | GraphQL/ExtractType: 7 | Enabled: false 8 | 9 | GraphQL/ResolverMethodLength: 10 | Max: 10 11 | -------------------------------------------------------------------------------- /config/rails-locales.yml: -------------------------------------------------------------------------------- 1 | Rails/I18nLocaleTexts: 2 | Enabled: true 3 | -------------------------------------------------------------------------------- /config/rails.yml: -------------------------------------------------------------------------------- 1 | require: rubocop-rails 2 | 3 | inherit_from: base.yml 4 | 5 | AllCops: 6 | Exclude: 7 | - "db/**/*" 8 | - "bin/*" 9 | - "lib/tasks/**/*" 10 | - "config/**/*" 11 | - "node_modules/**/*" 12 | - "vendor/**/*" 13 | - "app/views/**/*" 14 | - "config.ru" 15 | - "Rakefile" 16 | 17 | Rails/I18nLocaleTexts: 18 | Enabled: false 19 | 20 | Style/Documentation: 21 | Enabled: true 22 | Exclude: 23 | - "app/**/*" 24 | -------------------------------------------------------------------------------- /config/rspec.yml: -------------------------------------------------------------------------------- 1 | require: rubocop-rspec 2 | 3 | Lint/AmbiguousBlockAssociation: 4 | Exclude: 5 | - "spec/**/*" 6 | 7 | Metrics/BlockLength: 8 | Exclude: 9 | - "spec/**/*" 10 | 11 | RSpec/ContextWording: 12 | Prefixes: 13 | - when 14 | - with 15 | - without 16 | - for 17 | 18 | RSpec/ExampleLength: 19 | Enabled: true 20 | Max: 10 21 | 22 | RSpec/ExpectChange: 23 | EnforcedStyle: block 24 | 25 | RSpec/ImplicitSubject: 26 | Enabled: false 27 | 28 | RSpec/LetSetup: 29 | Enabled: true 30 | 31 | RSpec/MessageSpies: 32 | EnforcedStyle: have_received 33 | 34 | RSpec/MultipleMemoizedHelpers: 35 | Enabled: false 36 | 37 | RSpec/NestedGroups: 38 | Enabled: false 39 | 40 | RSpec/PreferBeforeOverSetup: 41 | Enabled: true 42 | -------------------------------------------------------------------------------- /config/ruby.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - "bundler/**/*" 4 | - "bin/*" 5 | - "lib/tasks/**/*" 6 | - "vendor/**/*" 7 | - "config.ru" 8 | - "Rakefile" 9 | 10 | inherit_from: base.yml 11 | -------------------------------------------------------------------------------- /datarockets-style.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path("lib", __dir__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | require "datarockets_style/version" 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = "datarockets-style" 9 | spec.version = DatarocketsStyle::VERSION 10 | spec.authors = ["Roman Dubrovsky"] 11 | spec.email = ["r.dubrovsky@datarockets.com"] 12 | 13 | spec.summary = "Datarockets style guides and shared style configs" 14 | spec.homepage = "https://github.com/datarockets/datarockets-style" 15 | spec.license = "MIT" 16 | 17 | spec.required_ruby_version = ">= 3.0.0" 18 | 19 | # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' 20 | # to allow pushing to a single host or delete this section to allow pushing to any host. 21 | if spec.respond_to?(:metadata) 22 | spec.metadata["allowed_push_host"] = "https://rubygems.org" 23 | else 24 | raise "RubyGems 2.0 or newer is required to protect against " \ 25 | "public gem pushes." 26 | end 27 | 28 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 29 | f.match(%r{^(test|spec|features)/}) 30 | end 31 | spec.bindir = "exe" 32 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 33 | spec.require_paths = ["lib"] 34 | 35 | spec.add_dependency "rubocop", "~> 1.65.1" 36 | spec.add_dependency "rubocop-graphql", "~> 1.5.4" 37 | spec.add_dependency "rubocop-rails", "~> 2.26" 38 | spec.add_dependency "rubocop-rspec", "~> 3.0.4" 39 | 40 | spec.metadata["rubygems_mfa_required"] = "true" 41 | end 42 | -------------------------------------------------------------------------------- /doc/STYLE_GUIDE.md: -------------------------------------------------------------------------------- 1 | # Style Guide 2 | 3 | In datarockets we enforce a community [Ruby Style Guide](https://github.com/rubocop-hq/ruby-style-guide). 4 | 5 | Also, we have some new rules for writing Rails applications and Rspec test. You can find it by next links: 6 | 7 | - [Rails Style Guide](doc/STYLE_GUIDE_RAILS.md) 8 | - [Rspec Style Guide](doc/STYLE_GUIDE_RSPEC.md) 9 | 10 | This is a small list of differences which we have when compared with community style guide: 11 | 12 | ## Table of contents 13 | 14 | * [Bundler](#Bundler) 15 | * [Style](#Style) 16 | * [Rspec](#Rspec) 17 | 18 | ## Bundler 19 | 20 | * 21 | A Gem's requirements should be listed only once in a Gemfile. 22 | [[link](#bundler-add-once)] 23 | 24 | * 25 | Gems should be alphabetically sorted within groups. Also, you can use a line comment as a group separator. 26 | [[link](#bundler-ordering)] 27 | 28 | ## Style 29 | 30 | * 31 | Limit lines to 120 characters. 32 | [[link](#style-line-length)] 33 | 34 | * 35 | Adopt a consistent string literal quoting style. 36 | [[link](#style-string-quotes)] 37 | 38 | * 39 | Avoid using nested interpolation. 40 | [[link](#style-nested-interpolation)] 41 | 42 | ```ruby 43 | # bad 44 | "Hello, #{user.blank? ? 'guest' : "dear #{user.name}"}" 45 | 46 | # good 47 | user_name = user.blank? ? 'guest' : "dear #{user.name}" 48 | "Hello, #{user_name}" 49 | ``` 50 | 51 | * 52 | If elements of a hash literal span more than one line, we're aligning them by keys. 53 | Also, the first hash key is aligned by an indentation level. 54 | [[link](#style-hash-aligning)] 55 | 56 | 57 | ```ruby 58 | # bad 59 | { 60 | foo: bar, 61 | ba: baz 62 | } 63 | 64 | { 65 | foo: bar, 66 | ba: baz 67 | } 68 | 69 | { 70 | foo: bar, 71 | ba: baz 72 | } 73 | 74 | { 75 | foo: { 76 | bar: bar, 77 | ba: baz 78 | } 79 | } 80 | 81 | method_call({ 82 | its_like: :this 83 | }) 84 | 85 | # good 86 | { 87 | foo: bar, 88 | ba: baz 89 | } 90 | 91 | { 92 | foo: { 93 | bar: bar, 94 | ba: baz 95 | } 96 | } 97 | 98 | method_call({ 99 | no: :difference 100 | }) 101 | ``` 102 | 103 | * 104 | All arguments on a multi-line method definition are aligning by an indentation level. 105 | This rule works as for keyword arguments, as for usual arguments. 106 | [[link](#style-arguments-aligning)] 107 | 108 | ```ruby 109 | # bad 110 | do_something(foo: 1, 111 | bar: 2) 112 | 113 | # good 114 | do_something(foo: 1, 115 | bar: 2) 116 | 117 | # good 118 | foo :bar, 119 | :baz 120 | 121 | # bad 122 | foo :bar, 123 | :baz 124 | ``` 125 | 126 | * 127 | The parameters on a multi-line method call or definition are aligning by an indentation level. 128 | [[link](#style-parameters-aligning)] 129 | 130 | **Recommended:** write each parameter on the separate line. 131 | 132 | ```ruby 133 | # bad 134 | 135 | def foo(bar, baz, 136 | kek, lol) 137 | 123 138 | end 139 | 140 | # bad 141 | 142 | def foo( 143 | bar, 144 | baz) 145 | 123 146 | end 147 | 148 | # good 149 | 150 | def foo(bar, baz, 151 | kek, lol) 152 | 123 153 | end 154 | 155 | # better 156 | 157 | def foo( 158 | bar, 159 | baz, 160 | kek, 161 | lol 162 | ) 163 | 123 164 | end 165 | ``` 166 | 167 | * 168 | The elements of a multi-line array are aligning by an indentation level. 169 | [[link](#style-array-aligning)] 170 | 171 | ```ruby 172 | # bad 173 | 174 | array = [1, 2, 3, 175 | 4, 5, 6] 176 | 177 | # bad 178 | 179 | array = [1, 2, 3, 180 | 4, 5, 6] 181 | 182 | # good 183 | 184 | array = [1, 2, 3, 185 | 4, 5, 6] 186 | ``` 187 | 188 | * 189 | The indentation of the method name part in method calls that span more than one line are aligning by an indentation level. 190 | [[link](#style-multiline-method-call-indentation)] 191 | 192 | ```ruby 193 | # bad 194 | while myvariable 195 | .b 196 | # do something 197 | end 198 | 199 | # bad 200 | Thing.a 201 | .b 202 | .c 203 | 204 | # good 205 | while myvariable 206 | .b 207 | 208 | # do something 209 | end 210 | 211 | # good 212 | Thing.a 213 | .b 214 | .c 215 | ``` 216 | 217 | * 218 | The `end` shall be aligned with the left-hand-side of the variable assignment. But we prefer not to use code blocks with `end` for variable assignment and prefer move it into the separate methods. 219 | [[link](#style-end-aligning)] 220 | 221 | ```ruby 222 | # bad 223 | 224 | variable = if true 225 | end 226 | 227 | variable = array.map do |value| 228 | value 229 | end 230 | 231 | # good 232 | 233 | variable = if true 234 | end 235 | 236 | variable = 237 | if true 238 | end 239 | 240 | variable = array.map do |value| 241 | value 242 | end 243 | 244 | # better 245 | 246 | variable = condition_value 247 | 248 | def condition_value(*args) 249 | if true 250 | end 251 | end 252 | 253 | variable = values_from_array(array) 254 | 255 | def values_from_array(array) 256 | array.map do |value| 257 | value 258 | end 259 | end 260 | 261 | ``` 262 | 263 | * 264 | We're preferring a ruby style for methods indentations, not rails. You can check it [here](https://github.com/rubocop-hq/ruby-style-guide#indent-public-private-protected). 265 | [[link](#style-method-indentations)] 266 | 267 | ```ruby 268 | # bad 269 | class A 270 | def test 271 | puts "hello" 272 | puts "world" 273 | end 274 | end 275 | 276 | # bad 277 | class A 278 | def test 279 | puts "hello" 280 | puts "world" 281 | end 282 | 283 | protected 284 | 285 | def foo 286 | end 287 | 288 | private 289 | 290 | def bar 291 | end 292 | end 293 | 294 | # good 295 | class A 296 | def test 297 | puts "hello" 298 | puts "world" 299 | end 300 | end 301 | 302 | # good 303 | class A 304 | def test 305 | puts "hello" 306 | puts "world" 307 | end 308 | 309 | protected 310 | 311 | def foo 312 | end 313 | 314 | private 315 | 316 | def bar 317 | end 318 | end 319 | ``` 320 | 321 | * 322 | For hash literals not to add spaces after `{` or before `}`. We want to have the advantage of adding visual difference between block and hash literals. 323 | [[link](#style-hash-spaces)] 324 | 325 | ```ruby 326 | # bad 327 | h = { a: 1, b: 2 } 328 | Array.new(3) {|i| i + 1} 329 | 330 | # good 331 | h = {a: 1, b: 2} 332 | Array.new(3) { |i| i + 1 } 333 | ``` 334 | 335 | * 336 | Use `error` as a variable name on processing exceptions. 337 | [[link](#style-rescued-variable-name)] 338 | 339 | ```ruby 340 | # bad 341 | begin 342 | # do something 343 | rescue MyException => e 344 | # do something 345 | end 346 | 347 | # good 348 | begin 349 | # do something 350 | rescue MyException => error 351 | # do something 352 | end 353 | ``` 354 | 355 | 356 | * 357 | Write empty methods in an expanded way. 358 | [[link](#style-empty-method)] 359 | 360 | ```ruby 361 | # bad 362 | def foo(bar); end 363 | 364 | def self.foo(bar); end 365 | 366 | # good 367 | def foo(bar) 368 | end 369 | 370 | def self.foo(bar) 371 | end 372 | ``` 373 | 374 | * 375 | Use leading underscores in cached instance variable name. 376 | [[link](#style-cached-instance-variable-name)] 377 | 378 | ```ruby 379 | # bad 380 | def foo 381 | @something ||= calculate_expensive_thing 382 | end 383 | 384 | # bad 385 | def foo 386 | @foo ||= calculate_expensive_thing 387 | end 388 | 389 | # good 390 | def foo 391 | @_foo ||= calculate_expensive_thing 392 | end 393 | ``` 394 | 395 | * 396 | Requires a comma after the last argument, but only for parenthesized method calls where each argument is on its own line. 397 | [[link](#style-trailing-comma-in-arguments)] 398 | 399 | ```ruby 400 | # bad 401 | method(1, 2,) 402 | 403 | # good 404 | method(1, 2) 405 | 406 | # bad 407 | method( 408 | 1, 2, 409 | 3, 410 | ) 411 | 412 | # good 413 | method( 414 | 1, 2, 415 | 3 416 | ) 417 | 418 | # bad 419 | method( 420 | 1, 2, 3, 421 | ) 422 | 423 | # good 424 | method( 425 | 1, 2, 3 426 | ) 427 | 428 | # good 429 | method( 430 | 1, 431 | 2, 432 | ) 433 | ``` 434 | 435 | * 436 | Requires a comma after last item in an array, but only when each item is on its own line. 437 | [[link](#style-trailing-comma-in-array-literals)] 438 | 439 | ```ruby 440 | # bad 441 | a = [1, 2,] 442 | 443 | # good 444 | a = [1, 2] 445 | 446 | # bad 447 | a = [ 448 | 1, 2, 449 | 3, 450 | ] 451 | 452 | # good 453 | a = [ 454 | 1, 2, 455 | 3 456 | ] 457 | 458 | # bad 459 | a = [ 460 | 1, 2, 3, 461 | ] 462 | 463 | # good 464 | a = [ 465 | 1, 2, 3 466 | ] 467 | 468 | # good 469 | a = [ 470 | 1, 471 | 2, 472 | ] 473 | ``` 474 | 475 | * 476 | Requires a comma after the last item in a hash. 477 | [[link](#style-trailing-comma-in-hash-literal)] 478 | 479 | ```ruby 480 | # bad 481 | a = { foo: 1, bar: 2, } 482 | 483 | # good 484 | a = { foo: 1, bar: 2 } 485 | 486 | # bad 487 | a = { 488 | foo: 1, bar: 2, 489 | qux: 3, 490 | } 491 | 492 | # good 493 | a = { 494 | foo: 1, bar: 2, 495 | qux: 3 496 | } 497 | 498 | # bad 499 | a = { 500 | foo: 1, bar: 2, qux: 3, 501 | } 502 | 503 | # good 504 | a = { 505 | foo: 1, bar: 2, qux: 3 506 | } 507 | 508 | # good 509 | a = { 510 | foo: 1, 511 | bar: 2, 512 | } 513 | ``` 514 | 515 | * 516 | There are not any required rules for `frozen_string_literal` magic url. 517 | Set up [this cop](https://rubocop.readthedocs.io/en/latest/cops_style/#stylefrozenstringliteralcomment) depends on the project. 518 | So set up it on the local rubocop config manually. 519 | [[link](#style-magic-link)] 520 | -------------------------------------------------------------------------------- /doc/STYLE_GUIDE_GRAPHQL.md: -------------------------------------------------------------------------------- 1 | # GraphQL Style Guide 2 | 3 | This style is based on the rules from [RuboCop::GraphQL](https://github.com/DmitryTsepelev/rubocop-graphql) gem with a few differences. 4 | 5 | ## Table of contents 6 | 7 | * [Style](#Style) 8 | 9 | ## Style 10 | 11 | * 12 | Each argument should have a description. 13 | [[link](#graphql-argument-description)] 14 | 15 | ```ruby 16 | # bad 17 | class BanUser < BaseMutation 18 | argument :uuid, ID, required: true 19 | end 20 | 21 | # good 22 | class BanUser < BaseMutation 23 | argument :uuid, ID, required: true, description: "UUID of the user to ban" 24 | end 25 | ``` 26 | 27 | * 28 | All argument names should be snake_case. 29 | [[link](#graphql-argument-name)] 30 | 31 | ```ruby 32 | # bad 33 | class BanUser < BaseMutation 34 | argument :userId, ID, required: true 35 | end 36 | 37 | # good 38 | class BanUser < BaseMutation 39 | argument :user_id, ID, required: true 40 | end 41 | ``` 42 | 43 | * 44 | Avoid duplicate argument definitions. 45 | [[link](#graphql-argument-uniqueness)] 46 | 47 | ```ruby 48 | # bad 49 | class BanUser < BaseMutation 50 | argument :user_id, ID, required: true 51 | argument :user_id, ID, required: true 52 | end 53 | 54 | # good 55 | class BanUser < BaseMutation 56 | argument :user_id, ID, required: true 57 | end 58 | ``` 59 | 60 | * 61 | All field definitions should be grouped together. 62 | [[link](#graphql-field-definitions)] 63 | 64 | ```ruby 65 | # bad 66 | class UserType < BaseType 67 | field :first_name, String, null: true 68 | 69 | def first_name 70 | object.contact_data.first_name 71 | end 72 | 73 | field :last_name, String, null: true 74 | 75 | def last_name 76 | object.contact_data.last_name 77 | end 78 | end 79 | 80 | # good 81 | class UserType < BaseType 82 | field :first_name, String, null: true 83 | field :last_name, String, null: true 84 | 85 | def first_name 86 | object.contact_data.first_name 87 | end 88 | 89 | def last_name 90 | object.contact_data.last_name 91 | end 92 | end 93 | ``` 94 | 95 | * 96 | Each field should have a description. 97 | [[link](#graphql-field-description)] 98 | 99 | ```ruby 100 | # bad 101 | class UserType < BaseType 102 | field :name, String, null: true 103 | end 104 | 105 | # good 106 | class UserType < BaseType 107 | field :name, String, "Name of the user", null: true 108 | end 109 | ``` 110 | 111 | * 112 | Avoid unnecessary resolver methods in cases when :hash_key option can be used. 113 | [[link](#graphql-field-hash-key)] 114 | 115 | ```ruby 116 | # bad 117 | class Types::UserType < Types::BaseObject 118 | field :phone, String, null: true 119 | 120 | def phone 121 | object[:home_phone] 122 | end 123 | end 124 | 125 | # good 126 | class Types::UserType < Types::BaseObject 127 | field :phone, String, null: true, hash_key: :home_phone 128 | end 129 | ``` 130 | 131 | * 132 | Avoid unnecessary resolver methods in cases when :method option can be used. 133 | [[link](#graphql-field-mathod)] 134 | 135 | ```ruby 136 | # bad 137 | class Types::UserType < Types::BaseObject 138 | field :phone, String, null: true 139 | 140 | def phone 141 | object.home_phone 142 | end 143 | end 144 | 145 | # good 146 | class Types::UserType < Types::BaseObject 147 | field :phone, String, null: true, method: :home_phone 148 | end 149 | ``` 150 | 151 | * 152 | All field names should be snake_case. 153 | [[link](#graphql-field-name)] 154 | 155 | ```ruby 156 | # bad 157 | class UserType < BaseType 158 | field :firstName, String, null: true 159 | end 160 | 161 | # good 162 | class UserType < BaseType 163 | field :first_name, String, null: true 164 | end 165 | ``` 166 | 167 | * 168 | Avoid duplicate field definitions. 169 | [[link](#graphql-field-uniqueness)] 170 | 171 | ```ruby 172 | # bad 173 | class UserType < BaseType 174 | field :name, String, null: true 175 | field :phone, String, null: true 176 | field :phone, String, null: true do 177 | argument :something, String, required: false 178 | end 179 | end 180 | 181 | # good 182 | class UserType < BaseType 183 | field :name, String, null: true 184 | field :phone, String, null: true do 185 | argument :something, String, required: false 186 | end 187 | end 188 | ``` 189 | 190 | * 191 | Types and mutations should have graphql_name configured only if it's different from the default name. 192 | [[link](#graphql-graphql-name)] 193 | 194 | ```ruby 195 | # bad 196 | class UserType < BaseType 197 | graphql_name 'User' 198 | end 199 | 200 | # good 201 | class UserType < BaseType 202 | graphql_name 'Viewer' 203 | end 204 | ``` 205 | 206 | * 207 | Use max_complexity configuration in schema files. 208 | [[link](#graphql-max-complexity-schema)] 209 | 210 | ```ruby 211 | # good 212 | class AppSchema < BaseSchema 213 | max_complexity 42 214 | end 215 | ``` 216 | 217 | * 218 | Use max_depth configuration in schema files. 219 | [[link](#graphql-max-depth-schema)] 220 | 221 | ```ruby 222 | # good 223 | class AppSchema < BaseSchema 224 | max_depth 42 225 | end 226 | ``` 227 | 228 | * 229 | Fields with multiple definitions should be grouped together. 230 | [[link](#graphql-multiple-field-definitions)] 231 | 232 | ```ruby 233 | # bad 234 | class UserType < BaseType 235 | field :first_name, String, null: true 236 | 237 | def first_name 238 | object.contact_data.first_name 239 | end 240 | field :first_name, Name, null: true 241 | end 242 | 243 | # good 244 | class UserType < BaseType 245 | field :first_name, String, null: true 246 | field :first_name, Name, null: true 247 | 248 | def first_name 249 | object.contact_data.first_name 250 | end 251 | end 252 | ``` 253 | 254 | * 255 | Types that implement Node interface should have `.authorized?` check. Such types can be fetched by ID and therefore should have type level check to avoid accidental information exposure. 256 | If `.authorized?` is defined in a parent class, you can add parent to the "SafeBaseClasses" to avoid offenses in children. 257 | This cop also checks the `can_can_action` or `pundit_role` methods that can be used as part of the Ruby GraphQL Pro. 258 | [[link](#graphql-not-authorized-node-type)] 259 | 260 | ```ruby 261 | # bad 262 | class UserType < BaseType 263 | implements GraphQL::Types::Relay::Node 264 | 265 | field :uuid, ID, null: false 266 | end 267 | 268 | # good 269 | class UserType < BaseType 270 | implements GraphQL::Types::Relay::Node 271 | 272 | field :uuid, ID, null: false 273 | 274 | def self.authorized?(object, context) 275 | super && object.owner == context[:viewer] 276 | end 277 | end 278 | 279 | # good 280 | class UserType < BaseType 281 | implements GraphQL::Types::Relay::Node 282 | 283 | pundit_role :staff 284 | 285 | field :uuid, ID, null: false 286 | end 287 | 288 | # good 289 | class UserType < BaseType 290 | implements GraphQL::Types::Relay::Node 291 | 292 | can_can_action :staff 293 | 294 | field :uuid, ID, null: false 295 | end 296 | ``` 297 | 298 | * 299 | All types (objects, inputs, interfaces, scalars, unions, mutations, subscriptions, and resolvers) should have a description. 300 | [[link](#graphql-object-description)] 301 | 302 | ```ruby 303 | # bad 304 | class Types::UserType < Types::BaseObject 305 | ... 306 | end 307 | 308 | # good 309 | class Types::UserType < Types::BaseObject 310 | description "Represents application user" 311 | ... 312 | end 313 | ``` 314 | 315 | * 316 | Arguments should be alphabetically sorted within groups. 317 | [[link](#graphql-ordered-arguments)] 318 | 319 | ```ruby 320 | # bad 321 | class UpdateProfile < BaseMutation 322 | argument :uuid, ID, required: true 323 | argument :email, String, required: false 324 | argument :name, String, required: false 325 | end 326 | 327 | # good 328 | class UpdateProfile < BaseMutation 329 | argument :email, String, required: false 330 | argument :name, String, required: false 331 | end 332 | 333 | # good 334 | class UpdateProfile < BaseMutation 335 | argument :uuid, ID, required: true 336 | 337 | argument :email, String, required: false 338 | argument :name, String, required: false 339 | end 340 | ``` 341 | 342 | * 343 | Fields should be alphabetically sorted within groups. 344 | [[link](#graphql-ordered-fields)] 345 | 346 | ```ruby 347 | # bad 348 | class UpdateProfile < BaseMutation 349 | field :phone, String, null: true 350 | field :name, String, null: true 351 | end 352 | 353 | # good 354 | class UpdateProfile < BaseMutation 355 | field :name, String, null: true 356 | field :phone, String, null: true 357 | end 358 | 359 | # good 360 | class UpdateProfile < BaseMutation 361 | field :phone, String, null: true 362 | 363 | field :name, String, null: true 364 | end 365 | ``` 366 | 367 | * 368 | The length of a resolver method should not exceed 10 lines. 369 | [[link](#graphql-resolver-method-length)] 370 | 371 | * 372 | Avoid using :camelize option for arguments where it is unnecessary. 373 | [[link](#graphql-unnecessary-argument-camelize)] 374 | 375 | ```ruby 376 | # bad 377 | class UserType < BaseType 378 | argument :filter, String, required: false, camelize: false 379 | end 380 | 381 | # good 382 | class UserType < BaseType 383 | argument :filter, String, required: false 384 | end 385 | 386 | # good 387 | class UserType < BaseType 388 | argument :email_filter, String, required: false, camelize: true 389 | end 390 | ``` 391 | 392 | * 393 | Avoid defining an unnecessary alias, method, or resolver_method. 394 | [[link](#graphql-unnecessary-field-alias)] 395 | 396 | ```ruby 397 | # bad 398 | field :name, String, "Name of the user", null: true, alias: :name 399 | field :name, String, "Name of the user", null: true, method: :name 400 | field :name, String, "Name of the user", null: true, resolver_method: :name 401 | field :name, String, "Name of the user", null: true, hash_key: :name 402 | 403 | # good 404 | field :name, String, "Name of the user", null: true, alias: :real_name 405 | field :name, String, "Name of the user", null: true, method: :real_name 406 | field :name, String, "Name of the user", null: true, resolver_method: :real_name 407 | field :name, String, "Name of the user", null: true, hash_key: :real_name 408 | ``` 409 | 410 | * 411 | Avoid using :camelize option for fields where it is unnecessary. 412 | [[link](#graphql-unnecessary-field-camelize)] 413 | 414 | ```ruby 415 | # bad 416 | class UserType < BaseType 417 | field :name, "Name of the user", String, null: true, camelize: true 418 | end 419 | 420 | # good 421 | class UserType < BaseType 422 | field :name, String, "Name of the user", null: true 423 | end 424 | 425 | # good 426 | class UserType < BaseType 427 | field :first_name, "Name of the user", String, null: true, camelize: true 428 | end 429 | ``` 430 | 431 | * 432 | Arguments should either be listed explicitly or **rest should be in the resolve signature (and similar methods, such as #authorized?). 433 | [[link](#graphql-unused-argument)] 434 | 435 | ```ruby 436 | # bad 437 | class SomeResolver < Resolvers::Base 438 | type SomeType, null: false 439 | 440 | argument :arg1, String, required: true 441 | argument :arg2, String, required: true 442 | 443 | def authorized?; end 444 | def resolve(arg1:); end 445 | end 446 | 447 | # bad 448 | class SomeResolver < Resolvers::Base 449 | type SomeType, null: false 450 | 451 | argument :arg1, String, required: true 452 | argument :arg2, String, required: true 453 | 454 | def resolve; end 455 | end 456 | 457 | # good 458 | class SomeResolver < Resolvers::Base 459 | argument :arg1, String, required: true 460 | argument :user_id, String, required: true, loads: Types::UserType 461 | argument :post_id, String, loads: Types::PostType, as: :article 462 | argument :comment_ids, String, loads: Types::CommentType 463 | 464 | def authorized?(arg1:, user:, article:, comments:); end 465 | def resolve(arg1:, user:, article:, comments:); end 466 | end 467 | 468 | # good 469 | class SomeResolver < Resolvers::Base 470 | argument :arg1, String, required: true 471 | argument :user_id, String, required: true, loads: Types::UserType 472 | argument :comment_ids, String, loads: Types::CommentType 473 | 474 | def resolve(arg1:, **rest); end 475 | end 476 | 477 | # good 478 | class SomeResolver < Resolvers::Base 479 | type SomeType, null: false 480 | 481 | argument :arg1, String, required: true 482 | argument :arg2, String, required: true 483 | 484 | def resolve(args); end 485 | end 486 | ``` 487 | -------------------------------------------------------------------------------- /doc/STYLE_GUIDE_RAILS.md: -------------------------------------------------------------------------------- 1 | # Rails Style Guide 2 | 3 | This style is based on rules from [Ruby Style Guide](docs/STYLE_GUIDE.md). Also, we enforce rules from [community Rails Style Guide][1]. 4 | 5 | This is a small list of differences which we have when compared with community and our Ruby style guides: 6 | 7 | ## Table of contents 8 | 9 | * [Style](#Style) 10 | 11 | ## Style 12 | 13 | * 14 | Documentation is requeried for all files except `app` directory. 15 | [[link](#documentation)] 16 | 17 | * 18 | Prefer to use nested style of children definitions at classes and modules. 19 | [[link](#nested-style-and-modules)] 20 | 21 | ```ruby 22 | # bad 23 | class Api::V1::UsersController 24 | end 25 | 26 | # good 27 | module Api 28 | module V1 29 | class UsersController 30 | end 31 | end 32 | end 33 | ``` 34 | 35 | [1]: https://github.com/rubocop-hq/rails-style-guide 36 | -------------------------------------------------------------------------------- /doc/STYLE_GUIDE_RSPEC.md: -------------------------------------------------------------------------------- 1 | # Rspec Style Guide 2 | 3 | This style guide recommends best practices for writing a clear Rspec tests and enjoy this process. 4 | 5 | * 6 | We're happy to use [better spec rules](http://www.betterspecs.org/) for improving our tests. 7 | [[link](#rspec-betterrspec)] 8 | 9 | * 10 | We're not limiting a length of rspec files. 11 | [[link](#rspec-file-length)] 12 | 13 | * 14 | Nested context is a nice approach for organizing your code and tests structure. 15 | So there are not any limits for deep or nested groups. 16 | [[link](#rspec-nested-groups)] 17 | 18 | * 19 | We're not limiting a number of `let` blocks in describe and context blocks. 20 | [[link](#rspec-let-count)] 21 | 22 | * 23 | Each subject should be named, and we should not use `subject` in our test cases. 24 | Prefer to use `is_expected` that `expect(subject_name)` for small tests. 25 | [[link](#rspec-subject)] 26 | 27 | ```ruby 28 | # bad 29 | subject { service.call } 30 | 31 | it "test" do 32 | expect(subject).to eq value 33 | end 34 | 35 | # ok 36 | subject(:service_call) { service.call } 37 | 38 | it "test" do 39 | expect(service_call).to eq value 40 | end 41 | 42 | # better 43 | subject { service.call } 44 | 45 | it "test" do 46 | is_expected.to eq value 47 | end 48 | ``` 49 | 50 | * 51 | When describing a context, start its description with "when", "for", with" or "without". 52 | [[link](#rspec-context-wording)] 53 | 54 | ```ruby 55 | # bad 56 | context "the display name not present" do 57 | # ... 58 | end 59 | 60 | # good 61 | context "when the display name is not present" do 62 | # ... 63 | end 64 | ``` 65 | 66 | * 67 | A long example is usually more difficult to understand. Consider extracting out some behavior, e.g. with a `let` block, or a helper method. 68 | 69 | [[link](#rspec-example-length)] 70 | 71 | ```ruby 72 | # bad 73 | it 'creates correct deal config object' do 74 | expect(ZohoUtil::DealConfig).to receive(:new) 75 | .with( 76 | deal_name: translated_deal_name, 77 | stage_uid: zoho_post.stage_uid, 78 | change_existing_stage: zoho_post.change_existing_stage, 79 | note_title: translated_note_title, 80 | note: translated_note 81 | ) 82 | 83 | execute_action 84 | end 85 | 86 | # good 87 | let(:service_arguments) do 88 | { 89 | deal_name: translated_deal_name, 90 | stage_uid: zoho_post.stage_uid, 91 | change_existing_stage: zoho_post.change_existing_stage, 92 | note_title: translated_note_title, 93 | note: translated_note 94 | } 95 | end 96 | 97 | it 'creates correct deal config object' do 98 | expect(ZohoUtil::DealConfig).to receive(:new).with(**service_arguments) 99 | execute_action 100 | end 101 | ``` 102 | 103 | * 104 | Prefer using blocks for change matcher than method calls. 105 | [[link](#rspec-expect-change)] 106 | 107 | ```ruby 108 | # bad 109 | expect { run }.to change(Foo, :bar) 110 | 111 | # good 112 | expect { run }.to change { Foo.bar } 113 | ``` 114 | 115 | * 116 | Check spies messages post-factum (after calling some methods). 117 | [[link](#rspec-message-spies)] 118 | 119 | ```ruby 120 | # bad 121 | expect(foo).to receive(:bar) 122 | foo.bar 123 | 124 | # good 125 | foo.bar 126 | expect(foo).to have_received(:bar) 127 | ``` 128 | 129 | * 130 | Prefer using `expect` instead of `should` matchers. 131 | [[link](#rspec-prefer-expect)] 132 | 133 | ```ruby 134 | # bad 135 | calculator.compute(line_item).should == 5 136 | 137 | # good 138 | expect(calculator.compute(line_item)).to eq(5) 139 | ``` 140 | 141 | **Note:** this is a Part of Rspec configuration. Read more [by link](https://github.com/rubocop-hq/rubocop-rspec#enforcing-should-vs-expect-syntax). 142 | 143 | * 144 | Not to use `Rspec.describe` in your test and just write `describe` instead. 145 | [[link](#rspec-top-rspec)] 146 | 147 | ```ruby 148 | # bad 149 | RSpec.describe MyClass do 150 | # ... 151 | end 152 | 153 | # good 154 | describe MyClass do 155 | # ... 156 | end 157 | ``` 158 | 159 | **Note:** this is a Part of Rspec configuration. Read more [by link](https://github.com/rubocop-hq/rubocop-rspec#enforcing-an-explicit-rspec-receiver-for-top-level-methods-disabling-monkey-patching). 160 | 161 | * 162 | We allow using let! in your test, but suggest not to use it for setting up testing data. 163 | [[link](#rspec-let-setup)] 164 | 165 | ```ruby 166 | # ok 167 | let!(:my_widget) { create(:widget) } 168 | 169 | it "counts widgets" do 170 | expect(Widget.count).to eq(1) 171 | end 172 | 173 | # better 174 | before do 175 | create(:widget) 176 | end 177 | 178 | it "counts widgets" do 179 | expect(Widget.count).to eq(1) 180 | end 181 | 182 | # good 183 | let!(:my_widget) { create(:widget) } 184 | 185 | it "returns the last widget" do 186 | expect(Widget.last).to eq my_widget 187 | end 188 | ``` 189 | 190 | * 191 | Prefer using `before` instead of `setup`. 192 | [[link](#rspec-prefer-before)] 193 | 194 | ```ruby 195 | # bad 196 | setup do 197 | allow(post).to receive(:publish!) 198 | end 199 | 200 | # good 201 | before do 202 | allow(post).to receive(:publish!) 203 | end 204 | ``` 205 | -------------------------------------------------------------------------------- /lib/datarockets_style.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rubocop" 4 | require "datarockets_style/formatter/todo_list_formatter" 5 | 6 | require "datarockets_style/version" 7 | 8 | require "datarockets_style/cop/layout/array_alignment_extended" 9 | require "datarockets_style/cop/style/nested_interpolation" 10 | require "datarockets_style/cop/rspec/prefer_before_over_setup" 11 | 12 | # Top level module for datarockets-style 13 | module DatarocketsStyle 14 | # Datarickors sharable config 15 | end 16 | -------------------------------------------------------------------------------- /lib/datarockets_style/cop/layout/array_alignment_extended.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DatarocketsStyle 4 | module Cop 5 | module Layout 6 | # Here we check if the elements of a multi-line array literal are 7 | # aligned. 8 | # 9 | # @example EnforcedStyle: with_first_argument (default) 10 | # # good 11 | # 12 | # array = [1, 2, 3, 13 | # 4, 5, 6] 14 | # array = ['run', 15 | # 'forrest', 16 | # 'run'] 17 | # 18 | # # bad 19 | # 20 | # array = [1, 2, 3, 21 | # 4, 5, 6] 22 | # array = ['run', 23 | # 'forrest', 24 | # 'run'] 25 | # 26 | # @example EnforcedStyle: with_fixed_indentation 27 | # # good 28 | # 29 | # array = [1, 2, 3, 30 | # 4, 5, 6] 31 | # 32 | # # bad 33 | # 34 | # array = [1, 2, 3, 35 | # 4, 5, 6] 36 | class ArrayAlignmentExtended < RuboCop::Cop::Base 37 | include RuboCop::Cop::Alignment 38 | extend RuboCop::Cop::AutoCorrector 39 | 40 | ALIGN_PARAMS_MSG = "Align the elements of an array literal if they span more than one line." 41 | 42 | FIXED_INDENT_MSG = "Use one level of indentation for elements " \ 43 | "following the first line of a multi-line array." 44 | 45 | def on_array(node) 46 | return if node.children.size < 2 47 | 48 | check_alignment(node.children, base_column(node, node.children)) 49 | end 50 | 51 | def autocorrect(corrector, node) 52 | RuboCop::Cop::AlignmentCorrector.correct(corrector, processed_source, node, column_delta) 53 | end 54 | 55 | private 56 | 57 | def message(_node) 58 | fixed_indentation? ? FIXED_INDENT_MSG : ALIGN_PARAMS_MSG 59 | end 60 | 61 | def fixed_indentation? 62 | cop_config["EnforcedStyle"] == "with_fixed_indentation" 63 | end 64 | 65 | def base_column(node, args) 66 | fixed_indentation? ? line_indentation(node) : display_column(args.first.source_range) 67 | end 68 | 69 | def line_indentation(node) 70 | lineno = target_method_lineno(node) 71 | line = node.source_range.source_buffer.source_line(lineno) 72 | line_indentation = /\S.*/.match(line).begin(0) 73 | line_indentation + configured_indentation_width 74 | end 75 | 76 | def target_method_lineno(node) 77 | node.loc.line 78 | end 79 | end 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /lib/datarockets_style/cop/rspec/prefer_before_over_setup.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DatarocketsStyle 4 | module Cop 5 | module RSpec 6 | # Checks that tests use `before` instead of RoR unit-test `setup` method (part of `rspec-rails` gem) 7 | # 8 | # bad 9 | # 10 | # setup do 11 | # allow(post).to receive(:publish!) 12 | # end 13 | # 14 | # good 15 | # 16 | # before do 17 | # allow(post).to receive(:publish!) 18 | # end 19 | class PreferBeforeOverSetup < RuboCop::Cop::Cop 20 | MSG = "Use `before` instead of `setup`." 21 | 22 | def_node_matcher :setup_call?, <<-PATTERN 23 | (block 24 | (send _ :setup) 25 | (args) _) 26 | PATTERN 27 | 28 | def on_block(node) 29 | return unless setup_call?(node) 30 | 31 | add_offense(node) 32 | end 33 | 34 | def autocorrect(node) 35 | lambda do |corrector| 36 | block_internals = node.source.split(/ /) 37 | corrector.replace node.loc.expression, ["before", *block_internals[1..]].join(" ") 38 | end 39 | end 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/datarockets_style/cop/style/nested_interpolation.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DatarocketsStyle 4 | module Cop 5 | module Style 6 | # This cop checks nested interpolations 7 | # 8 | # @example 9 | # 10 | # # bad 11 | # "Hello, #{user.blank? ? 'guest' : "dear #{user.name}"}" 12 | # 13 | # # good 14 | # user_name = user.blank? ? 'guest' : "dear #{user.name}" 15 | # "Hello, #{user_name}" 16 | class NestedInterpolation < RuboCop::Cop::Cop 17 | include RuboCop::Cop::Interpolation 18 | 19 | MSG = "Redundant nested interpolation." 20 | 21 | def on_interpolation(node) 22 | node.each_descendant(:dstr) do |descendant_node| 23 | detect_double_interpolation(descendant_node) 24 | end 25 | end 26 | 27 | private 28 | 29 | def detect_double_interpolation(node) 30 | node.each_child_node(:begin) do |begin_node| 31 | add_offense(begin_node) 32 | end 33 | end 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/datarockets_style/formatter/todo_list_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "pathname" 4 | require_relative "todo_list_formatter/report_summary" 5 | 6 | # This formatter works like default formattter (display dots for files with no offenses and 7 | # letters for files with problems in the them). 8 | # 9 | # In the end, it shows report with sorted cops and files which can be added to rubocop config. 10 | # 11 | # Here's the format: 12 | # 13 | # Inspecting 3 files 14 | # .CC 15 | # 3 files inspected, 1005001 offenses detected 16 | # 17 | # LineLength 18 | # Exclude: 19 | # - "really/bad/file.rb" # 100500 20 | # - "almost/ok.rb" # 1 21 | class TodoListFormatter < RuboCop::Formatter::ProgressFormatter 22 | attr_reader :offense_list 23 | 24 | FileOffence = Struct.new(:file_path, :cop_name) 25 | 26 | def started(target_files) 27 | super 28 | @offense_list = [] 29 | end 30 | 31 | def file_finished(file, offenses) 32 | count_stats(offenses) 33 | report_file_as_mark(offenses) 34 | 35 | return if offenses.empty? 36 | 37 | path = Pathname.new(file).relative_path_from(Pathname.new(Dir.pwd)) 38 | 39 | offenses.reject(&:corrected?).each do |offense| 40 | offense_list << FileOffence.new(path, offense.cop_name) 41 | end 42 | end 43 | 44 | def finished(inspected_files) 45 | report_summary(inspected_files.length, 46 | @total_offense_count, 47 | @total_correction_count, 48 | @total_correctable_count) 49 | output.puts 50 | 51 | DatarocketsStyle::Formatter::TodoListFormatter::ReportSummary.new(offense_list).call(output) 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/datarockets_style/formatter/todo_list_formatter/report_summary.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DatarocketsStyle 4 | module Formatter 5 | module TodoListFormatter 6 | # Get file of pairs: file path and cop name - and prepare report for ToDo list formatter. 7 | # 8 | # Example of result: 9 | # 10 | # LineLength 11 | # Exclude: 12 | # - "really/bad/file.rb" # 100500 13 | # - "almost/ok.rb" # 1 14 | class ReportSummary 15 | attr_reader :offense_list 16 | 17 | FileGroup = Struct.new(:file, :offenses_count) do 18 | def print(output) 19 | output.puts " - '#{file}' # #{offenses_count}" 20 | end 21 | end 22 | 23 | OffenseGroup = Struct.new(:cop_name, :offenses) do 24 | def file_groups 25 | @_file_groups ||= offenses.group_by(&:file_path).map do |file, offenses| 26 | FileGroup.new(file, offenses.length) 27 | end 28 | end 29 | 30 | def print(output) 31 | output.puts("#{cop_name}:") 32 | output.puts(" Exclude:") 33 | file_groups.sort_by(&:file).each do |file_group| 34 | file_group.print(output) 35 | end 36 | output.puts 37 | end 38 | end 39 | 40 | def initialize(offense_list) 41 | @offense_list = offense_list 42 | end 43 | 44 | def call(output) 45 | offense_groups.sort_by(&:cop_name).each { |group| group.print(output) } 46 | end 47 | 48 | private 49 | 50 | def offense_groups 51 | @_offense_groups ||= offense_list.group_by(&:cop_name) 52 | .map { |cop_name, offenses| OffenseGroup.new(cop_name, offenses) } 53 | end 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/datarockets_style/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DatarocketsStyle 4 | VERSION = "1.6.0" 5 | end 6 | -------------------------------------------------------------------------------- /manual/cops_layout.md: -------------------------------------------------------------------------------- 1 | # Layout 2 | 3 | ## Layout/ArrayAlignmentExtended 4 | 5 | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged 6 | --- | --- | --- | --- | --- 7 | Enabled | Yes | Yes | 0.7.0 | - 8 | 9 | Here we check if the elements of a multi-line array literal are 10 | aligned. 11 | 12 | ### Examples 13 | 14 | #### EnforcedStyle: with_fixed_indentation (default) 15 | 16 | ```ruby 17 | # good 18 | 19 | array = [1, 2, 3, 20 | 4, 5, 6] 21 | 22 | # bad 23 | 24 | array = [1, 2, 3, 25 | 4, 5, 6] 26 | ``` 27 | 28 | #### EnforcedStyle: with_first_argument 29 | 30 | ```ruby 31 | # good 32 | 33 | array = [1, 2, 3, 34 | 4, 5, 6] 35 | array = ['run', 36 | 'forrest', 37 | 'run'] 38 | 39 | # bad 40 | 41 | array = [1, 2, 3, 42 | 4, 5, 6] 43 | array = ['run', 44 | 'forrest', 45 | 'run'] 46 | ``` 47 | 48 | ### Configurable attributes 49 | 50 | Name | Default value | Configurable values 51 | --- | --- | --- 52 | EnforcedStyle | `with_first_parameter` | `with_first_parameter`, `with_fixed_indentation` 53 | IndentationWidth | `` | Integer 54 | 55 | # RSpec 56 | 57 | ## RSpec/PreferBeforeOverSetup 58 | 59 | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged 60 | --- | --- | --- | --- | --- 61 | Enabled | Yes | Yes | 1.2.0 | - 62 | 63 | Checks that tests use `before` instead of RoR unit-test `setup` method (part of `rspec-rails` gem) 64 | 65 | ### Example 66 | 67 | ```ruby 68 | # bad 69 | setup do 70 | allow(post).to receive(:publish!) 71 | end 72 | 73 | # good 74 | before do 75 | allow(post).to receive(:publish!) 76 | end 77 | ``` 78 | 79 | # Style 80 | 81 | ## Style/NestedInterpolation 82 | 83 | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged 84 | --- | --- | --- | --- | --- 85 | Enabled | Yes | No | 0.8.0 | - 86 | 87 | This cop checks nested interpolations 88 | 89 | ### Example 90 | 91 | ```ruby 92 | # bad 93 | "Hello, #{user.blank? ? 'guest' : "dear #{user.name}"}" 94 | 95 | # good 96 | user_name = user.blank? ? 'guest' : "dear #{user.name}" 97 | "Hello, #{user_name}" 98 | ``` 99 | -------------------------------------------------------------------------------- /spec/datarockets_style/cop/layout/array_alignment_extended_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe DatarocketsStyle::Cop::Layout::ArrayAlignmentExtended do 4 | subject(:cop) { described_class.new(config) } 5 | 6 | let(:config) do 7 | RuboCop::Config.new( 8 | "Layout/ArrayAlignmentExtended" => { 9 | "EnforcedStyle" => cop_enforced_style, 10 | }, 11 | "Layout/IndentationWidth" => { 12 | "Width" => 2, 13 | }, 14 | ) 15 | end 16 | 17 | context "when aligned with first parameter" do 18 | let(:cop_enforced_style) { "with_first_parameter" } 19 | 20 | it "accepts single line array" do 21 | expect_no_offenses("array = [ a, b ]") 22 | end 23 | 24 | it "accepts several elements per line" do 25 | expect_no_offenses(<<~RUBY) 26 | array = [ a, b, 27 | c, d ] 28 | RUBY 29 | end 30 | 31 | it "accepts aligned array with fullwidth characters" do 32 | expect_no_offenses(<<~RUBY) 33 | puts 'Ruby', [ a, 34 | b ] 35 | RUBY 36 | end 37 | 38 | context "when basic usage" do 39 | let(:not_aligned_array) do 40 | <<~RUBY 41 | array = [a, 42 | b, 43 | ^ Align the elements of an array literal if they span more than one line. 44 | c, 45 | ^ Align the elements of an array literal if they span more than one line. 46 | d] 47 | ^ Align the elements of an array literal if they span more than one line. 48 | RUBY 49 | end 50 | 51 | let(:aligned_array) do 52 | <<~RUBY 53 | array = [a, 54 | b, 55 | c, 56 | d] 57 | RUBY 58 | end 59 | 60 | it "registers an offense and corrects misaligned array elements" do 61 | expect_offense(not_aligned_array) 62 | 63 | expect_correction(aligned_array) 64 | end 65 | 66 | it "accepts aligned array keys" do 67 | expect_no_offenses(aligned_array) 68 | end 69 | end 70 | 71 | context "when using nested arrays" do 72 | let(:not_aligned_arrays) do 73 | <<~RUBY 74 | [:l1, 75 | [:l2, 76 | [:l3, 77 | [:l4]]]] 78 | RUBY 79 | end 80 | 81 | it "does not auto-correct array within array with too much indentation" do 82 | expect_offense(<<~RUBY) 83 | [:l1, 84 | [:l2, 85 | ^^^^^ Align the elements of an array literal if they span more than one line. 86 | [:l3, 87 | ^^^^^ Align the elements of an array literal if they span more than one line. 88 | [:l4]]]] 89 | RUBY 90 | 91 | expect_correction(not_aligned_arrays) 92 | end 93 | 94 | it "does not auto-correct array within array with too little indentation" do 95 | expect_offense(<<~RUBY) 96 | [:l1, 97 | [:l2, 98 | ^^^^^ Align the elements of an array literal if they span more than one line. 99 | [:l3, 100 | ^^^^^ Align the elements of an array literal if they span more than one line. 101 | [:l4]]]] 102 | RUBY 103 | 104 | expect_correction(not_aligned_arrays) 105 | end 106 | end 107 | 108 | context "when arrays with heredoc strings" do 109 | let(:not_aligned_array) do 110 | <<~RUBY 111 | var = [ 112 | { :type => 'something', 113 | :sql => < 'something', 119 | ^^^^^^^^^^^^^^^^^^^^^^^ Align the elements of an array literal if they span more than one line. 120 | :sql => < 'something', 133 | :sql => < 'something', 139 | :sql => < 'something', 287 | :sql => < 'something', 293 | ^^^^^^^^^^^^^^^^^^^^^^^ Use one level of indentation for elements following the first line of a multi-line array. 294 | :sql => < 'something', 307 | :sql => < 'something', 313 | :sql => <