Skip to content

Commit ee4c065

Browse files
author
Robert Mosolgo
authored
Merge pull request #1304 from cjoudrey/amp-delimited-implements
Separate multiple inherited interfaces with `&`
2 parents 5e28ac1 + 7423866 commit ee4c065

8 files changed

Lines changed: 855 additions & 712 deletions

File tree

lib/graphql/language/lexer.rb

Lines changed: 65 additions & 51 deletions
Large diffs are not rendered by default.

lib/graphql/language/lexer.rl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
EQUALS = '=';
4646
BANG = '!';
4747
PIPE = '|';
48+
AMP = '&';
4849

4950
QUOTED_STRING = QUOTE STRING_CHAR* QUOTE;
5051
BLOCK_STRING = BLOCK_QUOTE BLOCK_STRING_CHAR* BLOCK_QUOTE;
@@ -86,6 +87,7 @@
8687
EQUALS => { emit(:EQUALS, ts, te, meta) };
8788
BANG => { emit(:BANG, ts, te, meta) };
8889
PIPE => { emit(:PIPE, ts, te, meta) };
90+
AMP => { emit(:AMP, ts, te, meta) };
8991
IDENTIFIER => { emit(:IDENTIFIER, ts, te, meta) };
9092
COMMENT => { record_comment(ts, te, meta) };
9193

lib/graphql/language/parser.rb

Lines changed: 699 additions & 652 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/graphql/language/parser.y

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,6 @@ rule
152152
| operation_type
153153
| schema_keyword
154154

155-
name_list:
156-
name { return [make_node(:TypeName, name: val[0])] }
157-
| name_list name { val[0] << make_node(:TypeName, name: val[1]) }
158-
159155
enum_value_definition:
160156
enum_name directives_list_opt { return make_node(:EnumValueDefinition, name: val[0], directives: val[1], description: get_description(val[0])) }
161157

@@ -306,7 +302,17 @@ rule
306302

307303
implements_opt:
308304
/* none */ { return [] }
309-
| IMPLEMENTS name_list { return val[1] }
305+
| IMPLEMENTS AMP interfaces_list { return val[2] }
306+
| IMPLEMENTS interfaces_list { return val[1] }
307+
| IMPLEMENTS legacy_interfaces_list { return val[1] }
308+
309+
interfaces_list:
310+
name { return [make_node(:TypeName, name: val[0])] }
311+
| interfaces_list AMP name { val[0] << make_node(:TypeName, name: val[2]) }
312+
313+
legacy_interfaces_list:
314+
name { return [make_node(:TypeName, name: val[0])] }
315+
| legacy_interfaces_list name { val[0] << make_node(:TypeName, name: val[1]) }
310316

311317
input_value_definition:
312318
name COLON type default_value_opt directives_list_opt {

lib/graphql/language/printer.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def print_scalar_type_definition(scalar_type)
154154
def print_object_type_definition(object_type)
155155
out = print_description(object_type)
156156
out << "type #{object_type.name}"
157-
out << " implements " << object_type.interfaces.map(&:name).join(", ") unless object_type.interfaces.empty?
157+
out << " implements " << object_type.interfaces.map(&:name).join(" & ") unless object_type.interfaces.empty?
158158
out << print_directives(object_type.directives)
159159
out << print_field_definitions(object_type.fields)
160160
end

spec/graphql/language/parser_spec.rb

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,80 @@
4444
assert_equal schema_string, document.to_query_string
4545
end
4646

47+
describe "implements" do
48+
it "parses when there are no interfaces" do
49+
schema = "
50+
type A {
51+
a: String
52+
}
53+
"
54+
55+
document = subject.parse(schema)
56+
57+
assert_equal [], document.definitions[0].interfaces.map(&:name)
58+
end
59+
60+
it "parses with leading ampersand" do
61+
schema = "
62+
type A implements & B {
63+
a: String
64+
}
65+
"
66+
67+
document = subject.parse(schema)
68+
69+
assert_equal ["B"], document.definitions[0].interfaces.map(&:name)
70+
end
71+
72+
it "parses with leading ampersand and multiple interfaces" do
73+
schema = "
74+
type A implements & B & C {
75+
a: String
76+
}
77+
"
78+
79+
document = subject.parse(schema)
80+
81+
assert_equal ["B", "C"], document.definitions[0].interfaces.map(&:name)
82+
end
83+
84+
it "parses without leading ampersand" do
85+
schema = "
86+
type A implements B {
87+
a: String
88+
}
89+
"
90+
91+
document = subject.parse(schema)
92+
93+
assert_equal ["B"], document.definitions[0].interfaces.map(&:name)
94+
end
95+
96+
it "parses without leading ampersand and multiple interfaces" do
97+
schema = "
98+
type A implements B & C {
99+
a: String
100+
}
101+
"
102+
103+
document = subject.parse(schema)
104+
105+
assert_equal ["B", "C"], document.definitions[0].interfaces.map(&:name)
106+
end
107+
108+
it "supports the old way of parsing multiple interfaces for backwards compatibility" do
109+
schema = "
110+
type A implements B, C {
111+
a: String
112+
}
113+
"
114+
115+
document = subject.parse(schema)
116+
117+
assert_equal ["B", "C"], document.definitions[0].interfaces.map(&:name)
118+
end
119+
end
120+
47121
describe ".parse_file" do
48122
it "assigns filename to all nodes" do
49123
example_filename = "spec/support/parser/filename_example.graphql"

spec/graphql/language/printer_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
# Union description
116116
union AnnotatedUnion @onUnion = A | B
117117
118-
type Foo implements Bar {
118+
type Foo implements Bar & AnnotatedInterface {
119119
one: Type
120120
two(argument: InputType!): Type
121121
three(argument: InputType, other: String): Int

spec/graphql/static_validation/rules/fields_will_merge_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@
515515
scalar: String!
516516
}
517517
518-
type NonNullStringBox1Impl implements SomeBox, NonNullStringBox1 {
518+
type NonNullStringBox1Impl implements SomeBox & NonNullStringBox1 {
519519
scalar: String!
520520
unrelatedField: String
521521
deepBox: SomeBox
@@ -525,7 +525,7 @@
525525
scalar: String!
526526
}
527527
528-
type NonNullStringBox2Impl implements SomeBox, NonNullStringBox2 {
528+
type NonNullStringBox2Impl implements SomeBox & NonNullStringBox2 {
529529
scalar: String!
530530
unrelatedField: String
531531
deepBox: SomeBox

0 commit comments

Comments
 (0)