graphql-dsl 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # Container for directive
7
+ class Directive
8
+ ##
9
+ # @return [String, Symbol, nil] directive name
10
+ attr_reader :name
11
+
12
+ ##
13
+ # @return [Hash] arguments
14
+ attr_reader :arguments
15
+
16
+ ##
17
+ # Create directive container
18
+ #
19
+ # @param name [String, Symbol] directive name
20
+ # @param arguments [Hash] arguments
21
+ def initialize(name, arguments = {})
22
+ raise Error, 'Variable name must be specified' if name.nil? || name.empty?
23
+
24
+ @name = name
25
+ @arguments = arguments
26
+ end
27
+
28
+ class << self
29
+ ##
30
+ # Create directive container from argument value
31
+ #
32
+ # @param value [] argument value
33
+ #
34
+ # @return [Directive] directive container
35
+ def from(value)
36
+ case value
37
+ when Directive then value
38
+ when Symbol, String then new(value)
39
+ else
40
+ raise Error.new('Unsupported format of directive', class: value.class.name, value: value)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # Container for variable definition
7
+ class VariableDefinition
8
+ ##
9
+ # @return [String, Symbol, nil] variable type
10
+ attr_reader :type
11
+
12
+ ##
13
+ # @return [Object, nil] default value of variable
14
+ attr_reader :default
15
+
16
+ ##
17
+ # @return [Array<Directive>] list of directives
18
+ attr_reader :directives
19
+
20
+ ##
21
+ # Create variable definition container
22
+ #
23
+ # @param type [String, Symbol] variable type
24
+ # @param default [Object, nil] default value
25
+ # @param directives [Array<Directive, Hash, Array>] list of directives
26
+ def initialize(type, default = UNDEFINED, directives = [])
27
+ raise Error, 'Variable type must be specified' if type.nil? || type.empty?
28
+
29
+ @type = type
30
+ @default = default
31
+ @directives = directives.map { |directive| Directive.from(directive) }
32
+ end
33
+
34
+ class << self
35
+ ##
36
+ # Create variable definition container from argument value
37
+ #
38
+ # @param value [] argument value
39
+ #
40
+ # @return [VariableDefinition] variable definition container
41
+ def from(value)
42
+ case value
43
+ when VariableDefinition then value
44
+ when Symbol, String then new(value)
45
+ else
46
+ raise Error.new('Unsupported format of variable definition', class: value.class.name, value: value)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # Executable document GraphQL node
7
+ class ExecutableDocument < Node
8
+ ##
9
+ # Create executable document
10
+ #
11
+ # @param block [Proc] declare DSL for operations
12
+ def initialize(&block)
13
+ super(nil, &block)
14
+ end
15
+
16
+ ##
17
+ # Create GraphQL query operation
18
+ #
19
+ # @param name [String, Symbol, nil] query name
20
+ # @param variable_definitions [Hash] variable definitions
21
+ # @param directives [Array<Directive, Hash, Array>] list of directives
22
+ # @param block [Proc] declare DSL for sub-fields
23
+ #
24
+ # @return [void]
25
+ def query(name = nil, variable_definitions = {}, directives = [], &block)
26
+ @__nodes << Operation.new(:query, name, variable_definitions, directives, &block)
27
+ end
28
+
29
+ ##
30
+ # Create GraphQL mutation operation
31
+ #
32
+ # @param name [String, Symbol, nil] mutation name
33
+ # @param variable_definitions [Hash] variable definitions
34
+ # @param directives [Array<Directive, Hash, Array>] list of directives
35
+ # @param block [Proc] declare DSL for sub-fields
36
+ #
37
+ # @return [void]
38
+ def mutation(name = nil, variable_definitions = {}, directives = [], &block)
39
+ @__nodes << Operation.new(:mutation, name, variable_definitions, directives, &block)
40
+ end
41
+
42
+ ##
43
+ # Create GraphQL subscription operation
44
+ #
45
+ # @param name [String, Symbol, nil] subscription name
46
+ # @param variable_definitions [Hash] variable definitions
47
+ # @param directives [Array<Directive, Hash, Array>] list of directives
48
+ # @param block [Proc] declare DSL for sub-fields
49
+ #
50
+ # @return [void]
51
+ def subscription(name = nil, variable_definitions = {}, directives = [], &block)
52
+ @__nodes << Operation.new(:subscription, name, variable_definitions, directives, &block)
53
+ end
54
+
55
+ ##
56
+ # Create GraphQL fragment operation
57
+ #
58
+ # @param name [String, Symbol] fragment name
59
+ # @param type [String, Symbol] fragment type or interface
60
+ # @param directives [Array<Directive, Hash, Array>] list of directives
61
+ # @param block [Proc] declare DSL for sub-fields
62
+ #
63
+ # @return [void]
64
+ def fragment(name, type, directives = [], &block)
65
+ @__nodes << FragmentOperation.new(name, type, directives, &block)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # Field GraphQL node
7
+ class Field < Node
8
+ include SelectionSet
9
+
10
+ ##
11
+ # @return [String, Symbol, nil] field alias
12
+ attr_reader :__alias
13
+
14
+ ##
15
+ # @return [Hash] list of filed arguments
16
+ attr_reader :__arguments
17
+
18
+ ##
19
+ # @return [Array<Directive>] list of directives
20
+ attr_reader :__directives
21
+
22
+ ##
23
+ # Create field
24
+ #
25
+ # @param field_name [String, Symbol] field name
26
+ # @param field_alias [String, Symbol, nil] field alias
27
+ # @param arguments [Hash] field arguments
28
+ # @param directives [Array<Directive, Hash, Array>] list of directives
29
+ # @param block [Proc] declare DSL for sub-fields
30
+ def initialize(field_name, field_alias = nil, arguments = {}, directives = [], &block)
31
+ @__alias = field_alias
32
+ @__arguments = arguments
33
+ @__directives = directives.map { |directive| Directive.from(directive) }
34
+
35
+ super(field_name, &block)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # Fragment operation GraphQL node
7
+ class FragmentOperation < Node
8
+ include SelectionSet
9
+
10
+ ##
11
+ # @return [String, Symbol] fragment type or interface
12
+ attr_reader :__type
13
+
14
+ ##
15
+ # @return [Array<Directive>] list of directives
16
+ attr_reader :__directives
17
+
18
+ ##
19
+ # Create fragment operation
20
+ #
21
+ # @param name [String, Symbol] fragment name
22
+ # @param type [String, Symbol] fragment type or interface
23
+ # @param directives [Array<Directive, Hash, Array>] list of directives
24
+ # @param block [Proc] declare DSL for sub-fields
25
+ def initialize(name, type, directives = [], &block)
26
+ raise Error, '`name` must be specified' if name.nil? || name.empty?
27
+ raise Error, '`type` must be specified' if type.nil? || type.empty?
28
+
29
+ @__type = type
30
+ @__directives = directives.map { |directive| Directive.from(directive) }
31
+
32
+ super(name, &block)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # Fragment spread GraphQL node
7
+ class FragmentSpread < Node
8
+ ##
9
+ # @return [Array<Directive>] list of directives
10
+ attr_reader :__directives
11
+
12
+ ##
13
+ # Create fragment spread
14
+ #
15
+ # @param name [String, Symbol] fragment name
16
+ # @param directives [Array<Hash, Array, Directive>] list of directives
17
+ def initialize(name, directives = [])
18
+ @__directives = directives.map { |directive| Directive.from(directive) }
19
+
20
+ super(name)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # Inline fragment GraphQL node
7
+ class InlineFragment < Node
8
+ include SelectionSet
9
+
10
+ ##
11
+ # @return [String, Symbol, nil] inline fragment type or interface
12
+ attr_reader :__type
13
+
14
+ ##
15
+ # @return [Array<Directive>] list of directives
16
+ attr_reader :__directives
17
+
18
+ ##
19
+ # Create inline fragment
20
+ #
21
+ # @param type [String, Symbol, nil] fragment type
22
+ # @param directives [Array<Directive, Hash, Array>] list of directives
23
+ # @param block [Proc] declare DSL for sub-fields
24
+ def initialize(type = nil, directives = [], &block)
25
+ raise Error, 'Sub-fields must be specified for inline fragment' if block.nil?
26
+
27
+ @__type = type
28
+ @__directives = directives.map { |directive| Directive.from(directive) }
29
+
30
+ super(nil, &block)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # This mixin help to reuse selections sets
7
+ module SelectionSet
8
+ ##
9
+ # Declare new GraphQL field
10
+ #
11
+ # This method can help to avoid name collisions i.e. +__field(:object_id)+
12
+ #
13
+ # @param name [String, Symbol] field name
14
+ # @param __alias [String, Symbol, nil] field alias
15
+ # @param __directives [Array] list of directives
16
+ # @param arguments [Hash] field arguments
17
+ # @param block [Proc] declare sub-fields
18
+ #
19
+ # @return [void]
20
+ #
21
+ # @example Declare fields use __field method (i.e. use GraphQL query)
22
+ # query = GraphQL::DSL.query {
23
+ # __field(:field1, id: 1) {
24
+ # __field(:subfield1, id: 1)
25
+ # __field(:subfield2, id: 2)
26
+ # }
27
+ # }
28
+ #
29
+ # @example Declare fields use DSL (i.e. use GraphQL query)
30
+ # query = GraphQL::DSL.query {
31
+ # field1 id: 1 {
32
+ # subfield1 id: 1
33
+ # subfield2 id: 2
34
+ # }
35
+ # }
36
+ def __field(name, __alias: nil, __directives: [], **arguments, &block) # rubocop:disable Lint/UnderscorePrefixedVariableName
37
+ @__nodes << Field.new(name, __alias, arguments, __directives, &block)
38
+ end
39
+
40
+ ###
41
+ # Insert GraphQL fragment
42
+ #
43
+ # @param name [String, Symbol] fragment name
44
+ # @param __directives [Array] list of directives
45
+ #
46
+ # @return [void]
47
+ #
48
+ # @example Insert fragment with +fragment1+ name
49
+ # query = GraphQL::DSL.query {
50
+ # field1 id: 1 {
51
+ # __fragment :fragment1
52
+ # }
53
+ # }
54
+ def __fragment(name, __directives: []) # rubocop:disable Lint/UnderscorePrefixedVariableName
55
+ @__nodes << FragmentSpread.new(name, __directives)
56
+ end
57
+
58
+ ###
59
+ # Insert GraphQL inline fragment
60
+ #
61
+ # @param type [String, Symbol, nil] fragment type
62
+ # @param __directives [Array] list of directives
63
+ # @param block [Proc] declare DSL for sub-fields
64
+ #
65
+ # @return [void]
66
+ def __inline_fragment(type, __directives: [], &block) # rubocop:disable Lint/UnderscorePrefixedVariableName
67
+ @__nodes << InlineFragment.new(type, __directives, &block)
68
+ end
69
+
70
+ private
71
+
72
+ ##
73
+ # Allow to respond to method missing at any case.
74
+ def respond_to_missing?(_method_name, _include_private = false)
75
+ true
76
+ end
77
+
78
+ ##
79
+ # Declare new GraphQL field
80
+ #
81
+ # @example Declare fields (i.e. use GraphQL query)
82
+ # query = GraphQL::DSL.query {
83
+ # items {
84
+ # id
85
+ # title
86
+ # }
87
+ # }
88
+ #
89
+ # puts query.to_gql
90
+ # # {
91
+ # # items
92
+ # # {
93
+ # # id
94
+ # # title
95
+ # # }
96
+ # # }
97
+ #
98
+ # @see #__field
99
+ def method_missing(name, *args, &block)
100
+ arguments = args.empty? ? {} : args[0]
101
+
102
+ __field(name, **arguments, &block)
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # @abstract Base class for all GraphQL DSL nodes
7
+ class Node
8
+ ##
9
+ # @return [String, Symbol, nil] node name
10
+ attr_reader :__name
11
+
12
+ # @return [Array<Node>] list of sub-nodes
13
+ attr_reader :__nodes
14
+
15
+ ##
16
+ # Create node
17
+ #
18
+ # @param name [String, Symbol, nil] node name
19
+ # @param block [Proc] declare DSL for sub-nodes
20
+ def initialize(name = nil, &block)
21
+ @__name = name
22
+ @__nodes = []
23
+
24
+ instance_eval(&block) if block
25
+ end
26
+
27
+ ##
28
+ # Generate GraphQL query
29
+ #
30
+ # @param level [Integer] indent level
31
+ # @param formatter [Formatter] GraphQL query formatter
32
+ #
33
+ # @return [String] GraphQL query string
34
+ def to_gql(level = 0, formatter = Formatter.new)
35
+ formatter.format_node(self, level)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # Operation GraphQL node
7
+ class Operation < Node
8
+ include SelectionSet
9
+
10
+ ##
11
+ # @return [Symbol] operation type (see {#initialize})
12
+ attr_reader :__operation_type
13
+
14
+ ##
15
+ # @return [Hash<Symbol, VariableDefinition>] variable definitions
16
+ attr_reader :__variable_definitions
17
+
18
+ ##
19
+ # @return [Array<Directive>] list of directives
20
+ attr_reader :__directives
21
+
22
+ ##
23
+ # Create operation (query, mutation, subscription)
24
+ #
25
+ # @param operation_type [Symbol] operation type
26
+ # @option operation_type [Symbol] :query query operation
27
+ # @option operation_type [Symbol] :mutation mutation operation
28
+ # @option operation_type [Symbol] :subscription subscription operation
29
+ # @param name [String, Symbol, nil] operation name
30
+ # @param variable_definitions [Hash] variable definitions
31
+ # @param directives [Array<Directive, Hash, Array>] list of directives
32
+ # @param block [Proc] declare DSL for sub-fields
33
+ def initialize(operation_type, name = nil, variable_definitions = {}, directives = [], &block)
34
+ variable_definitions.each do |variable_name, _|
35
+ raise Error, 'Variable name must be specified' if variable_name.nil? || variable_name.empty?
36
+ end
37
+
38
+ @__operation_type = operation_type
39
+ @__variable_definitions = variable_definitions.transform_values do |variable_definition|
40
+ VariableDefinition.from(variable_definition)
41
+ end
42
+ @__directives = directives.map { |directive| Directive.from(directive) }
43
+
44
+ super(name, &block)
45
+ end
46
+
47
+ ##
48
+ # Declare operation variable
49
+ #
50
+ # @param name [Symbol, String] variable name
51
+ # @param type [Symbol, String] variable type
52
+ # @param directives [Array<Directive, Hash, Array>] variable directives
53
+ # @param default [Object] variable default value
54
+ #
55
+ # @return [void]
56
+ def __var(name, type, default: UNDEFINED, directives: [])
57
+ @__variable_definitions[name] = VariableDefinition.new(type, default, directives)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module DSL
5
+ ##
6
+ # Gem version
7
+ VERSION = '1.0.0'
8
+ end
9
+ end