graphlyte 0.2.3 → 0.3.1
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/lib/graphlyte/arguments/set.rb +11 -21
- data/lib/graphlyte/arguments/value.rb +8 -2
- data/lib/graphlyte/builder.rb +15 -3
- data/lib/graphlyte/directive.rb +25 -0
- data/lib/graphlyte/field.rb +32 -5
- data/lib/graphlyte/inline_fragment.rb +29 -0
- data/lib/graphlyte/query.rb +42 -5
- data/lib/graphlyte/refinements/string_refinement.rb +2 -2
- data/lib/graphlyte/schema/parser.rb +360 -224
- data/lib/graphlyte/schema/types/base.rb +2 -0
- data/lib/graphlyte/types.rb +2 -2
- data/lib/graphlyte.rb +14 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62efb09535601a1ef60cd1f6d24108a32c954382a8e2f280e8e570bf8d14d0e2
|
4
|
+
data.tar.gz: 9098e5efe7b6344f1bff6effddd660de3e4d6632ec5d8c7b18516cdc7d522a4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2577fa1470f54149841295630b98b865f44b2cc5059c67a34bdea735dfb10b0da8bedfd325567577b7e0d6b900f9cc5004e4cf7d4b7fe8e6ac801ea491487e49
|
7
|
+
data.tar.gz: b319c1970a99b031e13df0b33c74a5687490e423c4d141016cb01edf1fb0e2a5a17474ed19bea4b4db709715a883fb3148fac8414a1da1d293fca52d355d5c7c
|
@@ -26,15 +26,15 @@ module Graphlyte
|
|
26
26
|
variables
|
27
27
|
end
|
28
28
|
|
29
|
-
def to_h(
|
29
|
+
def to_h(raw = false)
|
30
30
|
return {} unless values && !values.empty?
|
31
31
|
values.inject({}) do |memo, (k, v)|
|
32
32
|
if v.is_a?(Array)
|
33
|
-
memo[k.to_s.to_camel_case] = v.map(
|
33
|
+
memo[k.to_s.to_camel_case] = v.map { |value| value.to_s(raw) }
|
34
34
|
elsif v.is_a?(Set)
|
35
35
|
memo[k.to_s.to_camel_case] = v.to_h
|
36
36
|
else
|
37
|
-
memo[k.to_s.to_camel_case] = v.to_s
|
37
|
+
memo[k.to_s.to_camel_case] = v.to_s(raw)
|
38
38
|
end
|
39
39
|
memo
|
40
40
|
end
|
@@ -62,27 +62,17 @@ module Graphlyte
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def expand_arguments(data)
|
65
|
-
data.
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
Value.new(item)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
elsif v.is_a?(Hash)
|
75
|
-
memo[k] = Set.new(v)
|
65
|
+
data.transform_values do |value|
|
66
|
+
case value
|
67
|
+
when Array
|
68
|
+
value.map { |item| Value.from(item) }
|
69
|
+
when Hash
|
70
|
+
Set.new(value)
|
76
71
|
else
|
77
|
-
|
78
|
-
memo[k] = v
|
79
|
-
else
|
80
|
-
memo[k] = Value.new(v)
|
81
|
-
end
|
72
|
+
Value.from(value)
|
82
73
|
end
|
83
|
-
memo
|
84
74
|
end
|
85
75
|
end
|
86
76
|
end
|
87
77
|
end
|
88
|
-
end
|
78
|
+
end
|
@@ -11,6 +11,12 @@ module Graphlyte
|
|
11
11
|
@value = value
|
12
12
|
end
|
13
13
|
|
14
|
+
def self.from(value)
|
15
|
+
return value if value.is_a? self
|
16
|
+
|
17
|
+
new(value)
|
18
|
+
end
|
19
|
+
|
14
20
|
def symbol?
|
15
21
|
value.is_a? Symbol
|
16
22
|
end
|
@@ -19,10 +25,10 @@ module Graphlyte
|
|
19
25
|
value.is_a? Schema::Types::Base
|
20
26
|
end
|
21
27
|
|
22
|
-
def to_s
|
28
|
+
def to_s(raw = false)
|
23
29
|
return "$#{value.to_s.to_camel_case}" if value.is_a? Symbol
|
24
30
|
return value if value.is_a? Numeric
|
25
|
-
return "\"#{value}\"" if value.is_a?
|
31
|
+
return "\"#{value}\"" if value.is_a?(String) && !raw
|
26
32
|
return "null" if value.nil?
|
27
33
|
return "$#{value.placeholder.to_camel_case}" if value.is_a? Schema::Types::Base
|
28
34
|
value.to_s
|
data/lib/graphlyte/builder.rb
CHANGED
@@ -8,19 +8,31 @@ module Graphlyte
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def <<(buildable)
|
11
|
-
raise "Must pass a Fieldset or Fragment" unless [Fragment, Fieldset].include?(buildable.class)
|
11
|
+
raise "Must pass a Fieldset or Fragment" unless [Fragment, Fieldset, InlineFragment].include?(buildable.class)
|
12
12
|
|
13
13
|
@fields.concat(buildable.fields) if buildable.class.eql? Fieldset
|
14
14
|
|
15
15
|
# todo: handle fragments better, it's not a field
|
16
|
-
@fields << buildable if buildable.class
|
16
|
+
@fields << buildable if [InlineFragment, Fragment].include? buildable.class
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove(field_symbol)
|
20
|
+
@fields.reject! do |field|
|
21
|
+
field.class == Fragment ? false : field.name == field_symbol.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
@fields.select { |field| field.class == Fragment }.each do |fragment|
|
25
|
+
fragment.fields.reject! do |field|
|
26
|
+
field.name == field_symbol.to_s
|
27
|
+
end
|
28
|
+
end
|
17
29
|
end
|
18
30
|
|
19
31
|
def method_missing(method, fieldset_or_hargs=nil, hargs={}, &block)
|
20
32
|
# todo: camel case method
|
21
33
|
|
22
34
|
# hack for ruby bug in lower versions
|
23
|
-
if [Fieldset, Fragment].include?(fieldset_or_hargs.class)
|
35
|
+
if [Fieldset, Fragment, InlineFragment].include?(fieldset_or_hargs.class)
|
24
36
|
field = Field.new(method, fieldset_or_hargs, hargs)
|
25
37
|
else
|
26
38
|
field = Field.new(method, Fieldset.empty, fieldset_or_hargs)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'arguments/set'
|
2
|
+
|
3
|
+
module Graphlyte
|
4
|
+
class Directive
|
5
|
+
attr_reader :name, :inputs
|
6
|
+
|
7
|
+
def initialize(name, **hargs)
|
8
|
+
@name = name
|
9
|
+
@inputs = Arguments::Set.new(hargs)
|
10
|
+
end
|
11
|
+
|
12
|
+
def inflate(indent, string, field: nil, args: nil)
|
13
|
+
# add directive after fieldname?
|
14
|
+
string += ' ' * indent unless indent.nil?
|
15
|
+
if !args.nil? && args.to_s.empty?
|
16
|
+
string += "#{field} " if field
|
17
|
+
else
|
18
|
+
string += "#{field}#{args.to_s} "
|
19
|
+
end
|
20
|
+
string += "@#{name}"
|
21
|
+
string += @inputs.to_s unless @inputs.to_s.empty?
|
22
|
+
string
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/graphlyte/field.rb
CHANGED
@@ -1,25 +1,39 @@
|
|
1
1
|
require_relative "./arguments/set"
|
2
|
+
require_relative 'directive'
|
2
3
|
require_relative "./refinements/string_refinement"
|
3
4
|
module Graphlyte
|
4
5
|
class Field
|
5
6
|
using Refinements::StringRefinement
|
6
7
|
|
7
|
-
attr_reader :name, :fieldset, :inputs, :
|
8
|
+
attr_reader :name, :fieldset, :inputs, :directive
|
8
9
|
|
9
|
-
def initialize(name, fieldset, hargs, inputs: Arguments::Set.new(hargs))
|
10
|
+
def initialize(name, fieldset, hargs, directive: nil, inputs: Arguments::Set.new(hargs))
|
10
11
|
@name = name.to_s.to_camel_case
|
11
12
|
@fieldset = fieldset
|
12
13
|
@inputs = inputs
|
13
14
|
@alias = nil
|
15
|
+
@directive = directive
|
14
16
|
end
|
15
17
|
|
16
18
|
def atomic?
|
17
19
|
fieldset.empty?
|
18
|
-
end
|
20
|
+
end
|
19
21
|
|
20
22
|
def alias(name, &block)
|
21
23
|
@alias = name
|
22
|
-
|
24
|
+
if block
|
25
|
+
fieldset.builder.>.instance_eval(&block)
|
26
|
+
else
|
27
|
+
self
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def include(**hargs, &block)
|
32
|
+
make_directive('include', **hargs, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def skip(**hargs, &block)
|
36
|
+
make_directive('skip', **hargs, &block)
|
23
37
|
end
|
24
38
|
|
25
39
|
def to_s(indent=0)
|
@@ -27,12 +41,25 @@ module Graphlyte
|
|
27
41
|
actual_indent = ("\s" * indent) * 2
|
28
42
|
if @alias
|
29
43
|
str += "#{actual_indent}#{@alias}: #{name}"
|
30
|
-
str += inputs.to_s.empty? ? "()" : inputs.to_s
|
44
|
+
str += inputs.to_s.empty? ? "()" : inputs.to_s
|
45
|
+
elsif @directive
|
46
|
+
str = @directive.inflate(indent * 2, str, field: name, args: inputs)
|
31
47
|
else
|
32
48
|
str += "#{actual_indent}#{name}#{inputs.to_s}"
|
33
49
|
end
|
34
50
|
str += " {\n#{fieldset.to_s(indent + 1)}\n#{actual_indent}}" unless atomic?
|
35
51
|
str
|
36
52
|
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def method_missing(symbol, **hargs, &block)
|
57
|
+
make_directive(symbol.to_s, **hargs, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def make_directive(name, **hargs, &block)
|
61
|
+
@directive = Directive.new(name, **hargs)
|
62
|
+
fieldset.builder.>.instance_eval(&block) if block
|
63
|
+
end
|
37
64
|
end
|
38
65
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "./fieldset"
|
2
|
+
|
3
|
+
module Graphlyte
|
4
|
+
class InlineFragment < Fieldset
|
5
|
+
attr_reader :directive
|
6
|
+
|
7
|
+
def self.from_directive(directive, **hargs)
|
8
|
+
new(nil, directive: directive, **hargs)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(model = nil, directive: nil, **hargs)
|
12
|
+
@directive = directive
|
13
|
+
super(model, **hargs)
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s(indent=0)
|
17
|
+
actual_indent = ("\s" * indent) * 2
|
18
|
+
string = '...'
|
19
|
+
string += " on #{model_name}" if model_name
|
20
|
+
inflate_indent = model_name ? 1 : 0
|
21
|
+
string = directive.inflate(0, string) if directive
|
22
|
+
string += " {\n"
|
23
|
+
string += super(indent + 1)
|
24
|
+
string += "\n#{actual_indent}}"
|
25
|
+
|
26
|
+
"#{actual_indent}#{string}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/graphlyte/query.rb
CHANGED
@@ -1,6 +1,37 @@
|
|
1
1
|
require_relative "./refinements/string_refinement"
|
2
2
|
require "json"
|
3
3
|
module Graphlyte
|
4
|
+
class Selector
|
5
|
+
def initialize(selector)
|
6
|
+
@selector_tokens = selector.split('.')
|
7
|
+
end
|
8
|
+
|
9
|
+
def modify(fields, selector_tokens = @selector_tokens, &block)
|
10
|
+
token = selector_tokens.shift
|
11
|
+
|
12
|
+
if token == '*'
|
13
|
+
fields.each do |field|
|
14
|
+
next if field.class == Fragment
|
15
|
+
|
16
|
+
modify(field.fieldset.fields, [token], &block)
|
17
|
+
field.fieldset.builder.instance_eval(&block) unless field.fieldset.fields.empty?
|
18
|
+
end
|
19
|
+
else
|
20
|
+
needle = fields.find do |field|
|
21
|
+
field.name == token
|
22
|
+
end
|
23
|
+
|
24
|
+
raise "#{token} not found in query" unless needle
|
25
|
+
|
26
|
+
if selector_tokens.size.zero?
|
27
|
+
needle.fieldset.builder.instance_eval(&block)
|
28
|
+
else
|
29
|
+
modify(needle.fieldset.fields, selector_tokens, &block)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
4
35
|
class Query < Fieldset
|
5
36
|
using Refinements::StringRefinement
|
6
37
|
attr_reader :name, :type
|
@@ -11,6 +42,10 @@ module Graphlyte
|
|
11
42
|
super(**hargs)
|
12
43
|
end
|
13
44
|
|
45
|
+
def at(selector, &block)
|
46
|
+
Selector.new(selector).modify(fields, &block)
|
47
|
+
end
|
48
|
+
|
14
49
|
def placeholders
|
15
50
|
flatten_variables(builder.>>).map do |value|
|
16
51
|
unless value.formal?
|
@@ -43,7 +78,7 @@ module Graphlyte
|
|
43
78
|
end
|
44
79
|
str += "(#{type_new.join(", ")})"
|
45
80
|
end
|
46
|
-
{ query: "#{str} #{to_s(1)}", variables: Arguments::Set.new(hargs).to_h }.to_json
|
81
|
+
{ query: "#{str} #{to_s(1)}", variables: Arguments::Set.new(hargs).to_h(true) }.to_json
|
47
82
|
end
|
48
83
|
|
49
84
|
def to_s(indent=0)
|
@@ -62,7 +97,7 @@ module Graphlyte
|
|
62
97
|
elsif hargs[var.value].is_a? Integer
|
63
98
|
memo << [var.value, "Int"]
|
64
99
|
elsif hargs[var.value].is_a? Array
|
65
|
-
memo <<
|
100
|
+
memo << "[#{merge_variable_types(var.value, hargs).first}]"
|
66
101
|
end
|
67
102
|
else
|
68
103
|
memo << [var.value.placeholder, var.value.name, var.value.default]
|
@@ -70,7 +105,7 @@ module Graphlyte
|
|
70
105
|
memo
|
71
106
|
end
|
72
107
|
end
|
73
|
-
|
108
|
+
|
74
109
|
def format_fragments
|
75
110
|
str = "\n"
|
76
111
|
flatten(builder.>>).each do |_, fragment|
|
@@ -83,8 +118,9 @@ module Graphlyte
|
|
83
118
|
|
84
119
|
def flatten_variables(fields, variables=[])
|
85
120
|
fields.each do |field|
|
86
|
-
variables.concat field.inputs.extract_variables unless field.class
|
87
|
-
if field.
|
121
|
+
variables.concat field.inputs.extract_variables unless [InlineFragment, Fragment].include? field.class
|
122
|
+
variables.concat field.directive.inputs.extract_variables if field.respond_to?(:directive) && field.directive
|
123
|
+
if [InlineFragment, Fragment].include? field.class
|
88
124
|
flatten_variables(field.fields, variables)
|
89
125
|
else
|
90
126
|
flatten_variables(field.fieldset.fields, variables)
|
@@ -95,6 +131,7 @@ module Graphlyte
|
|
95
131
|
|
96
132
|
def flatten(fields, new_fields = {})
|
97
133
|
fields.each do |field|
|
134
|
+
next if field.class == InlineFragment
|
98
135
|
if field.class.eql?(Fragment)
|
99
136
|
new_fields[field.fragment] = field
|
100
137
|
unless field.empty?
|
@@ -10,11 +10,11 @@ module Graphlyte
|
|
10
10
|
def to_camel_case
|
11
11
|
start_of_string = match(/(^_+)/)&.[](0)
|
12
12
|
end_of_string = match(/(_+$)/)&.[](0)
|
13
|
-
|
13
|
+
|
14
14
|
middle = split("_").reject(&:empty?).inject([]) do |memo, str|
|
15
15
|
memo << (memo.empty? ? str : str.capitalize)
|
16
16
|
end.join("")
|
17
|
-
|
17
|
+
|
18
18
|
"#{start_of_string}#{middle}#{end_of_string}"
|
19
19
|
end
|
20
20
|
end
|
@@ -13,18 +13,31 @@ module Graphlyte
|
|
13
13
|
fields
|
14
14
|
end
|
15
15
|
|
16
|
+
def skip_fieldset
|
17
|
+
expect(:FIELDSET)
|
18
|
+
parse_fields
|
19
|
+
need(:END_FIELDSET)
|
20
|
+
end
|
21
|
+
|
16
22
|
def parse_field
|
17
23
|
alias_field = expect(:ALIAS)
|
18
24
|
if token = expect(:FRAGMENT_REF)
|
19
25
|
raise "Can't find fragment #{token[0][1]}" unless fragments_dictionary[token[0][1]]
|
20
26
|
fragments_dictionary[token[0][1]]
|
21
|
-
elsif
|
27
|
+
elsif expect(:INLINE_FRAGMENT)
|
28
|
+
field = parse_inline_fragment
|
29
|
+
elsif expect(:FIELDSET)
|
30
|
+
|
31
|
+
elsif (field = expect(:FIELD_NAME))
|
22
32
|
args = parse_args
|
23
|
-
|
24
|
-
|
25
|
-
|
33
|
+
directive = parse_directive
|
34
|
+
|
35
|
+
if builder = parse_fieldset_into_builder
|
36
|
+
need(:END_FIELDSET)
|
37
|
+
fieldset = Fieldset.new(builder: builder)
|
38
|
+
field = Field.new(field[0][1], fieldset, args, directive: directive)
|
26
39
|
else
|
27
|
-
field = Field.new(field[0][1], Fieldset.empty, args)
|
40
|
+
field = Field.new(field[0][1], Fieldset.empty, args, directive: directive)
|
28
41
|
end
|
29
42
|
|
30
43
|
if alias_field
|
@@ -35,10 +48,29 @@ module Graphlyte
|
|
35
48
|
end
|
36
49
|
end
|
37
50
|
|
38
|
-
def
|
39
|
-
|
51
|
+
def parse_inline_fragment
|
52
|
+
model_name = expect(:MODEL_NAME)&.dig(0, 1)
|
53
|
+
directive = parse_directive
|
54
|
+
inputs = directive ? (parse_args || {}) : {}
|
55
|
+
fields = expect(:FIELDSET) ? parse_fields : []
|
56
|
+
need(:END_FIELDSET)
|
57
|
+
|
58
|
+
InlineFragment.new(model_name, directive: directive, builder: Builder.new(fields), **inputs)
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_directive
|
62
|
+
if token = expect(:DIRECTIVE)
|
63
|
+
inputs = parse_args || {}
|
64
|
+
|
65
|
+
Directive.new(token[0][1], **inputs)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_fieldset_into_builder
|
70
|
+
fields = []
|
71
|
+
if expect(:FIELDSET)
|
40
72
|
fields = parse_fields
|
41
|
-
|
73
|
+
Builder.new(fields)
|
42
74
|
end
|
43
75
|
end
|
44
76
|
|
@@ -51,7 +83,7 @@ module Graphlyte
|
|
51
83
|
end
|
52
84
|
|
53
85
|
def parse_default
|
54
|
-
if expect(:
|
86
|
+
if expect(:DEFAULT_VALUE)
|
55
87
|
value = parse_value
|
56
88
|
need(:END_DEFAULT_VALUE)
|
57
89
|
value
|
@@ -60,24 +92,31 @@ module Graphlyte
|
|
60
92
|
|
61
93
|
def parse_arg
|
62
94
|
if (token = expect(:ARG_KEY)) && (value = parse_value)
|
63
|
-
|
95
|
+
parse_default
|
64
96
|
key = token[0][1]
|
65
|
-
hash = {}
|
66
|
-
hash[key] = value
|
67
|
-
hash
|
68
|
-
elsif (token = expect(:SPECIAL_ARG_KEY)) && (value = parse_value)
|
69
|
-
defaults = parse_default
|
70
|
-
@special_args ||= {}
|
71
97
|
arg = {}
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
98
|
+
arg[key] = value
|
99
|
+
elsif (token = expect(:SPECIAL_ARG_KEY)) && (value = parse_value)
|
100
|
+
arg = expect_and_inflate_special_args(token, value)
|
101
|
+
end
|
102
|
+
|
103
|
+
arg
|
104
|
+
end
|
105
|
+
|
106
|
+
def expect_and_inflate_special_args(token, value)
|
107
|
+
return { token[0][1] => value } if value.class == Schema::Types::Base
|
108
|
+
|
109
|
+
defaults = parse_default
|
110
|
+
@special_args ||= {}
|
111
|
+
arg = {}
|
112
|
+
if [Array, Hash].include?(value.class)
|
113
|
+
arg[token[0][1]] = value
|
114
|
+
else
|
115
|
+
new_val = Schema::Types::Base.new(value, token[0][1], defaults)
|
116
|
+
arg[token[0][1]] = new_val
|
80
117
|
end
|
118
|
+
@special_args.merge!(arg)
|
119
|
+
arg
|
81
120
|
end
|
82
121
|
|
83
122
|
def parse_value
|
@@ -89,9 +128,9 @@ module Graphlyte
|
|
89
128
|
@special_args[ref]
|
90
129
|
elsif token = expect(:SPECIAL_ARG_VAL)
|
91
130
|
token[0][1]
|
92
|
-
elsif token = expect(:
|
131
|
+
elsif token = expect(:ARG_HASH)
|
93
132
|
parse_arg_hash
|
94
|
-
elsif expect(:
|
133
|
+
elsif expect(:ARG_ARRAY)
|
95
134
|
parse_arg_array
|
96
135
|
end
|
97
136
|
end
|
@@ -134,6 +173,10 @@ module Graphlyte
|
|
134
173
|
end
|
135
174
|
end
|
136
175
|
|
176
|
+
def tokens?
|
177
|
+
!tokens[position].nil?
|
178
|
+
end
|
179
|
+
|
137
180
|
def need(*required_tokens)
|
138
181
|
upcoming = tokens[position, required_tokens.size]
|
139
182
|
expect(*required_tokens) or raise "Unexpected tokens. Expected #{required_tokens.inspect} but got #{upcoming.inspect}"
|
@@ -154,7 +197,7 @@ module Graphlyte
|
|
154
197
|
if current_ref
|
155
198
|
exists = sorted.any? do |frags|
|
156
199
|
frags.find do |el|
|
157
|
-
el[0] == :
|
200
|
+
el[0] == :FRAGMENT && el[1] == current_ref[1]
|
158
201
|
end
|
159
202
|
end
|
160
203
|
if exists
|
@@ -170,21 +213,33 @@ module Graphlyte
|
|
170
213
|
end
|
171
214
|
end
|
172
215
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
216
|
+
# Select the fragments tokens as an array of arrays
|
217
|
+
# @return Array [[[:FRAGMENT, 'foo', bar], [:FIELDSET], [:END_FIELDSET]], [[:FRAGMENT 'buzz', 'bazz']...
|
218
|
+
def fetch_fragments(tokens = @tokens.dup, fragment_tokens = [], memo = { active: false, starts: 0, ends: 0, idx: 0 })
|
219
|
+
token_arr = tokens.shift
|
220
|
+
return fragment_tokens if token_arr.nil?
|
221
|
+
|
222
|
+
if memo[:active] == true
|
223
|
+
fragment_tokens[memo[:idx]] << token_arr
|
224
|
+
end
|
225
|
+
|
226
|
+
if token_arr[0] == :END_FIELDSET && memo[:active] == true
|
227
|
+
memo[:ends] += 1
|
228
|
+
if memo[:starts] == memo[:ends] + 1
|
229
|
+
memo[:active] = false
|
230
|
+
memo[:ends] = 0
|
231
|
+
memo[:starts] = 0
|
178
232
|
memo[:idx] += 1
|
179
|
-
elsif token_arr[0] === :START_FRAGMENT
|
180
|
-
memo[:fragments][memo[:idx]] = [token_arr]
|
181
|
-
memo[:taking] = true
|
182
|
-
elsif memo[:taking]
|
183
|
-
memo[:fragments][memo[:idx]] << token_arr
|
184
233
|
end
|
185
|
-
|
234
|
+
elsif token_arr[0] == :FRAGMENT
|
235
|
+
memo[:active] = true
|
236
|
+
memo[:starts] += 1
|
237
|
+
fragment_tokens[memo[:idx]] = [token_arr]
|
238
|
+
elsif token_arr[0] == :FIELDSET && memo[:active] == true
|
239
|
+
memo[:starts] += 1
|
186
240
|
end
|
187
|
-
|
241
|
+
|
242
|
+
fetch_fragments(tokens, fragment_tokens, memo)
|
188
243
|
end
|
189
244
|
end
|
190
245
|
|
@@ -205,12 +260,16 @@ module Graphlyte
|
|
205
260
|
end
|
206
261
|
|
207
262
|
def parse_fragment
|
208
|
-
if token = expect(:
|
263
|
+
if token = expect(:FRAGMENT)
|
209
264
|
parse_args
|
210
|
-
builder =
|
211
|
-
|
265
|
+
if builder = parse_fieldset_into_builder
|
266
|
+
fragment = Fragment.new(token[0][1], token[0][2], builder: builder)
|
267
|
+
need(:END_FIELDSET) if tokens?
|
268
|
+
elsif fields = parse_fields
|
269
|
+
builder = Builder.new(fields)
|
270
|
+
fragment = Fragment.new(token[0][1], token[0][2], builder: builder)
|
271
|
+
end
|
212
272
|
@fragments_dictionary[token[0][1]] = fragment
|
213
|
-
need(:END_FRAGMENT)
|
214
273
|
end
|
215
274
|
end
|
216
275
|
end
|
@@ -227,41 +286,42 @@ module Graphlyte
|
|
227
286
|
|
228
287
|
def initialize(tokens)
|
229
288
|
@tokens = tokens
|
230
|
-
|
289
|
+
|
290
|
+
@fragment_tokens = sort_fragments([], fetch_fragments)
|
231
291
|
@fragments_dictionary = {}
|
232
292
|
@fragments_dictionary = @fragment_tokens.any? ? FragmentParser.new(@fragment_tokens).parse_fragments : {}
|
233
293
|
@position = 0
|
234
294
|
end
|
235
295
|
|
236
296
|
def parse
|
237
|
-
if token = expect(:
|
238
|
-
|
239
|
-
elsif
|
240
|
-
|
297
|
+
if token = expect(:EXPRESSION)
|
298
|
+
parse_expression(token[0][1], token[0][2])
|
299
|
+
elsif expect(:FRAGMENT)
|
300
|
+
skip_fragments
|
301
|
+
parse
|
241
302
|
else
|
242
303
|
raise "INVALID"
|
243
304
|
end
|
244
305
|
end
|
245
306
|
|
246
|
-
def
|
247
|
-
|
248
|
-
builder = Builder.new parse_fields
|
249
|
-
query = Query.new(name, :query, builder: builder)
|
250
|
-
need(:END_QUERY)
|
251
|
-
query
|
307
|
+
def skip_fragments
|
308
|
+
skip_fieldset
|
252
309
|
end
|
253
310
|
|
254
|
-
def
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
311
|
+
def parse_expression(type, name)
|
312
|
+
parse_args
|
313
|
+
fields = []
|
314
|
+
builder = parse_fieldset_into_builder
|
315
|
+
need(:END_FIELDSET)
|
316
|
+
query = Query.new(name, type.to_sym, builder: builder)
|
317
|
+
query
|
259
318
|
end
|
260
319
|
end
|
261
320
|
|
321
|
+
class LexerError < StandardError; end
|
322
|
+
|
262
323
|
class Lexer
|
263
324
|
attr_reader :stack, :scanner
|
264
|
-
|
265
325
|
def initialize(gql, scanner: StringScanner.new(gql))
|
266
326
|
@original_string = gql
|
267
327
|
@scanner = scanner
|
@@ -269,211 +329,283 @@ module Graphlyte
|
|
269
329
|
end
|
270
330
|
|
271
331
|
SPECIAL_ARG_REGEX = /^\s*(?:(?<![\"\{]))([\w\!\[\]]+)(?:(?![\"\}]))/
|
272
|
-
SIMPLE_EXPRESSION = /(query|mutation|fragment)\s*\w+\s*on\w*.*\{\s*\n*[.|\w\s]*\}/
|
273
|
-
START_MAP = {
|
274
|
-
'query' => :START_QUERY,
|
275
|
-
'mutation' => :START_MUTATION,
|
276
|
-
'fragment' => :START_FRAGMENT
|
277
|
-
}
|
278
332
|
|
279
333
|
def tokenize
|
280
334
|
until scanner.eos?
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
@tokens << [:END_EXPRESSION_SHOULDNT_GET_THIS]
|
297
|
-
else
|
298
|
-
advance
|
299
|
-
end
|
300
|
-
when :fragment
|
301
|
-
if scanner.scan /\s*\}\s*/
|
302
|
-
@tokens << [:END_FRAGMENT]
|
303
|
-
pop_state
|
304
|
-
pop_context
|
305
|
-
elsif scanner.check /^\s*\{\s*/
|
306
|
-
if get_context == :field
|
307
|
-
push_state :field
|
308
|
-
push_context :field
|
309
|
-
else
|
310
|
-
scanner.scan /^\s*\{\s*/
|
311
|
-
push_context :field
|
312
|
-
end
|
313
|
-
else
|
314
|
-
handle_field
|
315
|
-
end
|
316
|
-
when :mutation
|
317
|
-
if scanner.scan /\}/
|
318
|
-
@tokens << [:END_MUTATION]
|
319
|
-
pop_state
|
320
|
-
pop_context
|
321
|
-
elsif scanner.check /^\s*\{\s*$/
|
322
|
-
if get_context == :field
|
323
|
-
push_state :field
|
324
|
-
else
|
325
|
-
scanner.scan /^\s*\{\s*$/
|
326
|
-
push_context :field
|
327
|
-
end
|
328
|
-
else
|
329
|
-
handle_field
|
330
|
-
end
|
331
|
-
when :query
|
332
|
-
if scanner.scan /\s*\}\s*/
|
333
|
-
@tokens << [:END_QUERY]
|
334
|
-
pop_state
|
335
|
-
pop_context
|
336
|
-
elsif scanner.check /^\s*\{\s*/
|
337
|
-
if get_context == :field
|
338
|
-
push_state :field
|
339
|
-
push_context :field
|
340
|
-
else
|
341
|
-
scanner.scan /^\s*\{\s*/
|
342
|
-
push_context :field
|
343
|
-
end
|
344
|
-
else
|
345
|
-
handle_field
|
346
|
-
end
|
347
|
-
when :field
|
348
|
-
if scanner.check /\s*\}\s*/
|
349
|
-
if get_context == :field
|
350
|
-
scanner.scan /\s*\}\s*/
|
351
|
-
@tokens << [:END_FIELD]
|
352
|
-
pop_state
|
353
|
-
else
|
354
|
-
pop_state
|
355
|
-
end
|
335
|
+
tokenize_objects
|
336
|
+
end
|
337
|
+
|
338
|
+
@tokens
|
339
|
+
end
|
340
|
+
|
341
|
+
def tokenize_objects
|
342
|
+
case state
|
343
|
+
when :default # the stack is empty, can only process top level fragments or expressions
|
344
|
+
if scanner.scan %r{\s*fragment\s*(\w+)\s*on\s*(\w+)}
|
345
|
+
@tokens << [:FRAGMENT, scanner[1], scanner[2]]
|
346
|
+
push_context :fragments
|
347
|
+
# check for a fieldset
|
348
|
+
if scanner.check %r[\s*{]
|
349
|
+
tokenize_fieldset
|
356
350
|
else
|
357
|
-
|
351
|
+
scanner.scan /\(/
|
352
|
+
@tokens << [:START_ARGS]
|
353
|
+
push_state :arguments
|
358
354
|
end
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
elsif scanner.scan /,/
|
371
|
-
#
|
355
|
+
elsif scanner.check /\{/
|
356
|
+
@tokens << [:EXPRESSION, 'query', nil] if get_context == :default
|
357
|
+
push_context :fieldset
|
358
|
+
|
359
|
+
tokenize_fieldset
|
360
|
+
elsif scanner.scan %r{^(\w+) (\w+)?}
|
361
|
+
@tokens << [:EXPRESSION, scanner[1], scanner[2]]
|
362
|
+
push_context :expression
|
363
|
+
# check for a fieldset
|
364
|
+
if scanner.check %r[\s*{]
|
365
|
+
tokenize_fieldset
|
372
366
|
else
|
373
|
-
|
367
|
+
scanner.scan /\(/
|
368
|
+
@tokens << [:START_ARGS]
|
369
|
+
push_state :arguments
|
374
370
|
end
|
375
|
-
|
376
|
-
if
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
else
|
381
|
-
push_state :argument_defaults
|
382
|
-
handle_shared_arguments
|
371
|
+
elsif scanner.check /\s*\}/
|
372
|
+
if get_context == :fragments
|
373
|
+
end_fragment
|
374
|
+
elsif get_context == :expression
|
375
|
+
end_expression
|
383
376
|
end
|
384
|
-
|
385
|
-
|
377
|
+
else
|
378
|
+
advance
|
386
379
|
end
|
380
|
+
when :fieldset
|
381
|
+
tokenize_fields
|
382
|
+
when :arguments
|
383
|
+
tokenize_arguments
|
384
|
+
when :argument_defaults
|
385
|
+
tokenize_argument_defaults
|
386
|
+
when :hash_arguments
|
387
|
+
tokenize_hash_arguments
|
388
|
+
when :array_arguments
|
389
|
+
tokenize_array_arguments
|
390
|
+
when :special_args
|
391
|
+
tokenize_special_arguments
|
392
|
+
when :inline_fragment
|
393
|
+
tokenize_inline_fragment
|
387
394
|
end
|
388
|
-
@tokens
|
389
395
|
end
|
390
396
|
|
391
|
-
|
397
|
+
def check_for_last(regex = /\s*\}/)
|
398
|
+
scanner.check regex
|
399
|
+
end
|
392
400
|
|
393
|
-
def
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
elsif scanner.scan
|
407
|
-
@tokens << [:
|
408
|
-
|
409
|
-
|
410
|
-
@tokens << [:FIELD_NAME, scanner[1]]
|
411
|
-
pop_context
|
412
|
-
# we need to pop state if we are nested in a field, and not in the query context
|
413
|
-
pop_state if get_context == :field
|
414
|
-
elsif scanner.scan /\s*(\w+)\s*/
|
415
|
-
@tokens << [:FIELD_NAME, scanner[1]]
|
416
|
-
elsif scanner.scan /^\s*\(/
|
417
|
-
@tokens << [:START_ARGS]
|
418
|
-
push_state :arguments
|
401
|
+
def check_for_final
|
402
|
+
scanner.check /\s*\}(?!\s*\})/
|
403
|
+
end
|
404
|
+
|
405
|
+
def check_for_not_last
|
406
|
+
scanner.check /\s*\}(?=\s*\})/
|
407
|
+
end
|
408
|
+
|
409
|
+
def tokenize_inline_fragment
|
410
|
+
if scanner.scan /on (\w+)/
|
411
|
+
@tokens << [:MODEL_NAME, scanner[1]]
|
412
|
+
|
413
|
+
pop_state
|
414
|
+
elsif scanner.scan /@(\w+)/
|
415
|
+
@tokens << [:DIRECTIVE, scanner[1]]
|
416
|
+
|
417
|
+
pop_state
|
419
418
|
else
|
419
|
+
# throw an error here?
|
420
420
|
advance
|
421
421
|
end
|
422
422
|
end
|
423
423
|
|
424
|
-
def
|
424
|
+
def end_fieldset
|
425
|
+
scanner.scan /\s*\}/
|
426
|
+
@tokens << [:END_FIELDSET]
|
427
|
+
pop_state
|
428
|
+
pop_context if state == :default
|
429
|
+
end
|
430
|
+
|
431
|
+
def end_arguments
|
432
|
+
scanner.scan /\s*\)/
|
433
|
+
@tokens << [:END_ARGS]
|
434
|
+
pop_state
|
435
|
+
end_fieldset while check_for_last && state == :fieldset
|
436
|
+
end
|
437
|
+
|
438
|
+
# to tired to figure out why this is right now
|
439
|
+
def tokenize_argument_defaults
|
440
|
+
if scanner.check /\)/
|
441
|
+
@tokens << [:END_DEFAULT_VALUE]
|
442
|
+
pop_state
|
443
|
+
elsif scanner.scan /[\n|,]/
|
444
|
+
@tokens << [:END_DEFAULT_VALUE]
|
445
|
+
pop_state
|
446
|
+
else
|
447
|
+
tokenize_shared_arguments
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
def tokenize_special_arguments
|
452
|
+
if scanner.check SPECIAL_ARG_REGEX
|
453
|
+
scanner.scan SPECIAL_ARG_REGEX
|
454
|
+
|
455
|
+
@tokens << [:SPECIAL_ARG_VAL, scanner[1]]
|
456
|
+
|
457
|
+
pop_state
|
458
|
+
|
459
|
+
end_arguments if check_for_last(/\s*\)/)
|
460
|
+
else
|
461
|
+
# revisit this.. should we throw an error here?
|
462
|
+
pop_state
|
463
|
+
raise LexerError, "why can't we parse #{scanner.peek(5)}"
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def tokenize_array_arguments
|
468
|
+
if scanner.scan /\]/
|
469
|
+
@tokens << [:ARG_ARRAY_END]
|
470
|
+
|
471
|
+
pop_state
|
472
|
+
# if check_for_last(')')
|
473
|
+
# pop_state
|
474
|
+
# end
|
475
|
+
else
|
476
|
+
tokenize_shared_arguments
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
def tokenize_hash_arguments
|
481
|
+
if scanner.scan /\}/
|
482
|
+
@tokens << [:ARG_HASH_END]
|
483
|
+
|
484
|
+
pop_state
|
485
|
+
# if this is the last argument in the list, maybe get back to the field scope?
|
486
|
+
# if check_for_last(')')
|
487
|
+
# pop_state
|
488
|
+
# end
|
489
|
+
else
|
490
|
+
tokenize_shared_arguments
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
def pop_argument_state
|
495
|
+
if check_for_last(/\s*\)/)
|
496
|
+
end_arguments
|
497
|
+
else
|
498
|
+
pop_state unless %i[argument_defaults hash_arguments array_arguments special_args arguments].include?(state)
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
def tokenize_arguments
|
503
|
+
# pop argument state if arguments are finished
|
504
|
+
if scanner.scan %r{\)}
|
505
|
+
@tokens << [:END_ARGS]
|
506
|
+
|
507
|
+
pop_state
|
508
|
+
# something(argument: $argument = true)
|
509
|
+
elsif scanner.scan %r{=}
|
510
|
+
@tokens << [:DEFAULT_VALUE]
|
511
|
+
|
512
|
+
push_state :argument_defaults
|
513
|
+
# noop, should expect this, but not important
|
514
|
+
elsif scanner.scan %r{,}
|
515
|
+
nil
|
516
|
+
else
|
517
|
+
tokenize_shared_arguments
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
def tokenize_shared_arguments
|
425
522
|
if scanner.scan /^(\w+):/
|
426
523
|
@tokens << [:ARG_KEY, scanner[1]]
|
427
|
-
elsif scanner.scan
|
428
|
-
@tokens << [:
|
524
|
+
elsif scanner.scan %r[{]
|
525
|
+
@tokens << [:ARG_HASH]
|
526
|
+
|
429
527
|
push_state :hash_arguments
|
430
|
-
elsif scanner.scan /\
|
431
|
-
@tokens << [:
|
528
|
+
elsif scanner.scan /\[/
|
529
|
+
@tokens << [:ARG_ARRAY]
|
530
|
+
|
432
531
|
push_state :array_arguments
|
433
|
-
elsif scanner.scan
|
532
|
+
elsif scanner.scan %r{"(.*?)"}
|
434
533
|
@tokens << [:ARG_STRING_VALUE, scanner[1]]
|
435
|
-
|
534
|
+
|
535
|
+
pop_argument_state
|
536
|
+
elsif scanner.scan /(\d+\.\d+)/
|
436
537
|
@tokens << [:ARG_FLOAT_VALUE, scanner[1].to_f]
|
437
|
-
|
538
|
+
|
539
|
+
pop_argument_state
|
540
|
+
elsif scanner.scan /(\d+)/
|
438
541
|
@tokens << [:ARG_NUM_VALUE, scanner[1].to_i]
|
439
|
-
|
440
|
-
|
441
|
-
|
542
|
+
|
543
|
+
pop_argument_state
|
544
|
+
elsif scanner.scan /(true|false)/
|
545
|
+
@tokens << [:ARG_BOOL_VALUE, (scanner[1] == 'true')]
|
546
|
+
|
547
|
+
pop_argument_state
|
442
548
|
elsif scanner.scan /\$(\w+):/
|
443
549
|
@tokens << [:SPECIAL_ARG_KEY, scanner[1]]
|
550
|
+
|
444
551
|
push_state :special_args
|
445
552
|
elsif scanner.scan /\$(\w+)/
|
446
553
|
@tokens << [:SPECIAL_ARG_REF, scanner[1]]
|
447
|
-
else
|
448
|
-
advance
|
449
|
-
end
|
450
|
-
end
|
451
554
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
555
|
+
pop_argument_state
|
556
|
+
elsif scanner.scan /,/
|
557
|
+
# no-op
|
558
|
+
elsif check_for_last(/\s*\)/)
|
559
|
+
@tokens << [:END_DEFAULT_VALUE] if state == :argument_defaults
|
560
|
+
end_arguments
|
456
561
|
pop_state
|
457
562
|
else
|
458
|
-
|
563
|
+
advance
|
459
564
|
end
|
460
565
|
end
|
461
566
|
|
462
|
-
def
|
463
|
-
if scanner.
|
464
|
-
|
465
|
-
|
567
|
+
def tokenize_fields
|
568
|
+
if scanner.check %r[{]
|
569
|
+
tokenize_fieldset
|
570
|
+
# ... on Model - or - ... @directive
|
571
|
+
elsif scanner.scan %r{\.{3}\s}
|
572
|
+
@tokens << [:INLINE_FRAGMENT]
|
573
|
+
push_state :inline_fragment
|
574
|
+
# @directive
|
575
|
+
elsif scanner.scan %r{@(\w+)}
|
576
|
+
@tokens << [:DIRECTIVE, scanner[1]]
|
577
|
+
|
578
|
+
end_fieldset while check_for_last && state == :fieldset
|
579
|
+
# ...fragmentReference (check for last since it is a field literal)
|
580
|
+
elsif scanner.scan /\.{3}(\w+)/
|
581
|
+
@tokens << [:FRAGMENT_REF, scanner[1]]
|
582
|
+
|
583
|
+
end_fieldset while check_for_last && state == :fieldset
|
584
|
+
# alias:
|
585
|
+
elsif scanner.scan %r{(\w+):}
|
586
|
+
@tokens << [:ALIAS, scanner[1]]
|
587
|
+
# fieldLiteral
|
588
|
+
elsif scanner.scan %r{(\w+)}
|
589
|
+
@tokens << [:FIELD_NAME, scanner[1]]
|
590
|
+
|
591
|
+
end_fieldset while check_for_last && state == :fieldset
|
592
|
+
# (arguments: true)
|
593
|
+
elsif scanner.scan /^\s*\(/
|
594
|
+
@tokens << [:START_ARGS]
|
595
|
+
|
596
|
+
push_state :arguments
|
466
597
|
else
|
467
|
-
|
598
|
+
advance
|
468
599
|
end
|
469
600
|
end
|
470
601
|
|
471
|
-
def
|
472
|
-
if scanner.scan
|
473
|
-
@tokens << [:
|
474
|
-
|
602
|
+
def tokenize_fieldset
|
603
|
+
if scanner.scan %r[\s*{]
|
604
|
+
@tokens << [:FIELDSET]
|
605
|
+
|
606
|
+
push_state :fieldset
|
475
607
|
else
|
476
|
-
|
608
|
+
raise LexerError, "Expecting `{` got `#{scanner.peek(3)}`"
|
477
609
|
end
|
478
610
|
end
|
479
611
|
|
@@ -498,6 +630,10 @@ module Graphlyte
|
|
498
630
|
end
|
499
631
|
|
500
632
|
def advance
|
633
|
+
unless scanner.check /\s/
|
634
|
+
raise LexerError, "Unexpected Char: '#{scanner.peek(20)}'"
|
635
|
+
end
|
636
|
+
|
501
637
|
scanner.pos = scanner.pos + 1
|
502
638
|
end
|
503
639
|
|
data/lib/graphlyte/types.rb
CHANGED
@@ -2,8 +2,8 @@ require_relative "schema/types/base"
|
|
2
2
|
|
3
3
|
module Graphlyte
|
4
4
|
class Types
|
5
|
-
def method_missing(method, placeholder)
|
6
|
-
Schema::Types::Base.new(method, placeholder)
|
5
|
+
def method_missing(method, placeholder, default = nil)
|
6
|
+
Schema::Types::Base.new(method, placeholder, default)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
data/lib/graphlyte.rb
CHANGED
@@ -2,13 +2,14 @@ require 'json'
|
|
2
2
|
require_relative "./graphlyte/fieldset"
|
3
3
|
require_relative "./graphlyte/query"
|
4
4
|
require_relative "./graphlyte/fragment"
|
5
|
+
require_relative 'graphlyte/inline_fragment'
|
5
6
|
require_relative "./graphlyte/schema_query"
|
6
7
|
require_relative "./graphlyte/types"
|
7
8
|
require_relative "./graphlyte/schema/parser"
|
8
9
|
|
9
10
|
module Graphlyte
|
10
11
|
extend SchemaQuery
|
11
|
-
|
12
|
+
|
12
13
|
TYPES = Types.new
|
13
14
|
|
14
15
|
def self.parse(gql)
|
@@ -23,6 +24,18 @@ module Graphlyte
|
|
23
24
|
Query.new(name, :mutation, builder: build(&block))
|
24
25
|
end
|
25
26
|
|
27
|
+
def self.custom(name, type, &block)
|
28
|
+
Query.new(name, type.to_sym, builder: build(&block))
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.inline_fragment(model_name, &block)
|
32
|
+
InlineFragment.new(model_name, builder: build(&block))
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.inline_directive(directive, **hargs, &block)
|
36
|
+
InlineFragment.from_directive(directive, **hargs, builder: build(&block) )
|
37
|
+
end
|
38
|
+
|
26
39
|
def self.fragment(fragment_name, model_name, &block)
|
27
40
|
Fragment.new(fragment_name, model_name, builder: build(&block))
|
28
41
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphlyte
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Gregory
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -48,9 +48,11 @@ files:
|
|
48
48
|
- lib/graphlyte/arguments/set.rb
|
49
49
|
- lib/graphlyte/arguments/value.rb
|
50
50
|
- lib/graphlyte/builder.rb
|
51
|
+
- lib/graphlyte/directive.rb
|
51
52
|
- lib/graphlyte/field.rb
|
52
53
|
- lib/graphlyte/fieldset.rb
|
53
54
|
- lib/graphlyte/fragment.rb
|
55
|
+
- lib/graphlyte/inline_fragment.rb
|
54
56
|
- lib/graphlyte/query.rb
|
55
57
|
- lib/graphlyte/refinements/string_refinement.rb
|
56
58
|
- lib/graphlyte/schema/parser.rb
|