rubocop-graphql 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 44b2a5447e5631dde2523566cca0f2e8e5f38a5d2a0e874c06e0febf9afa8661
4
+ data.tar.gz: db95fd1b174deb832d715a9e72d45864c9c3883e828d07768d7b2ad2b13ed606
5
+ SHA512:
6
+ metadata.gz: fc9381482049b6de23c6790f94a0828e49fe1f13a55922d8cbc1e565288ed3453f12b4fe6961620504b9bfaed6c9e996b3477725985e88dd32529317988b776c
7
+ data.tar.gz: bd246b919704fd783e58578793dba6f8f00e79de20fff9eb313ce5dd2b1c15bb6ad4b21e877a694b7d2f6f4016b2d768ed0975b293d6d405b3571682e798ba71
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 DmitryTsepelev
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.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # Rubocop::GraphQL
2
+
3
+ [Rubocop](https://github.com/rubocop-hq/rubocop) extension for enforcing [graphql-ruby](https://github.com/rmosolgo/graphql-ruby) best practices.
4
+
5
+ <p align="center">
6
+ <a href="https://evilmartians.com/?utm_source=graphql-rubocop">
7
+ <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54">
8
+ </a>
9
+ </p>
10
+
11
+ ## Installation
12
+
13
+ Install the gem:
14
+
15
+ ```bash
16
+ gem install rubocop-graphql
17
+ ```
18
+
19
+ If you use bundler put this in your Gemfile:
20
+
21
+ ```ruby
22
+ gem 'rubocop-graphql', require: false
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ You need to tell RuboCop to load the Rails extension. There are three ways to do this:
28
+
29
+ ### RuboCop configuration file
30
+
31
+ Put this into your `.rubocop.yml`.
32
+
33
+ ```yaml
34
+ require: rubocop-graphql
35
+ ```
36
+
37
+ Alternatively, use the following array notation when specifying multiple extensions.
38
+
39
+ ```yaml
40
+ require:
41
+ - rubocop-other-extension
42
+ - rubocop-graphql
43
+ ```
44
+
45
+ Now you can run `rubocop` and it will automatically load the RuboCop Rails cops together with the standard cops.
46
+
47
+ ### Command line
48
+
49
+ ```sh
50
+ rubocop --require rubocop-graphql
51
+ ```
52
+
53
+ ### Rake task
54
+
55
+ ```ruby
56
+ RuboCop::RakeTask.new do |task|
57
+ task.requires << 'rubocop-graphql'
58
+ end
59
+ ```
60
+
61
+ ## The Cops
62
+
63
+ All cops are located under [`lib/rubocop/cop/graphql`](lib/rubocop/cop/graphql), and contain examples and documentation.
64
+
65
+ In your `.rubocop.yml`, you may treat the Rails cops just like any other cop. For example:
66
+
67
+ ```yaml
68
+ GraphQL/ResolverMethodLength:
69
+ Max: 3
70
+ ```
71
+
72
+ ## Contributing
73
+
74
+ Bug reports and pull requests are welcome on GitHub at https://github.com/DmitryTsepelev/rubocop-graphql.
75
+
76
+ ## License
77
+
78
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,26 @@
1
+ AllCops:
2
+ GraphQL:
3
+ Patterns:
4
+ - "(?:^|/)graphql/"
5
+
6
+ GraphQL/ResolverMethodLength:
7
+ Enabled: true
8
+ VersionAdded: '0.80'
9
+ Description: 'Avoid resolver methods longer than 10 lines of code.'
10
+ Max: 10
11
+ CountComments: false
12
+ ExcludedMethods: []
13
+
14
+ GraphQL/FieldDefinitions:
15
+ Enabled: true
16
+ VersionAdded: '0.80'
17
+ Description: 'Checks consistency of field definitions'
18
+ EnforcedStyle: group_definitions
19
+ SupportedStyles:
20
+ - group_definitions
21
+ - define_resolver_after_definition
22
+
23
+ GraphQL/FieldDescription:
24
+ Enabled: true
25
+ VersionAdded: '0.80'
26
+ Description: 'Missing field description'
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module GraphQL
6
+ # This cop checks consistency of field definitions
7
+ #
8
+ # EnforcedStyle supports two modes:
9
+ #
10
+ # `group_definitions` : all field definitions should be grouped together.
11
+ #
12
+ # `define_resolver_after_definition` : if resolver method exists it should
13
+ # be defined right after the field definition.
14
+ #
15
+ # @example EnforcedStyle: group_definitions (default)
16
+ # # good
17
+ #
18
+ # class UserType < BaseType
19
+ # field :first_name, String, null: true
20
+ # field :last_name, String, null: true
21
+ #
22
+ # def first_name
23
+ # object.contact_data.first_name
24
+ # end
25
+ #
26
+ # def last_name
27
+ # object.contact_data.last_name
28
+ # end
29
+ # end
30
+ #
31
+ # @example EnforcedStyle: define_resolver_after_definition
32
+ # # good
33
+ #
34
+ # class UserType < BaseType
35
+ # field :first_name, String, null: true
36
+ #
37
+ # def first_name
38
+ # object.contact_data.first_name
39
+ # end
40
+ #
41
+ # field :last_name, String, null: true
42
+ #
43
+ # def last_name
44
+ # object.contact_data.last_name
45
+ # end
46
+ # end
47
+ class FieldDefinitions < Cop
48
+ include ConfigurableEnforcedStyle
49
+ include RuboCop::GraphQL::NodePattern
50
+
51
+ def_node_matcher :field_kwargs, <<~PATTERN
52
+ (send nil? :field
53
+ ...
54
+ (hash
55
+ $...
56
+ )
57
+ )
58
+ PATTERN
59
+
60
+ def_node_matcher :resolver_method_option, <<~PATTERN
61
+ (pair (sym :resolver_method) (sym $...))
62
+ PATTERN
63
+
64
+ def on_send(node)
65
+ return unless field_definition?(node)
66
+
67
+ case style
68
+ when :group_definitions
69
+ check_grouped_field_declarations(node.parent)
70
+ when :define_resolver_after_definition
71
+ field = RuboCop::GraphQL::Field.new(node)
72
+ check_resolver_is_defined_after_definition(field)
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ GROUP_DEFS_MSG = "Group all field definitions together."
79
+
80
+ def check_grouped_field_declarations(node)
81
+ fields = node.each_child_node.select { |node| field_definition?(node) }
82
+
83
+ first_field = fields.first
84
+ fields.each_with_index do |node, idx|
85
+ next if node.sibling_index == first_field.sibling_index + idx
86
+
87
+ add_offense(node, message: GROUP_DEFS_MSG)
88
+ end
89
+ end
90
+
91
+ RESOLVER_AFTER_FIELD_MSG = "Define resolver method after field definition."
92
+
93
+ def check_resolver_is_defined_after_definition(field)
94
+ return if field.resolver || field.method || field.hash_key
95
+
96
+ resolver_method = field.kwargs.flat_map { |kwarg| resolver_method_option(kwarg) }.compact.first
97
+
98
+ method_name = resolver_method || field.name
99
+ method_definition = field.parent.each_child_node.find { |node|
100
+ node.def_type? && node.method_name == method_name
101
+ }
102
+
103
+ if method_definition.sibling_index - field.sibling_index > 1
104
+ add_offense(field.node, message: RESOLVER_AFTER_FIELD_MSG)
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module GraphQL
6
+ # This cop checks if each field has a description.
7
+ #
8
+ # @example
9
+ # # good
10
+ #
11
+ # class UserType < BaseType
12
+ # field :name, String, "Name of the user", null: true
13
+ # end
14
+ #
15
+ # # bad
16
+ #
17
+ # class UserType < BaseType
18
+ # field :name, String, null: true
19
+ # end
20
+ #
21
+ class FieldDescription < Cop
22
+ include RuboCop::GraphQL::NodePattern
23
+
24
+ MSG = "Missing field description"
25
+
26
+ def on_send(node)
27
+ return unless field_definition?(node)
28
+
29
+ field = RuboCop::GraphQL::Field.new(node)
30
+
31
+ add_offense(node) unless field.description
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module GraphQL
6
+ # This cop checks if the length of a resolver method exceeds some maximum value.
7
+ # Comment lines can optionally be ignored.
8
+ #
9
+ # The maximum allowed length is configurable using the Max option.
10
+ class ResolverMethodLength < Cop
11
+ include RuboCop::Cop::ConfigurableMax
12
+ include RuboCop::Cop::TooManyLines
13
+
14
+ LABEL = "ResolverMethod"
15
+
16
+ def_node_matcher :field_definition, <<~PATTERN
17
+ (send nil? :field (sym $...) ...)
18
+ PATTERN
19
+
20
+ def on_def(node)
21
+ excluded_methods = cop_config["ExcludedMethods"]
22
+ return if excluded_methods.include?(String(node.method_name))
23
+
24
+ check_code_length(node) if field_is_defined?(node)
25
+ end
26
+ alias on_defs on_def
27
+
28
+ private
29
+
30
+ def field_is_defined?(node)
31
+ node.parent.children.flat_map { |child| field_definition(child) }.include?(node.method_name)
32
+ end
33
+
34
+ def cop_label
35
+ LABEL
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "graphql/field_definitions"
4
+ require_relative "graphql/field_description"
5
+ require_relative "graphql/resolver_method_length"
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module GraphQL
5
+ class Field
6
+ extend Forwardable
7
+ extend RuboCop::NodePattern::Macros
8
+
9
+ def_delegators :@node, :sibling_index, :parent
10
+
11
+ def_node_matcher :field_kwargs, <<~PATTERN
12
+ (send nil? :field
13
+ ...
14
+ (hash
15
+ $...
16
+ )
17
+ )
18
+ PATTERN
19
+
20
+ def_node_matcher :field_name, <<~PATTERN
21
+ (send nil? :field (:sym $...) ...)
22
+ PATTERN
23
+
24
+ def_node_matcher :field_description, <<~PATTERN
25
+ (send nil? :field _ _ (:str $...) ...)
26
+ PATTERN
27
+
28
+ def_node_matcher :resolver_kwarg?, <<~PATTERN
29
+ (pair (sym :resolver) ...)
30
+ PATTERN
31
+
32
+ def_node_matcher :method_kwarg?, <<~PATTERN
33
+ (pair (sym :method) ...)
34
+ PATTERN
35
+
36
+ def_node_matcher :hash_key_kwarg?, <<~PATTERN
37
+ (pair (sym :hash_key) ...)
38
+ PATTERN
39
+
40
+ attr_reader :node
41
+
42
+ def initialize(node)
43
+ @node = node
44
+ end
45
+
46
+ def name
47
+ @name ||= field_name(@node).first
48
+ end
49
+
50
+ def description
51
+ @name ||= field_description(@node)
52
+ end
53
+
54
+ def kwargs
55
+ @kwargs ||= field_kwargs(@node) || []
56
+ end
57
+
58
+ def resolver
59
+ kwargs.find { |kwarg| resolver_kwarg?(kwarg) }
60
+ end
61
+
62
+ def method
63
+ kwargs.find { |kwarg| method_kwarg?(kwarg) }
64
+ end
65
+
66
+ def hash_key
67
+ kwargs.find { |kwarg| hash_key_kwarg?(kwarg) }
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module GraphQL
5
+ # Because RuboCop doesn't yet support plugins, we have to monkey patch in a
6
+ # bit of our configuration.
7
+ module Inject
8
+ def self.defaults!
9
+ path = CONFIG_DEFAULT.to_s
10
+ hash = ConfigLoader.send(:load_yaml_configuration, path)
11
+ config = Config.new(hash, path).tap(&:make_excludes_absolute)
12
+ puts "configuration from \#{path}" if ConfigLoader.debug?
13
+ config = ConfigLoader.merge_with_default(config, path)
14
+ ConfigLoader.instance_variable_set(:@default_configuration, config)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module GraphQL
5
+ module NodePattern
6
+ extend RuboCop::NodePattern::Macros
7
+
8
+ def_node_matcher :field_definition?, <<~PATTERN
9
+ (send nil? :field ...)
10
+ PATTERN
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module RuboCop
2
+ module GraphQL
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # RuboCop GraphQL project namespace
5
+ module GraphQL
6
+ PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
7
+ CONFIG_DEFAULT = PROJECT_ROOT.join("config", "default.yml").freeze
8
+ CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze
9
+
10
+ private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubocop"
4
+
5
+ require_relative "rubocop/graphql"
6
+ require_relative "rubocop/graphql/version"
7
+ require_relative "rubocop/graphql/inject"
8
+ require_relative "rubocop/graphql/node_pattern"
9
+ require_relative "rubocop/graphql/field"
10
+
11
+ RuboCop::GraphQL::Inject.defaults!
12
+
13
+ require_relative "rubocop/cop/graphql_cops"
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubocop-graphql
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dmitry Tsepelev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: standard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.2.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.2.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 0.71.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.71.0
69
+ description: A collection of RuboCop cops to improve GraphQL-related code
70
+ email:
71
+ - dmitry.a.tsepelev@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - LICENSE.txt
77
+ - README.md
78
+ - config/default.yml
79
+ - lib/rubocop-graphql.rb
80
+ - lib/rubocop/cop/graphql/field_definitions.rb
81
+ - lib/rubocop/cop/graphql/field_description.rb
82
+ - lib/rubocop/cop/graphql/resolver_method_length.rb
83
+ - lib/rubocop/cop/graphql_cops.rb
84
+ - lib/rubocop/graphql.rb
85
+ - lib/rubocop/graphql/field.rb
86
+ - lib/rubocop/graphql/inject.rb
87
+ - lib/rubocop/graphql/node_pattern.rb
88
+ - lib/rubocop/graphql/version.rb
89
+ homepage: https://github.com/DmitryTsepelev/rubocop-graphql
90
+ licenses:
91
+ - MIT
92
+ metadata:
93
+ homepage_uri: https://github.com/DmitryTsepelev/rubocop-graphql
94
+ source_code_uri: https://github.com/DmitryTsepelev/rubocop-graphql
95
+ changelog_uri: https://github.com/DmitryTsepelev/rubocop-graphql/CHANGELOG.md
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubygems_version: 3.0.3
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Automatic performance checking tool for Ruby code.
115
+ test_files: []