Skip to content

Commit

Permalink
[#3600] Add new cop Bundler/OrderedGems (#3657)
Browse files Browse the repository at this point in the history
  • Loading branch information
tdeo authored and bbatsov committed Nov 22, 2016
1 parent 1637fe5 commit bfa08d8
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -12,6 +12,7 @@
* New cop `Rails/EnumUniqueness` checks for duplicate values defined in enum config. ([@olliebennett][])
* New cop `Rails/EnumUniqueness` checks for duplicate values defined in enum config hash. ([@olliebennett][])
* [#3451](https://github.com/bbatsov/rubocop/issues/3451): Add new `require_parentheses_when_complex` style to `Style/TernaryParentheses` cop. ([@swcraig][])
* [#3600](https://github.com/bbatsov/rubocop/issues/3600): Add new `Bundler/OrderedGems` cop. ([@tdeo][])

### Changes

Expand Down Expand Up @@ -2505,3 +2506,4 @@
[@aroben]: https://github.com/aroben
[@olliebennett]: https://github.com/olliebennett
[@aesthetikx]: https://github.com/aesthetikx
[@tdeo]: https://github.com/tdeo
4 changes: 2 additions & 2 deletions Gemfile
Expand Up @@ -4,11 +4,11 @@ source 'https://rubygems.org'

gemspec

gem 'pry'
gem 'rake', '~> 11.0'
gem 'rspec', '~> 3.5.0'
gem 'yard', '~> 0.8'
gem 'simplecov', '~> 0.10'
gem 'pry'
gem 'yard', '~> 0.8'

group :test do
gem 'codeclimate-test-reporter', '~> 1.0', require: false
Expand Down
9 changes: 9 additions & 0 deletions config/enabled.yml
Expand Up @@ -1577,3 +1577,12 @@ Bundler/DuplicatedGem:
Include:
- '**/Gemfile'
- '**/gems.rb'

Bundler/OrderedGems:
Description: >-
Sort alphabetically gems appearing within a contiguous set
of lines in the Gemfile
Enabled: true
Include:
- '**/Gemfile'
- '**/gems.rb'
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -92,6 +92,7 @@
require 'rubocop/cop/mixin/unused_argument'

require 'rubocop/cop/bundler/duplicated_gem'
require 'rubocop/cop/bundler/ordered_gems'

require 'rubocop/cop/lint/ambiguous_operator'
require 'rubocop/cop/lint/ambiguous_regexp_literal'
Expand Down
54 changes: 54 additions & 0 deletions lib/rubocop/cop/bundler/ordered_gems.rb
@@ -0,0 +1,54 @@
# frozen_string_literal: true
module RuboCop
module Cop
module Bundler
# Gems in consecutive lines should be alphabetically sorted
# @example
# # bad
# gem 'rubocop'
# gem 'rspec'
#
# # good
# gem 'rspec'
# gem 'rubocop'
#
# # good
# gem 'rubocop'
#
# gem 'rspec'
class OrderedGems < Cop
MSG = 'Gem `%s` should appear before `%s` in their gem group.'.freeze
def investigate(processed_source)
return if processed_source.ast.nil?
gem_declarations(processed_source.ast)
.each_cons(2) do |previous, current|
next unless consecutive_lines(previous, current)
next unless current.children[2].children.first.to_s <
previous.children[2].children.first.to_s
register_offense(previous, current)
end
end

def consecutive_lines(previous, current)
previous.source_range.last_line == current.source_range.first_line - 1
end

def register_offense(previous, current)
add_offense(
current,
current.source_range,
format(
MSG,
current.children[2].children.first,
previous.children[2].children.first
)
)
end

def_node_search :gem_declarations, <<-PATTERN
(:send, nil, :gem, ...)
PATTERN
end
end
end
end
32 changes: 32 additions & 0 deletions manual/cops_bundler.md
Expand Up @@ -39,3 +39,35 @@ Attribute | Value
--- | ---
Include | \*\*/Gemfile, \*\*/gems.rb


## Bundler/OrderedGems

Enabled by default | Supports autocorrection
--- | ---
Enabled | No

Gems in consecutive lines should be alphabetically sorted

### Example

```ruby
# bad
gem 'rubocop'
gem 'rspec'

# good
gem 'rspec'
gem 'rubocop'

# good
gem 'rubocop'

gem 'rspec'
```

### Important attributes

Attribute | Value
--- | ---
Include | \*\*/Gemfile, \*\*/gems.rb

81 changes: 81 additions & 0 deletions spec/rubocop/cop/bundler/ordered_gems_spec.rb
@@ -0,0 +1,81 @@
# frozen_string_literal: true

require 'spec_helper'

describe RuboCop::Cop::Bundler::OrderedGems, :config do
subject(:cop) { described_class.new(config) }

context 'When gems are alphabetically sorted' do
let(:source) { <<-END }
gem 'rspec'
gem 'rubocop'
END

it 'does not register any offenses' do
inspect_source(cop, source)
expect(cop.offenses).to be_empty
end
end

context 'When gems are not alphabetically sorted' do
let(:source) { <<-END }
gem 'rubocop'
gem 'rspec'
END

it 'registers an offense' do
inspect_source(cop, source)
expect(cop.offenses.size).to eq(1)
end

it 'mentions both gem names in message' do
inspect_source(cop, source)
expect(cop.offenses.first.message).to include('rspec')
expect(cop.offenses.first.message).to include('rubocop')
end

it 'highlights the second gem' do
inspect_source(cop, source)
expect(cop.highlights).to eq(["gem 'rspec'"])
end
end

context 'When each individual group of line is sorted' do
let(:source) { <<-END }
gem 'rspec'
gem 'rubocop'
gem 'hello'
gem 'world'
END

it 'does not register any offenses' do
inspect_source(cop, source)
expect(cop.offenses).to be_empty
end
end

context 'When a gem declaration takes several lines' do
let(:source) { <<-END }
gem 'rubocop',
'0.1.1'
gem 'rspec'
END

it 'registers an offense' do
inspect_source(cop, source)
expect(cop.offenses.size).to eq(1)
end
end

context 'When the gemfile is empty' do
let(:source) { <<-END }
# Gemfile
END

it 'does not register any offenses' do
inspect_source(cop, source)
expect(cop.offenses.size).to eq(0)
end
end
end

0 comments on commit bfa08d8

Please sign in to comment.