rubocop_method_order 0.1.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7211b5b76bc5400d2ced58be282733407e318266a1a7901337a9438c4491400a
4
+ data.tar.gz: 02a3d41c3a68f824a3571dffe4ae2e8c941f160775781759f26a3b1e7822e04c
5
+ SHA512:
6
+ metadata.gz: a173cdb0e717ddea7e00f3207fea70bd3c4162cd9e40c6621911714a12aa41584ac399331a403e6efa4d3be241cae90f77b3c61e397320004b5b51e936efa43b
7
+ data.tar.gz: e47bfee82e1a47cc63a973aa7e29261d057998ec015f9e72046a03d19c8324867cf4c257f4ea30bba9ffbabf603d96153cb4e29328ee6e7016d86af50dbb6669
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/.rubocop.yml ADDED
@@ -0,0 +1,16 @@
1
+ require:
2
+ - ./lib/rubocop/cop/style/method_order
3
+
4
+ AllCops:
5
+ Exclude:
6
+ - test/fixtures/files/**/*
7
+ TargetRubyVersion: 2.3
8
+
9
+ Metrics/LineLength:
10
+ Max: 100
11
+ Metrics/MethodLength:
12
+ Exclude:
13
+ - test/**/*_test.rb
14
+ Style/MethodOrder:
15
+ Exclude:
16
+ - test/**/*_test.rb
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,8 @@
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
+ # Specify your gem's dependencies in rubocop_method_order.gemspec
8
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rubocop_method_order (0.1.0.beta)
5
+ rubocop (~> 0.53)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.0)
11
+ minitest (5.11.3)
12
+ parallel (1.12.1)
13
+ parser (2.5.0.4)
14
+ ast (~> 2.4.0)
15
+ powerpack (0.1.1)
16
+ rainbow (3.0.0)
17
+ rake (12.3.0)
18
+ rubocop (0.53.0)
19
+ parallel (~> 1.10)
20
+ parser (>= 2.5)
21
+ powerpack (~> 0.1)
22
+ rainbow (>= 2.2.2, < 4.0)
23
+ ruby-progressbar (~> 1.7)
24
+ unicode-display_width (~> 1.0, >= 1.0.1)
25
+ ruby-progressbar (1.9.0)
26
+ unicode-display_width (1.3.0)
27
+
28
+ PLATFORMS
29
+ ruby
30
+
31
+ DEPENDENCIES
32
+ bundler (~> 1.16)
33
+ minitest (~> 5.0)
34
+ rake (~> 12.3)
35
+ rubocop_method_order!
36
+
37
+ BUNDLED WITH
38
+ 1.16.1
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Rubocop MethodOrder
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rubocop_method_order`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'rubocop_method_order'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install rubocop_method_order
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/CoffeeAndCode/rubocop_method_order.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+ require 'rubocop/rake_task'
6
+
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.libs << 'test'
9
+ t.libs << 'lib'
10
+ t.test_files = FileList['test/**/*_test.rb']
11
+ end
12
+ RuboCop::RakeTask.new
13
+
14
+ task default: %i[test rubocop]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'rubocop_method_order'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+ require_relative '../../../rubocop_method_order/method_collector'
5
+
6
+ module RuboCop
7
+ module Cop
8
+ module Style
9
+ # This cop enforces methods to be sorted alphabetically within the context
10
+ # of their scope and permission level such as `private` or `protected`.
11
+ # The method `initialize` is given special treatment by being required to
12
+ # be listed first in the context of a class if it's a public method.
13
+ #
14
+ # @example
15
+ #
16
+ # # bad
17
+ #
18
+ # # `foo` should be listed after `bar`. Both methods will actually
19
+ # # show a linter error with a message indicating if they should show
20
+ # # before or after the comparision method. The private methods will
21
+ # # also each show errors in relation to each other.
22
+ # class ExampleClass
23
+ # def foo
24
+ # end
25
+ #
26
+ # def bar
27
+ # end
28
+ #
29
+ # private
30
+ #
31
+ # def private_method
32
+ # end
33
+ #
34
+ # def another_method
35
+ # end
36
+ # end
37
+ #
38
+ # @example
39
+ #
40
+ # # bad
41
+ #
42
+ # # Works on modules too.
43
+ # module ExampleModule
44
+ # def foo
45
+ # end
46
+ #
47
+ # def bar
48
+ # end
49
+ # end
50
+ #
51
+ # @example
52
+ #
53
+ # # bad
54
+ #
55
+ # # As well as top level ruby methods in a file.
56
+ # def foo
57
+ # end
58
+ #
59
+ # def bar
60
+ # end
61
+ #
62
+ # @example
63
+ #
64
+ # # good
65
+ #
66
+ # class ExampleClass
67
+ # def bar
68
+ # end
69
+ #
70
+ # def foo
71
+ # end
72
+ #
73
+ # private
74
+ #
75
+ # def another_method
76
+ # end
77
+ #
78
+ # def private_method
79
+ # end
80
+ # end
81
+ #
82
+ # @example
83
+ #
84
+ # # good
85
+ #
86
+ # module ExampleModule
87
+ # def bar
88
+ # end
89
+ #
90
+ # def foo
91
+ # end
92
+ # end
93
+ #
94
+ # @example
95
+ #
96
+ # # good
97
+ #
98
+ # def bar
99
+ # end
100
+ #
101
+ # def foo
102
+ # end
103
+ class MethodOrder < Cop
104
+ include RangeHelp
105
+
106
+ MSG = 'Method `%<method>s` should come %<direction>s the method `%<other_method>s`.'
107
+
108
+ def autocorrect(node)
109
+ lambda do |corrector|
110
+ source_range = range_with_method_comments(node)
111
+ replace_range = range_with_method_comments(@autocorrect_replacements[node])
112
+ corrector.replace(replace_range, source_range.source)
113
+ end
114
+ end
115
+
116
+ def investigate(_processed_source)
117
+ @autocorrect_replacements = {}
118
+ @method_collector = RuboCopMethodOrder::MethodCollector.new(
119
+ should_skip_method: ->(node) { !cop_enabled_for_node?(node) }
120
+ )
121
+ @offenses_checked = false
122
+ end
123
+
124
+ # NOTE: Change this if cops get a callback method for running validations
125
+ # after the on_* methods have been executed.
126
+ def offenses(*args)
127
+ unless @offenses_checked
128
+ @offenses_checked = true
129
+ check_nodes
130
+ end
131
+
132
+ super(*args)
133
+ end
134
+
135
+ def on_class(node)
136
+ @method_collector.collect_nodes_from_class(node)
137
+ end
138
+
139
+ def on_def(node)
140
+ @method_collector.collect(node)
141
+ end
142
+ alias on_defs on_def
143
+
144
+ def on_module(node)
145
+ @method_collector.collect_nodes_from_module(node)
146
+ end
147
+
148
+ private
149
+
150
+ def begin_pos_with_comments(node)
151
+ range = node.source_range
152
+ begin_of_first_line = range.begin_pos - range.column
153
+ line_number = range.first_line
154
+
155
+ loop do
156
+ line_number -= 1
157
+ source_line = @processed_source.buffer.source_line(line_number)
158
+ break unless source_line.match?(/\s*#/)
159
+
160
+ begin_of_first_line -= source_line.length + 1 # account for \n char
161
+ end
162
+ begin_of_first_line
163
+ end
164
+
165
+ def check_nodes
166
+ @method_collector.nodes_by_scope.values.each do |method_collection|
167
+ @autocorrect_replacements.merge!(method_collection.replacements)
168
+ method_collection.offenses.each do |offense|
169
+ add_offense(offense[:node], location: :expression, message: message(
170
+ offense[:node].method_name,
171
+ offense[:other_node].method_name,
172
+ offense[:direction]
173
+ ))
174
+ end
175
+ end
176
+ end
177
+
178
+ def cop_enabled_for_node?(node)
179
+ processed_source.comment_config.cop_enabled_at_line?(self, node.first_line)
180
+ end
181
+
182
+ def end_pos_with_comments(node)
183
+ range = node.source_range
184
+ last_line = @processed_source.buffer.source_line(range.last_line)
185
+ range.end_pos + last_line.length - range.last_column + 1 # account for \n char
186
+ end
187
+
188
+ def message(method_name, following_method_name, direction)
189
+ format(MSG,
190
+ direction: direction,
191
+ method: method_name,
192
+ other_method: following_method_name)
193
+ end
194
+
195
+ def range_with_method_comments(node)
196
+ Parser::Source::Range.new(@processed_source.buffer,
197
+ begin_pos_with_comments(node),
198
+ end_pos_with_comments(node))
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './method_node_collection'
4
+ require_relative './public_method_node_collection'
5
+
6
+ module RuboCopMethodOrder
7
+ # This object collects every method definition found during a cop run and will
8
+ # attempt to split them by scope and context. If a method is disabled by a
9
+ # Rubocop comment it is not added to the collector. A method is also only
10
+ # collected once.
11
+ class MethodCollector
12
+ attr_reader :all_method_nodes,
13
+ :nodes_by_scope
14
+
15
+ def initialize(should_skip_method:)
16
+ @all_method_nodes = []
17
+ @should_skip_method = should_skip_method
18
+ @nodes_by_scope = {}
19
+ end
20
+
21
+ def collect(def_node, scope_name = 'global')
22
+ return if @should_skip_method.call(def_node)
23
+ return if @all_method_nodes.include?(def_node)
24
+
25
+ @all_method_nodes << def_node
26
+
27
+ unless @nodes_by_scope.key?(scope_name)
28
+ @nodes_by_scope[scope_name] = new_node_collection(scope_name)
29
+ end
30
+ @nodes_by_scope[scope_name].push(def_node)
31
+ end
32
+
33
+ def collect_nodes_from_class(class_node)
34
+ base_scope_name = "CLASS:#{scope_for_node(class_node)}"
35
+
36
+ mode = :public
37
+ child_nodes_from_container(class_node).each do |node|
38
+ if node.type == :def
39
+ collect(node, "#{base_scope_name}:#{mode}_methods")
40
+ elsif node.type == :defs
41
+ collect(node, "#{base_scope_name}:class_methods")
42
+ end
43
+
44
+ mode = node.method_name if scope_change_node?(node)
45
+ end
46
+ end
47
+
48
+ def collect_nodes_from_module(module_node)
49
+ base_scope_name = "MODULE:#{scope_for_node(module_node)}"
50
+
51
+ child_nodes_from_container(module_node).each do |node|
52
+ if node.type == :def
53
+ collect(node, "#{base_scope_name}:public_methods")
54
+ elsif node.type == :defs
55
+ collect(node, "#{base_scope_name}:class_methods")
56
+ end
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def child_nodes_from_container(node)
63
+ begin_node = node.child_nodes.select { |x| x&.type == :begin }.first
64
+ begin_node.nil? ? node.child_nodes : begin_node.child_nodes
65
+ end
66
+
67
+ def new_node_collection(scope_name)
68
+ if scope_name.match?(/:public_methods$/)
69
+ PublicMethodNodeCollection.new
70
+ else
71
+ MethodNodeCollection.new
72
+ end
73
+ end
74
+
75
+ def scope_change_node?(node)
76
+ node.type == :send && %i[private protected].include?(node.method_name)
77
+ end
78
+
79
+ def scope_for_node(node)
80
+ "#{node.source_range.begin_pos}-#{node.source_range.end_pos}"
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCopMethodOrder
4
+ # Hold collection of public instance methods that has custom sorted order.
5
+ class MethodNodeCollection
6
+ attr_reader :nodes
7
+
8
+ def initialize
9
+ @nodes = []
10
+ end
11
+
12
+ def offenses
13
+ nodes.reject { |node| method_order_correct?(node) }.map do |node|
14
+ is_after = expected_method_index(node) > nodes.index(node)
15
+
16
+ {
17
+ direction: is_after ? 'after' : 'before',
18
+ node: node,
19
+ other_node: is_after ? previous_method_node(node) : next_method_node(node)
20
+ }
21
+ end
22
+ end
23
+
24
+ def push(method_node)
25
+ @nodes << method_node
26
+ self
27
+ end
28
+
29
+ def replacements
30
+ nodes.reject { |node| method_order_correct?(node) }.each_with_object({}) do |node, obj|
31
+ obj[node] = nodes[expected_method_index(node)]
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def expected_method_index(method_node)
38
+ ordered_nodes.index(method_node)
39
+ end
40
+
41
+ def method_order_correct?(method_node)
42
+ nodes.index(method_node) == ordered_nodes.index(method_node)
43
+ end
44
+
45
+ def next_method_node(method_node)
46
+ expected_index = expected_method_index(method_node)
47
+ return if expected_index.nil?
48
+ return if expected_index + 1 >= nodes.size
49
+ ordered_nodes[expected_index + 1]
50
+ end
51
+
52
+ def ordered_nodes
53
+ nodes.sort_by(&:method_name)
54
+ end
55
+
56
+ def previous_method_node(method_node)
57
+ expected_index = expected_method_index(method_node)
58
+ return if expected_index.nil?
59
+ return if (expected_index - 1).negative?
60
+ ordered_nodes[expected_index - 1]
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCopMethodOrder
4
+ # Hold collection of public instance methods that has custom sorted order.
5
+ class PublicMethodNodeCollection < MethodNodeCollection
6
+ def initialize
7
+ @initialize_node = nil
8
+ super
9
+ end
10
+
11
+ def ordered_nodes
12
+ if @initialize_node
13
+ [@initialize_node] + (super - [@initialize_node])
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ def push(method_node)
20
+ @initialize_node = method_node if method_node.method_name == :initialize
21
+ super(method_node)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCopMethodOrder
4
+ VERSION = '0.1.0.beta'
5
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop/cop/style/method_order'
4
+ require 'rubocop_method_order/method_collector'
5
+ require 'rubocop_method_order/method_node_collection'
6
+ require 'rubocop_method_order/public_method_node_collection'
7
+ require 'rubocop_method_order/version'
@@ -0,0 +1,34 @@
1
+
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path('lib', __dir__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ require 'rubocop_method_order'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = 'rubocop_method_order'
10
+ spec.version = RuboCopMethodOrder::VERSION
11
+ spec.licenses = ['MIT']
12
+ spec.authors = ['Jonathan Knapp']
13
+ spec.email = ['jon@coffeeandcode.com']
14
+
15
+ spec.summary = 'Provide order to your Ruby files by expecting methods' \
16
+ ' to be listed alphabetically by permission group (public,' \
17
+ ' private, protected).'
18
+ spec.homepage = 'https://github.com/CoffeeAndCode/rubocop_method_order'
19
+ spec.metadata = { 'source_code_uri' => 'https://github.com/CoffeeAndCode/rubocop_method_order' }
20
+
21
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
22
+ f.match(%r{^(test|spec|features)/})
23
+ end
24
+ spec.bindir = 'exe'
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ['lib']
27
+ spec.required_ruby_version = '>= 2.3.0'
28
+
29
+ spec.add_dependency 'rubocop', '~> 0.53'
30
+
31
+ spec.add_development_dependency 'bundler', '~> 1.16'
32
+ spec.add_development_dependency 'minitest', '~> 5.0'
33
+ spec.add_development_dependency 'rake', '~> 12.3'
34
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubocop_method_order
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.beta
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Knapp
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-03-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubocop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.53'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.53'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.16'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '12.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '12.3'
69
+ description:
70
+ email:
71
+ - jon@coffeeandcode.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rubocop.yml"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - Gemfile.lock
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - lib/rubocop/cop/style/method_order.rb
86
+ - lib/rubocop_method_order.rb
87
+ - lib/rubocop_method_order/method_collector.rb
88
+ - lib/rubocop_method_order/method_node_collection.rb
89
+ - lib/rubocop_method_order/public_method_node_collection.rb
90
+ - lib/rubocop_method_order/version.rb
91
+ - rubocop_method_order.gemspec
92
+ homepage: https://github.com/CoffeeAndCode/rubocop_method_order
93
+ licenses:
94
+ - MIT
95
+ metadata:
96
+ source_code_uri: https://github.com/CoffeeAndCode/rubocop_method_order
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: 2.3.0
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.3.1
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.7.3
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Provide order to your Ruby files by expecting methods to be listed alphabetically
117
+ by permission group (public, private, protected).
118
+ test_files: []