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