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