tinygql 0.1.3 → 0.2.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/bin/bench.rb +38 -0
- data/lib/tinygql/lexer.rb +118 -40
- data/lib/tinygql/nodes.rb +255 -86
- data/lib/tinygql/nodes.rb.erb +15 -2
- data/lib/tinygql/nodes.yml +34 -4
- data/lib/tinygql/parser.rb +156 -50
- data/lib/tinygql/version.rb +1 -1
- data/lib/tinygql/visitors.rb +261 -304
- data/lib/tinygql/visitors.rb.erb +8 -20
- data/test/lexer_test.rb +78 -0
- data/test/parser_test.rb +68 -0
- data/test/schema-extensions.graphql +48 -0
- metadata +4 -2
data/lib/tinygql/visitors.rb.erb
CHANGED
@@ -1,46 +1,34 @@
|
|
1
1
|
module TinyGQL
|
2
2
|
module Visitors
|
3
3
|
module Visitor
|
4
|
-
|
4
|
+
<% nodes.each do |node| %>
|
5
5
|
def handle_<%= node.human_name %> obj
|
6
6
|
<%- node.fields.find_all(&:visitable?).each do |field| -%>
|
7
|
-
<%- if field.nullable? -%>
|
8
|
-
if obj.<%= field.name %>
|
9
|
-
<%- end -%>
|
10
7
|
<%- if field.list? -%>
|
11
|
-
|
8
|
+
obj.<%= field.name %>.each { |v| v.accept self }<% if field.nullable? %> if obj.<%= field.name %><% end %>
|
12
9
|
<%- end -%>
|
13
10
|
<%- if field.node? -%>
|
14
|
-
|
15
|
-
<%- end -%>
|
16
|
-
<%- if field.nullable? -%>
|
17
|
-
end
|
11
|
+
obj.<%= field.name %>.accept(self)<% if field.nullable? %> if obj.<%= field.name %><% end %>
|
18
12
|
<%- end -%>
|
19
13
|
<%- end -%>
|
20
14
|
end
|
21
|
-
|
15
|
+
<% end %>
|
22
16
|
end
|
23
17
|
|
24
18
|
module Fold
|
25
|
-
|
19
|
+
<% nodes.each do |node| %>
|
26
20
|
def handle_<%= node.human_name %> obj, seed
|
27
21
|
<%- node.fields.find_all(&:visitable?).each do |field| -%>
|
28
|
-
<%- if field.nullable? -%>
|
29
|
-
if obj.<%= field.name %>
|
30
|
-
<%- end -%>
|
31
22
|
<%- if field.list? -%>
|
32
|
-
|
23
|
+
obj.<%= field.name %>.each { |v| seed = v.fold(self, seed) }<% if field.nullable? %> if obj.<%= field.name %><% end %>
|
33
24
|
<%- end -%>
|
34
25
|
<%- if field.node? -%>
|
35
|
-
|
36
|
-
<%- end -%>
|
37
|
-
<%- if field.nullable? -%>
|
38
|
-
end
|
26
|
+
seed = obj.<%= field.name %>.fold(self, seed)<% if field.nullable? %> if obj.<%= field.name %><% end %>
|
39
27
|
<%- end -%>
|
40
28
|
<%- end -%>
|
41
29
|
seed
|
42
30
|
end
|
43
|
-
|
31
|
+
<% end %>
|
44
32
|
end
|
45
33
|
end
|
46
34
|
end
|
data/test/lexer_test.rb
CHANGED
@@ -101,5 +101,83 @@ eod
|
|
101
101
|
[:RCURLY, "}"],
|
102
102
|
[:RCURLY, "}"]], toks
|
103
103
|
end
|
104
|
+
|
105
|
+
def test_lex_4
|
106
|
+
words = ["true", "null", "enum", "type"]
|
107
|
+
doc = words.join(" ")
|
108
|
+
|
109
|
+
lexer = Lexer.new doc
|
110
|
+
toks = []
|
111
|
+
while tok = lexer.next_token
|
112
|
+
toks << tok
|
113
|
+
end
|
114
|
+
|
115
|
+
assert_equal words.map { |x| [x.upcase.to_sym, x] }, toks
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_lex_5
|
119
|
+
words = ["input", "false", "query", "union"]
|
120
|
+
doc = words.join(" ")
|
121
|
+
|
122
|
+
lexer = Lexer.new doc
|
123
|
+
toks = []
|
124
|
+
while tok = lexer.next_token
|
125
|
+
toks << tok
|
126
|
+
end
|
127
|
+
|
128
|
+
assert_equal words.map { |x| [x.upcase.to_sym, x] }, toks
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_lex_6
|
132
|
+
words = ["extend", "scalar", "schema"]
|
133
|
+
doc = words.join(" ")
|
134
|
+
|
135
|
+
lexer = Lexer.new doc
|
136
|
+
toks = []
|
137
|
+
while tok = lexer.next_token
|
138
|
+
toks << tok
|
139
|
+
end
|
140
|
+
|
141
|
+
assert_equal words.map { |x| [x.upcase.to_sym, x] }, toks
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_lex_8
|
145
|
+
words = ["mutation", "fragment"]
|
146
|
+
doc = words.join(" ")
|
147
|
+
|
148
|
+
lexer = Lexer.new doc
|
149
|
+
toks = []
|
150
|
+
while tok = lexer.next_token
|
151
|
+
toks << tok
|
152
|
+
end
|
153
|
+
|
154
|
+
assert_equal words.map { |x| [x.upcase.to_sym, x] }, toks
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_lex_9
|
158
|
+
words = ["interface", "directive"]
|
159
|
+
doc = words.join(" ")
|
160
|
+
|
161
|
+
lexer = Lexer.new doc
|
162
|
+
toks = []
|
163
|
+
while tok = lexer.next_token
|
164
|
+
toks << tok
|
165
|
+
end
|
166
|
+
|
167
|
+
assert_equal words.map { |x| [x.upcase.to_sym, x] }, toks
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_kw_lex
|
171
|
+
words = ["on", "fragment", "true", "false", "null", "query", "mutation", "subscription", "schema", "scalar", "type", "extend", "implements", "interface", "union", "enum", "input", "directive", "repeatable"]
|
172
|
+
doc = words.join(" ")
|
173
|
+
|
174
|
+
lexer = Lexer.new doc
|
175
|
+
toks = []
|
176
|
+
while tok = lexer.next_token
|
177
|
+
toks << tok
|
178
|
+
end
|
179
|
+
|
180
|
+
assert_equal words.map { |x| [x.upcase.to_sym, x] }, toks
|
181
|
+
end
|
104
182
|
end
|
105
183
|
end
|
data/test/parser_test.rb
CHANGED
@@ -3,6 +3,13 @@ require "tinygql"
|
|
3
3
|
|
4
4
|
module TinyGQL
|
5
5
|
class ParserTest < Test
|
6
|
+
def test_homogeneous_ast
|
7
|
+
%w{ kitchen-sink.graphql schema-extensions.graphql schema-kitchen-sink.graphql }.each do |f|
|
8
|
+
ast = Parser.parse File.read(File.join(__dir__, f))
|
9
|
+
assert ast.all? { |x| x.is_a?(TinyGQL::Nodes::Node) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
6
13
|
def test_multi_tok
|
7
14
|
doc = <<-eod
|
8
15
|
mutation aaron($neat: Int = 123) @foo(lol: { lon: 456 }) {
|
@@ -39,6 +46,23 @@ eod
|
|
39
46
|
assert_equal ["likeStory", "story", "likeCount"], ast.find_all(&:field?).map(&:name)
|
40
47
|
end
|
41
48
|
|
49
|
+
def test_has_position_and_line
|
50
|
+
doc = <<-eod
|
51
|
+
mutation {
|
52
|
+
likeStory(sturyID: 12345) {
|
53
|
+
story {
|
54
|
+
likeCount
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
eod
|
59
|
+
parser = Parser.new doc
|
60
|
+
ast = parser.parse
|
61
|
+
expected = ["likeStory", "story", "likeCount"].map { |str| doc.index(str) + str.bytesize }
|
62
|
+
assert_equal expected, ast.find_all(&:field?).map(&:pos)
|
63
|
+
assert_equal [2, 3, 4], ast.find_all(&:field?).map { |n| n.line(doc) }
|
64
|
+
end
|
65
|
+
|
42
66
|
def test_field_alias
|
43
67
|
doc = <<-eod
|
44
68
|
mutation {
|
@@ -187,5 +211,49 @@ directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
|
187
211
|
assert_equal "neat!", node.description.value
|
188
212
|
assert_equal ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"], node.directive_locations.map(&:name)
|
189
213
|
end
|
214
|
+
|
215
|
+
def test_scalar_schema_extensions
|
216
|
+
ast = Parser.parse File.read(File.join(__dir__, "schema-extensions.graphql"))
|
217
|
+
node = ast.find { |x| x.scalar_type_extension? && x.name == "PositiveInt" }
|
218
|
+
assert node
|
219
|
+
assert_equal 2, node.directives.length
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_scalar_schema_extensions_no_directives
|
223
|
+
ast = Parser.parse File.read(File.join(__dir__, "schema-extensions.graphql"))
|
224
|
+
node = ast.find { |x| x.scalar_type_extension? && x.name == "Aaron" }
|
225
|
+
assert node
|
226
|
+
assert_nil node.directives
|
227
|
+
end
|
228
|
+
|
229
|
+
def test_interface_extension
|
230
|
+
ast = Parser.parse File.read(File.join(__dir__, "schema-extensions.graphql"))
|
231
|
+
node = ast.find { |x| x.interface_type_extension? && x.name == "NamedEntity" }
|
232
|
+
assert node
|
233
|
+
assert_nil node.directives
|
234
|
+
assert node.fields_definition
|
235
|
+
end
|
236
|
+
|
237
|
+
def test_union_extension
|
238
|
+
ast = Parser.parse File.read(File.join(__dir__, "schema-extensions.graphql"))
|
239
|
+
node = ast.find { |x| x.union_type_extension? && x.name == "Cool" }
|
240
|
+
assert node
|
241
|
+
assert_equal 1, node.directives.length
|
242
|
+
assert_equal "foo", node.directives.first.name
|
243
|
+
end
|
244
|
+
|
245
|
+
def test_enum_extension
|
246
|
+
ast = Parser.parse File.read(File.join(__dir__, "schema-extensions.graphql"))
|
247
|
+
assert ast.find { |x| x.enum_type_extension? && x.name == "Direction" }
|
248
|
+
assert ast.find { |x| x.enum_type_extension? && x.name == "AnnotatedEnum" }
|
249
|
+
assert ast.find { |x| x.enum_type_extension? && x.name == "Neat" }
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_input_extension
|
253
|
+
ast = Parser.parse File.read(File.join(__dir__, "schema-extensions.graphql"))
|
254
|
+
assert ast.find { |x| x.input_object_type_extension? && x.name == "InputType" }
|
255
|
+
assert ast.find { |x| x.input_object_type_extension? && x.name == "AnnotatedInput" }
|
256
|
+
assert ast.find { |x| x.input_object_type_extension? && x.name == "NeatInput" }
|
257
|
+
end
|
190
258
|
end
|
191
259
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
extend scalar PositiveInt
|
2
|
+
@serializationType(name: "global::System.Int32")
|
3
|
+
@runtimeType(name: "global::System.Int32")
|
4
|
+
|
5
|
+
extend scalar Aaron
|
6
|
+
|
7
|
+
extend interface NamedEntity {
|
8
|
+
nickname: String
|
9
|
+
}
|
10
|
+
|
11
|
+
extend type Person {
|
12
|
+
nickname: String
|
13
|
+
}
|
14
|
+
|
15
|
+
extend type Business {
|
16
|
+
nickname: String
|
17
|
+
}
|
18
|
+
|
19
|
+
extend interface NamedEntity @addedDirective
|
20
|
+
|
21
|
+
extend union Cool @foo
|
22
|
+
|
23
|
+
extend union Great @onUnion = A | B
|
24
|
+
|
25
|
+
extend enum Direction {
|
26
|
+
NORTH
|
27
|
+
EAST
|
28
|
+
SOUTH
|
29
|
+
WEST
|
30
|
+
}
|
31
|
+
|
32
|
+
extend enum AnnotatedEnum @onEnum {
|
33
|
+
ANNOTATED_VALUE @onEnumValue
|
34
|
+
OTHER_VALUE
|
35
|
+
}
|
36
|
+
|
37
|
+
extend enum Neat @onEnum
|
38
|
+
|
39
|
+
extend input InputType {
|
40
|
+
key: String!
|
41
|
+
answer: Int = 42
|
42
|
+
}
|
43
|
+
|
44
|
+
extend input AnnotatedInput @onInputObjectType {
|
45
|
+
annotatedField: Type @onField
|
46
|
+
}
|
47
|
+
|
48
|
+
extend input NeatInput @onInputObjectType
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tinygql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Patterson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08-
|
11
|
+
date: 2023-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -67,6 +67,7 @@ files:
|
|
67
67
|
- test/kitchen-sink.graphql
|
68
68
|
- test/lexer_test.rb
|
69
69
|
- test/parser_test.rb
|
70
|
+
- test/schema-extensions.graphql
|
70
71
|
- test/schema-kitchen-sink.graphql
|
71
72
|
- tinygql.gemspec
|
72
73
|
homepage: https://github.com/tenderlove/tinygql
|
@@ -97,4 +98,5 @@ test_files:
|
|
97
98
|
- test/kitchen-sink.graphql
|
98
99
|
- test/lexer_test.rb
|
99
100
|
- test/parser_test.rb
|
101
|
+
- test/schema-extensions.graphql
|
100
102
|
- test/schema-kitchen-sink.graphql
|