graphlyte 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|