rubocop-cask 0.1.0 → 0.2.0
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 +4 -4
- data/README.md +6 -6
- data/config/default.yml +8 -0
- data/lib/rubocop-cask.rb +8 -3
- data/lib/rubocop/cask/ast/cask_block.rb +61 -0
- data/lib/rubocop/cask/ast/cask_header.rb +61 -0
- data/lib/rubocop/cask/ast/stanza.rb +57 -0
- data/lib/rubocop/cask/cask_help.rb +2 -20
- data/lib/rubocop/cask/constants.rb +54 -0
- data/lib/rubocop/cask/extend/astrolabe/node.rb +32 -0
- data/lib/rubocop/cask/extend/string.rb +6 -0
- data/lib/rubocop/cask/version.rb +1 -1
- data/lib/rubocop/cop/cask/no_dsl_version.rb +3 -3
- data/lib/rubocop/cop/cask/stanza_grouping.rb +96 -0
- data/lib/rubocop/cop/cask/stanza_order.rb +51 -0
- data/spec/rubocop/cop/cask/no_dsl_version_spec.rb +56 -77
- data/spec/rubocop/cop/cask/stanza_grouping_spec.rb +194 -0
- data/spec/rubocop/cop/cask/stanza_order_spec.rb +213 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/support/cop_shared_examples.rb +33 -0
- metadata +16 -5
- data/lib/rubocop/cask/cask_block.rb +0 -20
- data/lib/rubocop/cask/cask_header.rb +0 -59
- data/lib/rubocop/cask/node_help.rb +0 -81
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eedae76807564012ce59a4b4a81af9751a851d2a
|
4
|
+
data.tar.gz: f7d783a06b07aaa8ddb2fdeec8f1ad9969851c40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3009d1c3e6233735a9d308720627f8e44a504270d730e52fb08b7734957bd4e4bd26d322a1baf3538e816b848dfcf533392ceb1fbc8c83c8b48ea4e0be9d90f
|
7
|
+
data.tar.gz: 65e425329851c85db2789774311a20b8fe6680638dc285ec703ee7e448be73e10bf6602de593ea639d855a912dd49355dd070c9fed9d2675e8e6e79fcf093e4c
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# RuboCop Cask
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/rubocop-cask)
|
4
|
-
[](https://gemnasium.com/caskroom/rubocop-cask)
|
5
|
+
[](https://travis-ci.org/caskroom/rubocop-cask)
|
6
|
+
[](https://codeclimate.com/github/caskroom/rubocop-cask)
|
7
|
+
[](https://codeclimate.com/github/caskroom/rubocop-cask)
|
8
|
+
[](http://inch-ci.org/github/caskroom/rubocop-cask)
|
9
9
|
|
10
10
|
Cask-specific analysis for your Homebrew-Cask taps, as an extension to
|
11
11
|
[RuboCop](https://github.com/bbatsov/rubocop). Heavily inspired by [`rubocop-rspec`](https://github.com/nevir/rubocop-rspec).
|
@@ -78,7 +78,7 @@ Cask/NoDslVersion:
|
|
78
78
|
For running the spec files, this project depends on RuboCop's spec helpers. This means that in order to run the specs locally, you need a (shallow) clone of the RuboCop repository:
|
79
79
|
|
80
80
|
```bash
|
81
|
-
git
|
81
|
+
git submodule update --init --depth 1 vendor/rubocop
|
82
82
|
```
|
83
83
|
|
84
84
|
## License
|
data/config/default.yml
CHANGED
@@ -1,3 +1,11 @@
|
|
1
1
|
Cask/NoDslVersion:
|
2
2
|
Description: 'Do not use the deprecated DSL version syntax in your cask header.'
|
3
3
|
Enabled: true
|
4
|
+
|
5
|
+
Cask/StanzaOrder:
|
6
|
+
Description: 'Ensure that cask stanzas are sorted correctly. More info at https://github.com/caskroom/homebrew-cask/blob/master/CONTRIBUTING.md#stanza-order'
|
7
|
+
Enabled: true
|
8
|
+
|
9
|
+
Cask/StanzaGrouping:
|
10
|
+
Description: 'Ensure that cask stanzas are grouped correctly. More info at https://github.com/caskroom/homebrew-cask/blob/master/CONTRIBUTING.md#stanza-order'
|
11
|
+
Enabled: true
|
data/lib/rubocop-cask.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
require 'rubocop'
|
2
2
|
|
3
|
-
require 'rubocop/cask/
|
3
|
+
require 'rubocop/cask/constants'
|
4
|
+
require 'rubocop/cask/extend/string'
|
5
|
+
require 'rubocop/cask/extend/astrolabe/node'
|
4
6
|
require 'rubocop/cask/cask_help'
|
5
|
-
require 'rubocop/cask/cask_header'
|
6
|
-
require 'rubocop/cask/cask_block'
|
7
|
+
require 'rubocop/cask/ast/cask_header'
|
8
|
+
require 'rubocop/cask/ast/cask_block'
|
9
|
+
require 'rubocop/cask/ast/stanza'
|
7
10
|
require 'rubocop/cask/version'
|
8
11
|
require 'rubocop/cask/inject'
|
9
12
|
|
10
13
|
RuboCop::Cask::Inject.defaults!
|
11
14
|
|
12
15
|
require 'rubocop/cop/cask/no_dsl_version'
|
16
|
+
require 'rubocop/cop/cask/stanza_order'
|
17
|
+
require 'rubocop/cop/cask/stanza_grouping'
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cask
|
5
|
+
module AST
|
6
|
+
# This class wraps the AST block node that represents the entire cask
|
7
|
+
# definition. It includes various helper methods to aid cops in their
|
8
|
+
# analysis.
|
9
|
+
class CaskBlock
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
def initialize(block_node)
|
13
|
+
@block_node = block_node
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :block_node
|
17
|
+
|
18
|
+
alias_method :cask_node, :block_node
|
19
|
+
|
20
|
+
def cask_body
|
21
|
+
cask_node.block_body
|
22
|
+
end
|
23
|
+
|
24
|
+
def header
|
25
|
+
@header ||= CaskHeader.new(cask_node.method_node)
|
26
|
+
end
|
27
|
+
|
28
|
+
def stanzas
|
29
|
+
@stanzas ||= cask_body.each_descendant(:send)
|
30
|
+
.map { |n| Stanza.new(n) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def toplevel_stanzas
|
34
|
+
@toplevel_stanzas ||= stanzas.select(&:toplevel_stanza?)
|
35
|
+
end
|
36
|
+
|
37
|
+
def sorted_toplevel_stanzas
|
38
|
+
@sorted_toplevel_stanzas ||= sort_stanzas(toplevel_stanzas)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def sort_stanzas(stanzas)
|
44
|
+
stanzas.sort do |s1, s2|
|
45
|
+
i1, i2 = [s1, s2].map { |s| stanza_order_index(s) }
|
46
|
+
if i1 != i2
|
47
|
+
i1 - i2
|
48
|
+
else
|
49
|
+
i1, i2 = [s1, s2].map { |s| stanzas.index(s) }
|
50
|
+
i1 - i2
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def stanza_order_index(stanza)
|
56
|
+
Constants::STANZA_ORDER.index(stanza.stanza_name)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cask
|
3
|
+
module AST
|
4
|
+
# This class wraps the AST method node that represents the cask header. It
|
5
|
+
# includes various helper methods to aid cops in their analysis.
|
6
|
+
class CaskHeader
|
7
|
+
include CaskHelp
|
8
|
+
|
9
|
+
attr_reader :method_node
|
10
|
+
|
11
|
+
def initialize(method_node)
|
12
|
+
@method_node = method_node
|
13
|
+
end
|
14
|
+
|
15
|
+
def dsl_version?
|
16
|
+
hash_node
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_cask?
|
20
|
+
header_method_name == :test_cask || dsl_version.to_s.include?('test')
|
21
|
+
end
|
22
|
+
|
23
|
+
def header_str
|
24
|
+
@header_str ||= source_range.source
|
25
|
+
end
|
26
|
+
|
27
|
+
def source_range
|
28
|
+
@source_range ||= method_node.loc.expression
|
29
|
+
end
|
30
|
+
|
31
|
+
def preferred_header_str
|
32
|
+
"#{preferred_header_method_name} '#{cask_token}'"
|
33
|
+
end
|
34
|
+
|
35
|
+
def preferred_header_method_name
|
36
|
+
test_cask? ? :test_cask : :cask
|
37
|
+
end
|
38
|
+
|
39
|
+
def header_method_name
|
40
|
+
@header_method_name ||= method_node.method_name
|
41
|
+
end
|
42
|
+
|
43
|
+
def dsl_version
|
44
|
+
@dsl_version ||= pair_node.key_node.children.first
|
45
|
+
end
|
46
|
+
|
47
|
+
def cask_token
|
48
|
+
@cask_token ||= pair_node.val_node.children.first
|
49
|
+
end
|
50
|
+
|
51
|
+
def hash_node
|
52
|
+
@hash_node ||= method_node.each_child_node(:hash).first
|
53
|
+
end
|
54
|
+
|
55
|
+
def pair_node
|
56
|
+
@pair_node ||= hash_node.each_child_node(:pair).first
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cask
|
5
|
+
module AST
|
6
|
+
# This class wraps the AST send node that encapsulates the method call
|
7
|
+
# that comprises the stanza. It includes various helper methods to aid
|
8
|
+
# cops in their analysis.
|
9
|
+
class Stanza
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
def initialize(method_node)
|
13
|
+
@method_node = method_node
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :method_node
|
17
|
+
|
18
|
+
alias_method :stanza_node, :method_node
|
19
|
+
|
20
|
+
def_delegator :stanza_node, :method_name, :stanza_name
|
21
|
+
def_delegator :stanza_node, :parent, :parent_node
|
22
|
+
|
23
|
+
def expression
|
24
|
+
stanza_node.expression
|
25
|
+
end
|
26
|
+
|
27
|
+
def_delegator :expression, :source
|
28
|
+
|
29
|
+
def stanza_group
|
30
|
+
Constants::STANZA_GROUP_HASH[stanza_name]
|
31
|
+
end
|
32
|
+
|
33
|
+
def same_group?(other)
|
34
|
+
stanza_group == other.stanza_group
|
35
|
+
end
|
36
|
+
|
37
|
+
def toplevel_stanza?
|
38
|
+
parent_node.parent.cask_block?
|
39
|
+
end
|
40
|
+
|
41
|
+
def ==(other)
|
42
|
+
self.class == other.class && stanza_node == other.stanza_node
|
43
|
+
end
|
44
|
+
|
45
|
+
alias_method :eql?, :==
|
46
|
+
|
47
|
+
Constants::STANZA_ORDER.each do |stanza_name|
|
48
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
49
|
+
def #{stanza_name}?
|
50
|
+
stanza_name == :#{stanza_name}
|
51
|
+
end
|
52
|
+
EOS
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -2,30 +2,12 @@ module RuboCop
|
|
2
2
|
module Cask
|
3
3
|
# Common functionality for cops checking casks
|
4
4
|
module CaskHelp
|
5
|
-
include NodeHelp
|
6
|
-
|
7
|
-
CASK_METHOD_NAMES = %i(cask test_cask)
|
8
|
-
|
9
|
-
def cask?(block_node)
|
10
|
-
block?(block_node) && cask_method?(method_node(block_node))
|
11
|
-
end
|
12
|
-
|
13
|
-
def cask_method?(method_node)
|
14
|
-
CASK_METHOD_NAMES.include?(method_name(method_node))
|
15
|
-
end
|
16
|
-
|
17
|
-
def cask_children(node)
|
18
|
-
node_children(node).select { |e| cask?(e) }
|
19
|
-
end
|
20
|
-
|
21
5
|
def on_block(block_node)
|
22
6
|
super if defined? super
|
23
7
|
return unless respond_to?(:on_cask)
|
24
|
-
return unless
|
25
|
-
|
26
|
-
method_node, _args, block_body = *block_node
|
8
|
+
return unless block_node.cask_block?
|
27
9
|
|
28
|
-
on_cask(block_node
|
10
|
+
on_cask(block_node)
|
29
11
|
end
|
30
12
|
end
|
31
13
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cask
|
3
|
+
# Constants available globally for use in all Cask cops.
|
4
|
+
module Constants
|
5
|
+
CASK_METHOD_NAMES = [:cask, :test_cask].freeze
|
6
|
+
|
7
|
+
STANZA_GROUPS = [
|
8
|
+
[:version, :sha256],
|
9
|
+
[:url, :appcast, :name, :homepage, :license, :gpg],
|
10
|
+
[
|
11
|
+
:auto_updates,
|
12
|
+
:accessibility_access,
|
13
|
+
:conflicts_with,
|
14
|
+
:depends_on,
|
15
|
+
:container
|
16
|
+
],
|
17
|
+
[
|
18
|
+
:suite,
|
19
|
+
:app,
|
20
|
+
:pkg,
|
21
|
+
:installer,
|
22
|
+
:binary,
|
23
|
+
:colorpicker,
|
24
|
+
:font,
|
25
|
+
:input_method,
|
26
|
+
:internet_plugin,
|
27
|
+
:prefpane,
|
28
|
+
:qlplugin,
|
29
|
+
:screen_saver,
|
30
|
+
:service,
|
31
|
+
:audio_unit_plugin,
|
32
|
+
:vst_plugin,
|
33
|
+
:artifact,
|
34
|
+
:stage_only
|
35
|
+
],
|
36
|
+
[:preflight],
|
37
|
+
[:postflight],
|
38
|
+
[:uninstall_preflight],
|
39
|
+
[:uninstall_postflight],
|
40
|
+
[:uninstall],
|
41
|
+
[:zap],
|
42
|
+
[:caveats]
|
43
|
+
].freeze
|
44
|
+
|
45
|
+
STANZA_GROUP_HASH =
|
46
|
+
STANZA_GROUPS.each_with_object({}) do |stanza_group, hash|
|
47
|
+
stanza_group.each { |stanza| hash[stanza] = stanza_group }
|
48
|
+
hash
|
49
|
+
end.freeze
|
50
|
+
|
51
|
+
STANZA_ORDER = STANZA_GROUPS.flatten.freeze
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Astrolabe
|
2
|
+
# Extensions for Astrolabe's AST Node class
|
3
|
+
class Node
|
4
|
+
include RuboCop::Cask::Constants
|
5
|
+
|
6
|
+
def_matcher :method_node, '{$(send ...) (block $(send ...) ...)}'
|
7
|
+
def_matcher :block_args, '(block _ $_ _)'
|
8
|
+
def_matcher :block_body, '(block _ _ $_)'
|
9
|
+
|
10
|
+
def_matcher :key_node, '{(pair $_ _) (hash (pair $_ _) ...)}'
|
11
|
+
def_matcher :val_node, '{(pair _ $_) (hash (pair _ $_) ...)}'
|
12
|
+
|
13
|
+
def cask_block?
|
14
|
+
block_type? && method_node.cask_header?
|
15
|
+
end
|
16
|
+
|
17
|
+
def cask_header?
|
18
|
+
send_type? && CASK_METHOD_NAMES.include?(method_name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def heredoc?
|
22
|
+
loc.is_a?(Parser::Source::Map::Heredoc)
|
23
|
+
end
|
24
|
+
|
25
|
+
def expression
|
26
|
+
base_expression = loc.expression
|
27
|
+
descendants.select(&:heredoc?).reduce(base_expression) do |expr, node|
|
28
|
+
expr.join(node.loc.heredoc_end)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/rubocop/cask/version.rb
CHANGED
@@ -21,8 +21,8 @@ module RuboCop
|
|
21
21
|
|
22
22
|
MESSAGE = 'Use `%s` instead of `%s`'
|
23
23
|
|
24
|
-
def on_cask(
|
25
|
-
@cask_header = cask_header(method_node)
|
24
|
+
def on_cask(cask_node)
|
25
|
+
@cask_header = cask_header(cask_node.method_node)
|
26
26
|
return unless offense?
|
27
27
|
offense
|
28
28
|
end
|
@@ -40,7 +40,7 @@ module RuboCop
|
|
40
40
|
def_delegators :@cask_header, :header_str, :preferred_header_str
|
41
41
|
|
42
42
|
def cask_header(method_node)
|
43
|
-
RuboCop::Cask::CaskHeader.new(method_node)
|
43
|
+
RuboCop::Cask::AST::CaskHeader.new(method_node)
|
44
44
|
end
|
45
45
|
|
46
46
|
def offense?
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
module RuboCop
|
5
|
+
module Cop
|
6
|
+
module Cask
|
7
|
+
# This cop checks that a cask's stanzas are grouped correctly.
|
8
|
+
# See https://github.com/caskroom/homebrew-cask/blob/master/CONTRIBUTING.md#stanza-order
|
9
|
+
# for more info.
|
10
|
+
class StanzaGrouping < Cop
|
11
|
+
extend Forwardable
|
12
|
+
include RuboCop::Cask::CaskHelp
|
13
|
+
|
14
|
+
MISSING_LINE_MSG = 'stanza groups should be separated by a single ' \
|
15
|
+
'empty line'
|
16
|
+
|
17
|
+
EXTRA_LINE_MSG = 'stanzas within the same group should have no lines ' \
|
18
|
+
'between them'
|
19
|
+
|
20
|
+
def on_cask(cask_node)
|
21
|
+
@cask_block = RuboCop::Cask::AST::CaskBlock.new(cask_node)
|
22
|
+
@line_ops = {}
|
23
|
+
add_offenses
|
24
|
+
end
|
25
|
+
|
26
|
+
def autocorrect(range)
|
27
|
+
lambda do |corrector|
|
28
|
+
case line_ops[range.line - 1]
|
29
|
+
when :insert
|
30
|
+
corrector.insert_before(range, "\n")
|
31
|
+
when :remove
|
32
|
+
corrector.remove(range)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :cask_block, :line_ops
|
40
|
+
def_delegators :cask_block, :cask_node, :toplevel_stanzas
|
41
|
+
|
42
|
+
def add_offenses
|
43
|
+
toplevel_stanzas.each_cons(2) do |stanza, next_stanza|
|
44
|
+
next unless next_stanza
|
45
|
+
if missing_line_after?(stanza, next_stanza)
|
46
|
+
add_offense_missing_line(stanza)
|
47
|
+
elsif extra_line_after?(stanza, next_stanza)
|
48
|
+
add_offense_extra_line(stanza)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def missing_line_after?(stanza, next_stanza)
|
54
|
+
!(stanza.same_group?(next_stanza) ||
|
55
|
+
empty_line_after?(stanza))
|
56
|
+
end
|
57
|
+
|
58
|
+
def extra_line_after?(stanza, next_stanza)
|
59
|
+
stanza.same_group?(next_stanza) &&
|
60
|
+
empty_line_after?(stanza)
|
61
|
+
end
|
62
|
+
|
63
|
+
def empty_line_after?(stanza)
|
64
|
+
source_line_after(stanza).empty?
|
65
|
+
end
|
66
|
+
|
67
|
+
def source_line_after(stanza)
|
68
|
+
processed_source[index_of_line_after(stanza)]
|
69
|
+
end
|
70
|
+
|
71
|
+
def index_of_line_after(stanza)
|
72
|
+
stanza.expression.last_line
|
73
|
+
end
|
74
|
+
|
75
|
+
def add_offense_missing_line(stanza)
|
76
|
+
line_index = index_of_line_after(stanza)
|
77
|
+
line_ops[line_index] = :insert
|
78
|
+
add_offense(line_index, MISSING_LINE_MSG)
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_offense_extra_line(stanza)
|
82
|
+
line_index = index_of_line_after(stanza)
|
83
|
+
line_ops[line_index] = :remove
|
84
|
+
add_offense(line_index, EXTRA_LINE_MSG)
|
85
|
+
end
|
86
|
+
|
87
|
+
def add_offense(line_index, msg)
|
88
|
+
line_length = [processed_source[line_index].size, 1].max
|
89
|
+
range = source_range(processed_source.buffer, line_index + 1, 0,
|
90
|
+
line_length)
|
91
|
+
super(range, range, msg)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|