graphql-dsl 1.0.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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +23 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +44 -0
  6. data/.ruby-version +1 -0
  7. data/.yardopts +1 -0
  8. data/CODE_OF_CONDUCT.md +84 -0
  9. data/Gemfile +6 -0
  10. data/Gemfile.lock +64 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +899 -0
  13. data/Rakefile +6 -0
  14. data/bin/console +15 -0
  15. data/bin/setup +7 -0
  16. data/graphql-dsl.gemspec +38 -0
  17. data/lib/graphql/dsl/constants.rb +9 -0
  18. data/lib/graphql/dsl/error.rb +32 -0
  19. data/lib/graphql/dsl/formatter/arguments.rb +26 -0
  20. data/lib/graphql/dsl/formatter/directives.rb +52 -0
  21. data/lib/graphql/dsl/formatter/executable_document.rb +22 -0
  22. data/lib/graphql/dsl/formatter/field.rb +49 -0
  23. data/lib/graphql/dsl/formatter/formatter.rb +41 -0
  24. data/lib/graphql/dsl/formatter/fragment_operation.rb +41 -0
  25. data/lib/graphql/dsl/formatter/fragment_spread.rb +25 -0
  26. data/lib/graphql/dsl/formatter/inline_fragment.rb +43 -0
  27. data/lib/graphql/dsl/formatter/operation.rb +60 -0
  28. data/lib/graphql/dsl/formatter/values.rb +146 -0
  29. data/lib/graphql/dsl/formatter/variable_definitions.rb +43 -0
  30. data/lib/graphql/dsl/nodes/containers/directive.rb +46 -0
  31. data/lib/graphql/dsl/nodes/containers/variable_definition.rb +52 -0
  32. data/lib/graphql/dsl/nodes/executable_document.rb +69 -0
  33. data/lib/graphql/dsl/nodes/field.rb +39 -0
  34. data/lib/graphql/dsl/nodes/fragment_operation.rb +36 -0
  35. data/lib/graphql/dsl/nodes/fragment_spread.rb +24 -0
  36. data/lib/graphql/dsl/nodes/inline_fragment.rb +34 -0
  37. data/lib/graphql/dsl/nodes/mixins/selection_set.rb +106 -0
  38. data/lib/graphql/dsl/nodes/node.rb +39 -0
  39. data/lib/graphql/dsl/nodes/operation.rb +61 -0
  40. data/lib/graphql/dsl/version.rb +9 -0
  41. data/lib/graphql/dsl.rb +230 -0
  42. data/lib/graphql-dsl.rb +3 -0
  43. data/lib/graphql_dsl.rb +36 -0
  44. data/tasks/readme/update.rake +143 -0
  45. metadata +173 -0
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql_dsl'
4
+ require 'bundler/gem_tasks'
5
+
6
+ Rake.add_rakelib 'tasks/readme'
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 'graphql_dsl'
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,7 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle config set --local path 'vendor/bundle'
7
+ bundle install
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/graphql/dsl/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'graphql-dsl'
7
+ spec.version = GraphQL::DSL::VERSION
8
+ spec.authors = ['Maxim Dobryakov']
9
+ spec.email = ['maxim.dobryakov@gmail.com']
10
+
11
+ spec.summary = 'GraphQL DSL'
12
+ spec.description = 'Ruby DSL for GraphQL'
13
+ spec.homepage = 'https://github.com/maxd/graphql-dsl'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.6.0')
16
+
17
+ # spec.metadata['allowed_push_host'] = "Set to 'http://mygemserver.com'"
18
+
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = 'https://github.com/maxd/graphql-dsl'
21
+ spec.metadata['changelog_uri'] = 'https://github.com/maxd/graphql-dsl/releases'
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
27
+ end
28
+ spec.bindir = 'exe'
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ['lib']
31
+
32
+ spec.add_development_dependency 'rake', '~> 13.0'
33
+ spec.add_development_dependency 'rspec', '~> 3.10'
34
+ spec.add_development_dependency 'rubocop', '~> 1.18'
35
+ spec.add_development_dependency 'rubocop-rake', '~> 0.6'
36
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.4'
37
+ spec.add_development_dependency 'yard', '~> 0.9'
38
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # Help to mark default parameter as undefined
7
+ UNDEFINED = Object.new
8
+ end
9
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # GraphQL DSL error
7
+ class Error < StandardError
8
+ ##
9
+ # @return [Hash] additional error arguments
10
+ attr_reader :arguments
11
+
12
+ ##
13
+ # Create GraphQL error
14
+ #
15
+ # @param msg [String] error message
16
+ # @param arguments [Hash] additional error arguments
17
+ def initialize(msg = nil, **arguments)
18
+ super(msg)
19
+
20
+ @arguments = arguments
21
+ end
22
+
23
+ ##
24
+ # Error message
25
+ #
26
+ # @return [String] error message
27
+ def message
28
+ super + (arguments ? "\nArguments: #{arguments}" : '')
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ class Formatter # rubocop:disable Style/Documentation
6
+ private
7
+
8
+ ##
9
+ # Format arguments as string
10
+ #
11
+ # @param arguments [Hash] arguments
12
+ # @param is_const [Boolean] allow to use variables or not
13
+ #
14
+ # @return [String] representation of arguments as string
15
+ def format_arguments(arguments, is_const)
16
+ return nil if arguments.empty?
17
+
18
+ result = arguments.map do |name, value|
19
+ "#{name}: #{format_value(value, is_const)}"
20
+ end
21
+
22
+ "(#{result.join(', ')})"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ class Formatter # rubocop:disable Style/Documentation
6
+ private
7
+
8
+ ##
9
+ # Format directives to string
10
+ #
11
+ # @param directives [Array] directives
12
+ # @param is_const [Boolean] allow to use variables or not
13
+ #
14
+ # @return [String] representation of directives as string
15
+ def format_directives(directives, is_const)
16
+ return nil if directives.empty?
17
+
18
+ result = directives.map do |directive|
19
+ format_directive(directive, is_const)
20
+ end
21
+
22
+ result.join(' ')
23
+ end
24
+
25
+ ##
26
+ # Format directive to string
27
+ #
28
+ # @param directive [] directive
29
+ # @param is_const [Boolean] allow to use variables or not
30
+ #
31
+ # @return [String] representation of directive as string
32
+ def format_directive(directive, is_const)
33
+ result = []
34
+
35
+ result << format_directive_name(directive.name)
36
+ result << format_arguments(directive.arguments, is_const) unless directive.arguments.empty?
37
+
38
+ result.compact.join
39
+ end
40
+
41
+ ##
42
+ # Format directive name to string
43
+ #
44
+ # @param name [String] directive name
45
+ #
46
+ # @return [String] representation of directive name as string
47
+ def format_directive_name(name)
48
+ name.start_with?('@') ? name.to_s : "@#{name}"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ class Formatter # rubocop:disable Style/Documentation
6
+ private
7
+
8
+ ##
9
+ # Format executable document as string
10
+ #
11
+ # @param executable_document [ExecutableDocument] executable document node
12
+ # @param level [Integer] indent level
13
+ #
14
+ # @return [String] representation of executable document as string
15
+ def format_executable_document(executable_document, level)
16
+ result = []
17
+ result += executable_document.__nodes.map { |node| format_node(node, level) }
18
+ result.join("\n\n")
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ class Formatter # rubocop:disable Style/Documentation
6
+ private
7
+
8
+ ##
9
+ # Format field as string
10
+ #
11
+ # @param field [Field] field node
12
+ # @param level [Integer] indent level
13
+ #
14
+ # @return [String] representation of field as string
15
+ def format_field(field, level)
16
+ result = []
17
+ result << indent(level) + format_field_signature(field)
18
+
19
+ unless field.__nodes.empty?
20
+ result << "#{indent(level)}{"
21
+ result += field.__nodes.map { |node| format_node(node, level + 1) }
22
+ result << "#{indent(level)}}"
23
+ end
24
+
25
+ result.join("\n")
26
+ end
27
+
28
+ ##
29
+ # Format field signature as string
30
+ #
31
+ # @param field [Field] field node
32
+ #
33
+ # @return [String] representation of field signature as string
34
+ def format_field_signature(field)
35
+ field_alias = field.__alias ? "#{field.__alias}: " : ''
36
+ field_name = field.__name.to_s
37
+ field_arguments = field.__arguments.empty? ? '' : format_arguments(field.__arguments, false)
38
+ field_directives = field.__directives.empty? ? '' : " #{format_directives(field.__directives, false)}"
39
+
40
+ [
41
+ field_alias,
42
+ field_name,
43
+ field_arguments,
44
+ field_directives,
45
+ ].join
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # Format nodes to GraphQL
7
+ class Formatter
8
+ ##
9
+ # Format node to GraphQL
10
+ #
11
+ # @param node [Node] node
12
+ # @param level [Integer] indent level
13
+ #
14
+ # @return [String] representation of node as GraphQL string
15
+ def format_node(node, level)
16
+ case node
17
+ when ExecutableDocument then format_executable_document(node, level)
18
+ when Operation then format_operation(node, level)
19
+ when FragmentOperation then format_fragment_operation(node, level)
20
+ when Field then format_field(node, level)
21
+ when FragmentSpread then format_fragment_spread(node, level)
22
+ when InlineFragment then format_inline_fragment(node, level)
23
+ else
24
+ raise Error.new('Unknown node', class: node.class.name, node: node)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ ##
31
+ # Generate indent for formatting
32
+ #
33
+ # @param level [Integer] indent level
34
+ #
35
+ # @return [String] string for indent
36
+ def indent(level)
37
+ ' ' * level
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ class Formatter # rubocop:disable Style/Documentation
6
+ private
7
+
8
+ ##
9
+ # Format fragment operation as string
10
+ #
11
+ # @param fragment_operation [FragmentOperation] fragment operation node
12
+ # @param level [Integer] indent level
13
+ #
14
+ # @return [String] representation of fragment operation as string
15
+ def format_fragment_operation(fragment_operation, level)
16
+ result = []
17
+
18
+ result << indent(level) + format_fragment_operation_signature(fragment_operation)
19
+ result << "#{indent(level)}{"
20
+ result += fragment_operation.__nodes.map { |node| format_node(node, level + 1) }
21
+ result << "#{indent(level)}}"
22
+
23
+ result.join("\n")
24
+ end
25
+
26
+ ##
27
+ # Format fragment operation signature as string
28
+ #
29
+ # @param fragment_operator [FragmentOperation] fragment operation node
30
+ #
31
+ # @return [String] representation of fragment operation signature as string
32
+ def format_fragment_operation_signature(fragment_operator)
33
+ [
34
+ "fragment #{fragment_operator.__name}",
35
+ "on #{fragment_operator.__type}",
36
+ format_directives(fragment_operator.__directives, false),
37
+ ].compact.join(' ')
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ class Formatter # rubocop:disable Style/Documentation
6
+ private
7
+
8
+ ##
9
+ # Format fragment spread as string
10
+ #
11
+ # @param fragment_spread [FragmentSpread] fragment spread node
12
+ # @param level [Integer] indent level
13
+ #
14
+ # @return [String] representation of fragment spread as string
15
+ def format_fragment_spread(fragment_spread, level)
16
+ fragment_spread_signature = [
17
+ fragment_spread.__name,
18
+ format_directives(fragment_spread.__directives, false),
19
+ ].compact.join(' ')
20
+
21
+ indent(level) + "...#{fragment_spread_signature}"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ class Formatter # rubocop:disable Style/Documentation
6
+ private
7
+
8
+ ##
9
+ # Format inline fragment as string
10
+ #
11
+ # @param inline_fragment [InlineFragment] inline fragment node
12
+ # @param level [Integer] indent level
13
+ #
14
+ # @return [String] representation of inline fragment as string
15
+ def format_inline_fragment(inline_fragment, level)
16
+ result = []
17
+ result << indent(level) + format_inline_fragment_signature(inline_fragment)
18
+
19
+ unless inline_fragment.__nodes.empty?
20
+ result << "#{indent(level)}{"
21
+ result += inline_fragment.__nodes.map { |node| format_node(node, level + 1) }
22
+ result << "#{indent(level)}}"
23
+ end
24
+
25
+ result.join("\n")
26
+ end
27
+
28
+ ##
29
+ # Format inline fragment signature as string
30
+ #
31
+ # @param inline_fragment [InlineFragment] inline fragment node
32
+ #
33
+ # @return [String] representation of inline fragment signature as string
34
+ def format_inline_fragment_signature(inline_fragment)
35
+ [
36
+ '...',
37
+ (inline_fragment.__type ? "on #{inline_fragment.__type}" : nil),
38
+ format_directives(inline_fragment.__directives, false),
39
+ ].compact.join(' ')
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ class Formatter # rubocop:disable Style/Documentation
6
+ private
7
+
8
+ ##
9
+ # Format operation as string
10
+ #
11
+ # @param operation [FragmentOperation] operation node
12
+ # @param level [Integer] indent level
13
+ #
14
+ # @return [String] representation of operation as string
15
+ def format_operation(operation, level)
16
+ result = []
17
+
18
+ operation_signature = format_operation_signature(operation)
19
+
20
+ result << indent(level) + operation_signature if operation_signature
21
+ result << "#{indent(level)}{"
22
+ result += operation.__nodes.map { |node| format_node(node, level + 1) }
23
+ result << "#{indent(level)}}"
24
+
25
+ result.join("\n")
26
+ end
27
+
28
+ ##
29
+ # Format operation signature
30
+ #
31
+ # @return [String, nil] representation of operation signature as string
32
+ def format_operation_signature(operation)
33
+ operation_name_and_variables = [
34
+ operation.__name,
35
+ format_variable_definitions(operation.__variable_definitions),
36
+ ].compact
37
+
38
+ operation_signature = [
39
+ format_operation_type(operation),
40
+ (operation_name_and_variables.join unless operation_name_and_variables.empty?),
41
+ format_directives(operation.__directives, false),
42
+ ].compact
43
+
44
+ operation_signature.empty? ? nil : operation_signature.join(' ')
45
+ end
46
+
47
+ ##
48
+ # Format operation type
49
+ #
50
+ # @return [String, nil] representation of operation type as string
51
+ def format_operation_type(operation)
52
+ return unless operation.__operation_type != :query ||
53
+ operation.__name ||
54
+ !operation.__variable_definitions.empty?
55
+
56
+ operation.__operation_type.to_s
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ class Formatter # rubocop:disable Style/Documentation
6
+ private
7
+
8
+ ##
9
+ # Format value to string
10
+ #
11
+ # @param value [] value
12
+ # @param is_const [Boolean] allow to use variables or not i.e. value is constant or not
13
+ #
14
+ # @return [String] representation of value as string
15
+ def format_value(value, is_const) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
16
+ case value
17
+ when Integer
18
+ format_value_to_integer(value)
19
+ when Float
20
+ format_value_to_float(value)
21
+ when String
22
+ format_value_to_string(value)
23
+ when TrueClass, FalseClass
24
+ format_value_to_boolean(value)
25
+ when NilClass
26
+ format_value_to_null(value)
27
+ when Symbol
28
+ if value.start_with?('$')
29
+ format_value_to_variable(value, is_const)
30
+ else
31
+ format_value_to_enum(value)
32
+ end
33
+ when Array
34
+ format_value_to_list(value, is_const)
35
+ when Hash
36
+ format_value_to_object(value, is_const)
37
+ else
38
+ raise Error.new('Unsupported value type', class: value.class.name, value: value)
39
+ end
40
+ end
41
+
42
+ ##
43
+ # Format value to integer value
44
+ #
45
+ # @param value [Integer] value
46
+ #
47
+ # @return [String] representation of value as integer value
48
+ def format_value_to_integer(value)
49
+ value.to_s
50
+ end
51
+
52
+ ##
53
+ # Format value to float value
54
+ #
55
+ # @param value [Float] value
56
+ #
57
+ # @return [String] representation of value as float value
58
+ def format_value_to_float(value)
59
+ value.to_s
60
+ end
61
+
62
+ ##
63
+ # Format value to string value
64
+ #
65
+ # @param value [String] value
66
+ #
67
+ # @return [String] representation of value as string value
68
+ def format_value_to_string(value)
69
+ value.dump
70
+ end
71
+
72
+ ##
73
+ # Format value to boolean value
74
+ #
75
+ # @param value [TrueClass, FalseClass] value
76
+ #
77
+ # @return [String] representation of value as boolean value
78
+ def format_value_to_boolean(value)
79
+ value.to_s
80
+ end
81
+
82
+ ##
83
+ # Format value to null value
84
+ #
85
+ # @param _value [NilClass] value
86
+ #
87
+ # @return [String] representation of value as null value
88
+ def format_value_to_null(_value)
89
+ 'null'
90
+ end
91
+
92
+ ##
93
+ # Format value to variable value
94
+ #
95
+ # @param value [Symbol] value
96
+ # @param is_const [Boolean] allow to use variables or not
97
+ #
98
+ # @return [String] representation of value as variable value
99
+ def format_value_to_variable(value, is_const)
100
+ raise Error.new('Value must be constant', value: value) if is_const
101
+
102
+ value.to_s
103
+ end
104
+
105
+ ##
106
+ # Format value to enum value
107
+ #
108
+ # @param value [Symbol] value
109
+ #
110
+ # @return [String] representation of value as enum value
111
+ def format_value_to_enum(value)
112
+ value.to_s
113
+ end
114
+
115
+ ##
116
+ # Format value to list value
117
+ #
118
+ # @param value [Array] value
119
+ # @param is_const [Boolean] allow to use variables or not
120
+ #
121
+ # @return [String] representation of value as list value
122
+ def format_value_to_list(value, is_const)
123
+ result = value.map do |element|
124
+ format_value(element, is_const)
125
+ end.join(', ')
126
+
127
+ "[#{result}]"
128
+ end
129
+
130
+ ##
131
+ # Format value to object value
132
+ #
133
+ # @param value [Hash] value
134
+ # @param is_const [Boolean] allow to use variables or not
135
+ #
136
+ # @return [String] representation of value as object value
137
+ def format_value_to_object(value, is_const)
138
+ result = value.map do |n, v|
139
+ "#{n}: #{format_value(v, is_const)}"
140
+ end.join(', ')
141
+
142
+ "{#{result}}"
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ class Formatter # rubocop:disable Style/Documentation
6
+ private
7
+
8
+ ##
9
+ # Format variable definitions to string
10
+ #
11
+ # @param variable_definitions [Hash] variable definitions
12
+ #
13
+ # @return [String] representation of variable definitions as string
14
+ def format_variable_definitions(variable_definitions)
15
+ return nil if variable_definitions.empty?
16
+
17
+ result = variable_definitions.map do |variable_name, variable_definition|
18
+ format_variable_definition(variable_name, variable_definition)
19
+ end
20
+
21
+ "(#{result.join(', ')})"
22
+ end
23
+
24
+ ##
25
+ # Format variable definition to string
26
+ #
27
+ # @param variable_name [Symbol, String] variable name
28
+ # @param variable_definition [] variable definition
29
+ #
30
+ # @return [String] representation of variable definition as string
31
+ def format_variable_definition(variable_name, variable_definition)
32
+ result = []
33
+
34
+ result << "$#{variable_name}:"
35
+ result << variable_definition.type
36
+ result << "= #{format_value(variable_definition.default, true)}" if variable_definition.default != UNDEFINED
37
+ result << format_directives(variable_definition.directives, true) unless variable_definition.directives.empty?
38
+
39
+ result.compact.join(' ')
40
+ end
41
+ end
42
+ end
43
+ end