graphql-client 0.12.3 → 0.15.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 +5 -5
- data/README.md +24 -12
- data/lib/graphql/client/definition.rb +60 -21
- data/lib/graphql/client/erb.rb +50 -0
- data/lib/graphql/client/erubis.rb +2 -24
- data/lib/graphql/client/query_typename.rb +70 -19
- data/lib/graphql/client/railtie.rb +2 -2
- data/lib/graphql/client/schema/enum_type.rb +32 -8
- data/lib/graphql/client/schema/interface_type.rb +6 -4
- data/lib/graphql/client/schema/object_type.rb +125 -19
- data/lib/graphql/client/schema/possible_types.rb +1 -1
- data/lib/graphql/client/schema/scalar_type.rb +1 -1
- data/lib/graphql/client/schema/union_type.rb +6 -4
- data/lib/graphql/client/schema.rb +19 -19
- data/lib/graphql/client.rb +41 -25
- metadata +10 -23
- data/lib/graphql/language/nodes/deep_freeze_ext.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1f7ced5b5ef5fb8e496d8c221867165c4629670a5f8ecf1e44f8c523e5774ae2
|
4
|
+
data.tar.gz: 49453ec41a714dca5774c32b24139527beb7623a8c99a2368e789f9b1443c79a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b69902cbed392a6ad77d5e124db8196439b17750ecb369fc3189a93593857f5e1cf80ade820edb5abc0e11cade056832c733726e2e4202207496d7435eb6ebfa
|
7
|
+
data.tar.gz: a918d43558b2f75ce3627a7dd003ba76e083b9c087f15c36e4df7e571d5eef71a4760fe929a7d6d93eb78a22b4f310c8ef75f9737a719a516c659eb70117026e
|
data/README.md
CHANGED
@@ -1,21 +1,30 @@
|
|
1
|
-
# graphql-client
|
1
|
+
# graphql-client [](https://badge.fury.io/rb/graphql-client) [](https://travis-ci.org/github/graphql-client)
|
2
2
|
|
3
3
|
GraphQL Client is a Ruby library for declaring, composing and executing GraphQL queries.
|
4
4
|
|
5
5
|
## Usage
|
6
6
|
|
7
|
+
### Installation
|
8
|
+
|
9
|
+
Add `graphql-client` to your Gemfile and then run `bundle install`.
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
# Gemfile
|
13
|
+
gem 'graphql-client'
|
14
|
+
```
|
15
|
+
|
7
16
|
### Configuration
|
8
17
|
|
9
18
|
Sample configuration for a GraphQL Client to query from the [SWAPI GraphQL Wrapper](https://github.com/graphql/swapi-graphql).
|
10
19
|
|
11
|
-
```
|
20
|
+
```ruby
|
12
21
|
require "graphql/client"
|
13
22
|
require "graphql/client/http"
|
14
23
|
|
15
24
|
# Star Wars API example wrapper
|
16
25
|
module SWAPI
|
17
26
|
# Configure GraphQL endpoint using the basic HTTP network adapter.
|
18
|
-
HTTP = GraphQL::Client::HTTP.new("
|
27
|
+
HTTP = GraphQL::Client::HTTP.new("https://example.com/graphql") do
|
19
28
|
def headers(context)
|
20
29
|
# Optionally set any HTTP headers
|
21
30
|
{ "User-Agent": "My Client" }
|
@@ -42,7 +51,7 @@ If you haven't already, [familiarize yourself with the GraphQL query syntax](htt
|
|
42
51
|
|
43
52
|
This client library encourages all GraphQL queries to be declared statically and assigned to a Ruby constant.
|
44
53
|
|
45
|
-
```
|
54
|
+
```ruby
|
46
55
|
HeroNameQuery = SWAPI::Client.parse <<-'GRAPHQL'
|
47
56
|
query {
|
48
57
|
hero {
|
@@ -51,6 +60,7 @@ HeroNameQuery = SWAPI::Client.parse <<-'GRAPHQL'
|
|
51
60
|
}
|
52
61
|
GRAPHQL
|
53
62
|
```
|
63
|
+
|
54
64
|
Queries can reference variables that are passed in at query execution time.
|
55
65
|
|
56
66
|
```ruby
|
@@ -65,7 +75,7 @@ GRAPHQL
|
|
65
75
|
|
66
76
|
Fragments are declared similarly.
|
67
77
|
|
68
|
-
```
|
78
|
+
```ruby
|
69
79
|
HumanFragment = SWAPI::Client.parse <<-'GRAPHQL'
|
70
80
|
fragment on Human {
|
71
81
|
name
|
@@ -76,7 +86,7 @@ GRAPHQL
|
|
76
86
|
|
77
87
|
To include a fragment in a query, reference the fragment by constant.
|
78
88
|
|
79
|
-
```
|
89
|
+
```ruby
|
80
90
|
HeroNameQuery = SWAPI::Client.parse <<-'GRAPHQL'
|
81
91
|
{
|
82
92
|
luke: human(id: "1000") {
|
@@ -91,7 +101,7 @@ GRAPHQL
|
|
91
101
|
|
92
102
|
This works for namespaced constants.
|
93
103
|
|
94
|
-
```
|
104
|
+
```ruby
|
95
105
|
module Hero
|
96
106
|
Query = SWAPI::Client.parse <<-'GRAPHQL'
|
97
107
|
{
|
@@ -112,7 +122,7 @@ end
|
|
112
122
|
|
113
123
|
Pass the reference of a parsed query definition to `GraphQL::Client#query`. Data is returned back in a wrapped `GraphQL::Client::Schema::ObjectType` struct that provides Ruby-ish accessors.
|
114
124
|
|
115
|
-
```
|
125
|
+
```ruby
|
116
126
|
result = SWAPI::Client.query(Hero::Query)
|
117
127
|
|
118
128
|
# The raw data is Hash of JSON values
|
@@ -121,18 +131,20 @@ result = SWAPI::Client.query(Hero::Query)
|
|
121
131
|
# The wrapped result allows to you access data with Ruby methods
|
122
132
|
result.data.luke.home_planet
|
123
133
|
```
|
124
|
-
`GraphQL::Client#query` also accepts variables and context parameters that can be leveraged by the underlying network executor.
|
125
134
|
|
126
|
-
|
135
|
+
`GraphQL::Client#query` also accepts variables and context parameters that can be leveraged by the underlying network executor.
|
136
|
+
|
137
|
+
```ruby
|
127
138
|
result = SWAPI::Client.query(Hero::HeroFromEpisodeQuery, variables: {episode: "JEDI"}, context: {user_id: current_user_id})
|
128
139
|
```
|
140
|
+
|
129
141
|
### Rails ERB integration
|
130
142
|
|
131
143
|
If you're using Ruby on Rails ERB templates, theres a ERB extension that allows static queries to be defined in the template itself.
|
132
144
|
|
133
145
|
In standard Ruby you can simply assign queries and fragments to constants and they'll be available throughout the app. However, the contents of an ERB template is compiled into a Ruby method, and methods can't assign constants. So a new ERB tag was extended to declare static sections that include a GraphQL query.
|
134
146
|
|
135
|
-
```
|
147
|
+
```erb
|
136
148
|
<%# app/views/humans/human.html.erb %>
|
137
149
|
<%graphql
|
138
150
|
fragment HumanFragment on Human {
|
@@ -161,7 +173,7 @@ These `<%graphql` sections are simply ignored at runtime but make their definiti
|
|
161
173
|
|
162
174
|
Add `graphql-client` to your app's Gemfile:
|
163
175
|
|
164
|
-
```
|
176
|
+
```ruby
|
165
177
|
gem 'graphql-client'
|
166
178
|
```
|
167
179
|
|
@@ -14,39 +14,68 @@ module GraphQL
|
|
14
14
|
#
|
15
15
|
# Definitions MUST be assigned to a constant.
|
16
16
|
class Definition < Module
|
17
|
-
def self.for(
|
18
|
-
case
|
17
|
+
def self.for(ast_node:, **kargs)
|
18
|
+
case ast_node
|
19
19
|
when Language::Nodes::OperationDefinition
|
20
|
-
OperationDefinition.new(
|
20
|
+
OperationDefinition.new(ast_node: ast_node, **kargs)
|
21
21
|
when Language::Nodes::FragmentDefinition
|
22
|
-
FragmentDefinition.new(
|
22
|
+
FragmentDefinition.new(ast_node: ast_node, **kargs)
|
23
23
|
else
|
24
|
-
raise TypeError, "expected node to be a definition type, but was #{
|
24
|
+
raise TypeError, "expected node to be a definition type, but was #{ast_node.class}"
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def initialize(client:, document:,
|
28
|
+
def initialize(client:, document:, source_document:, ast_node:, source_location:)
|
29
29
|
@client = client
|
30
30
|
@document = document
|
31
|
-
@
|
31
|
+
@source_document = source_document
|
32
|
+
@definition_node = ast_node
|
32
33
|
@source_location = source_location
|
33
|
-
|
34
|
+
|
35
|
+
definition_type = case ast_node
|
36
|
+
when GraphQL::Language::Nodes::OperationDefinition
|
37
|
+
case ast_node.operation_type
|
38
|
+
when "mutation"
|
39
|
+
@client.schema.mutation
|
40
|
+
when "subscription"
|
41
|
+
@client.schema.subscription
|
42
|
+
when "query", nil
|
43
|
+
@client.schema.query
|
44
|
+
else
|
45
|
+
raise "Unexpected operation_type: #{ast_node.operation_type}"
|
46
|
+
end
|
47
|
+
when GraphQL::Language::Nodes::FragmentDefinition
|
48
|
+
@client.schema.types[ast_node.type.name]
|
49
|
+
else
|
50
|
+
raise "Unexpected ast_node: #{ast_node}"
|
51
|
+
end
|
52
|
+
|
53
|
+
@schema_class = client.types.define_class(self, [ast_node], definition_type)
|
54
|
+
|
55
|
+
# Clear cache only needed during initialization
|
56
|
+
@indexes = nil
|
34
57
|
end
|
35
58
|
|
36
59
|
# Internal: Get associated owner GraphQL::Client instance.
|
37
60
|
attr_reader :client
|
38
61
|
|
39
|
-
# Internal root schema class for
|
62
|
+
# Internal root schema class for definition. Returns
|
40
63
|
# GraphQL::Client::Schema::ObjectType or
|
41
64
|
# GraphQL::Client::Schema::PossibleTypes.
|
42
65
|
attr_reader :schema_class
|
43
66
|
|
44
|
-
# Internal: Get underlying operation or fragment
|
67
|
+
# Internal: Get underlying operation or fragment definition AST node for
|
45
68
|
# definition.
|
46
69
|
#
|
47
70
|
# Returns OperationDefinition or FragmentDefinition object.
|
48
71
|
attr_reader :definition_node
|
49
72
|
|
73
|
+
# Internal: Get original document that created this definition, without
|
74
|
+
# any additional dependencies.
|
75
|
+
#
|
76
|
+
# Returns GraphQL::Language::Nodes::Document.
|
77
|
+
attr_reader :source_document
|
78
|
+
|
50
79
|
# Public: Global name of definition in client document.
|
51
80
|
#
|
52
81
|
# Returns a GraphQL safe name of the Ruby constant String.
|
@@ -82,9 +111,9 @@ module GraphQL
|
|
82
111
|
when GraphQL::Client::Schema::PossibleTypes
|
83
112
|
case obj
|
84
113
|
when NilClass
|
85
|
-
|
114
|
+
obj
|
86
115
|
else
|
87
|
-
|
116
|
+
cast_object(obj)
|
88
117
|
end
|
89
118
|
when GraphQL::Client::Schema::ObjectType
|
90
119
|
case obj
|
@@ -93,14 +122,7 @@ module GraphQL
|
|
93
122
|
when Hash
|
94
123
|
schema_class.new(obj, errors)
|
95
124
|
else
|
96
|
-
|
97
|
-
unless obj.class._spreads.include?(definition_node.name)
|
98
|
-
raise TypeError, "#{definition_node.name} is not included in #{obj.class.source_definition.name}"
|
99
|
-
end
|
100
|
-
schema_class.cast(obj.to_h, obj.errors)
|
101
|
-
else
|
102
|
-
raise TypeError, "unexpected #{obj.class}"
|
103
|
-
end
|
125
|
+
cast_object(obj)
|
104
126
|
end
|
105
127
|
else
|
106
128
|
raise TypeError, "unexpected #{schema_class}"
|
@@ -119,9 +141,26 @@ module GraphQL
|
|
119
141
|
end
|
120
142
|
|
121
143
|
private
|
144
|
+
|
145
|
+
def cast_object(obj)
|
146
|
+
if obj.class.is_a?(GraphQL::Client::Schema::ObjectType)
|
147
|
+
unless obj.class._spreads.include?(definition_node.name)
|
148
|
+
raise TypeError, "#{definition_node.name} is not included in #{obj.class.source_definition.name}"
|
149
|
+
end
|
150
|
+
schema_class.cast(obj.to_h, obj.errors)
|
151
|
+
else
|
152
|
+
raise TypeError, "unexpected #{obj.class}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
EMPTY_SET = Set.new.freeze
|
157
|
+
|
122
158
|
def index_spreads(visitor)
|
123
159
|
spreads = {}
|
124
|
-
on_node = ->(node, _parent)
|
160
|
+
on_node = ->(node, _parent) do
|
161
|
+
node_spreads = flatten_spreads(node).map(&:name)
|
162
|
+
spreads[node] = node_spreads.empty? ? EMPTY_SET : Set.new(node_spreads).freeze
|
163
|
+
end
|
125
164
|
|
126
165
|
visitor[GraphQL::Language::Nodes::Field] << on_node
|
127
166
|
visitor[GraphQL::Language::Nodes::FragmentDefinition] << on_node
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "action_view"
|
3
|
+
|
4
|
+
module GraphQL
|
5
|
+
class Client
|
6
|
+
begin
|
7
|
+
require "action_view/template/handlers/erb/erubi"
|
8
|
+
rescue LoadError
|
9
|
+
require "graphql/client/erubis_enhancer"
|
10
|
+
|
11
|
+
# Public: Extended Erubis implementation that supports GraphQL static
|
12
|
+
# query sections.
|
13
|
+
#
|
14
|
+
# <%graphql
|
15
|
+
# query GetVersion {
|
16
|
+
# version
|
17
|
+
# }
|
18
|
+
# %>
|
19
|
+
# <%= data.version %>
|
20
|
+
#
|
21
|
+
# Configure ActionView's default ERB implementation to use this class.
|
22
|
+
#
|
23
|
+
# ActionView::Template::Handlers::ERB.erb_implementation = GraphQL::Client::Erubis
|
24
|
+
#
|
25
|
+
class ERB < ActionView::Template::Handlers::Erubis
|
26
|
+
include ErubisEnhancer
|
27
|
+
end
|
28
|
+
else
|
29
|
+
require "graphql/client/erubi_enhancer"
|
30
|
+
|
31
|
+
# Public: Extended Erubis implementation that supports GraphQL static
|
32
|
+
# query sections.
|
33
|
+
#
|
34
|
+
# <%graphql
|
35
|
+
# query GetVerison {
|
36
|
+
# version
|
37
|
+
# }
|
38
|
+
# %>
|
39
|
+
# <%= data.version %>
|
40
|
+
#
|
41
|
+
# Configure ActionView's default ERB implementation to use this class.
|
42
|
+
#
|
43
|
+
# ActionView::Template::Handlers::ERB.erb_implementation = GraphQL::Client::Erubi
|
44
|
+
#
|
45
|
+
class ERB < ActionView::Template::Handlers::ERB::Erubi
|
46
|
+
include ErubiEnhancer
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,30 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "
|
3
|
-
require "graphql/client/erubis_enhancer"
|
2
|
+
require "graphql/client/erb"
|
4
3
|
|
5
4
|
module GraphQL
|
6
5
|
class Client
|
7
|
-
|
8
|
-
ActiveSupport::Deprecation.silence do
|
9
|
-
ActionView::Template::Handlers::Erubis
|
10
|
-
end
|
11
|
-
|
12
|
-
# Public: Extended Erubis implementation that supports GraphQL static
|
13
|
-
# query sections.
|
14
|
-
#
|
15
|
-
# <%graphql
|
16
|
-
# query GetVersion {
|
17
|
-
# version
|
18
|
-
# }
|
19
|
-
# %>
|
20
|
-
# <%= data.version %>
|
21
|
-
#
|
22
|
-
# Configure ActionView's default ERB implementation to use this class.
|
23
|
-
#
|
24
|
-
# ActionView::Template::Handlers::ERB.erb_implementation = GraphQL::Client::Erubis
|
25
|
-
#
|
26
|
-
class Erubis < ActionView::Template::Handlers::Erubis
|
27
|
-
include ErubisEnhancer
|
28
|
-
end
|
6
|
+
Erubis = GraphQL::Client::ERB
|
29
7
|
end
|
30
8
|
end
|
@@ -13,33 +13,84 @@ module GraphQL
|
|
13
13
|
# document - GraphQL::Language::Nodes::Document to modify
|
14
14
|
# schema - Optional Map of GraphQL::Language::Nodes::Node to GraphQL::Type
|
15
15
|
#
|
16
|
-
# Returns
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
# Returns the document with `__typename` added to it
|
17
|
+
if GraphQL::Language::Nodes::AbstractNode.method_defined?(:merge)
|
18
|
+
# GraphQL 1.9 introduces a new visitor class
|
19
|
+
# and doesn't expose writer methods for node attributes.
|
20
|
+
# So, use the node mutation API instead.
|
21
|
+
class InsertTypenameVisitor < GraphQL::Language::Visitor
|
22
|
+
def initialize(document, types:)
|
23
|
+
@types = types
|
24
|
+
super(document)
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_typename(node, parent)
|
28
|
+
type = @types[node]
|
29
|
+
type = type && type.unwrap
|
30
|
+
|
31
|
+
if (node.selections.any? && (type.nil? || type.is_a?(GraphQL::InterfaceType) || type.is_a?(GraphQL::UnionType))) ||
|
32
|
+
(node.selections.none? && type.is_a?(GraphQL::ObjectType))
|
33
|
+
names = QueryTypename.node_flatten_selections(node.selections).map { |s| s.respond_to?(:name) ? s.name : nil }
|
25
34
|
names = Set.new(names.compact)
|
26
35
|
|
27
|
-
|
28
|
-
node
|
36
|
+
if names.include?("__typename")
|
37
|
+
yield(node, parent)
|
38
|
+
else
|
39
|
+
node_with_typename = node.merge(selections: [GraphQL::Language::Nodes::Field.new(name: "__typename")] + node.selections)
|
40
|
+
yield(node_with_typename, parent)
|
29
41
|
end
|
42
|
+
else
|
43
|
+
yield(node, parent)
|
30
44
|
end
|
31
|
-
|
32
|
-
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_operation_definition(node, parent)
|
48
|
+
add_typename(node, parent) { |n, p| super(n, p) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_field(node, parent)
|
52
|
+
add_typename(node, parent) { |n, p| super(n, p) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def on_fragment_definition(node, parent)
|
56
|
+
add_typename(node, parent) { |n, p| super(n, p) }
|
33
57
|
end
|
34
58
|
end
|
35
59
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
60
|
+
def self.insert_typename_fields(document, types: {})
|
61
|
+
visitor = InsertTypenameVisitor.new(document, types: types)
|
62
|
+
visitor.visit
|
63
|
+
visitor.result
|
64
|
+
end
|
65
|
+
|
66
|
+
else
|
67
|
+
def self.insert_typename_fields(document, types: {})
|
68
|
+
on_selections = ->(node, _parent) do
|
69
|
+
type = types[node]
|
70
|
+
|
71
|
+
if node.selections.any?
|
72
|
+
case type && type.unwrap
|
73
|
+
when NilClass, GraphQL::InterfaceType, GraphQL::UnionType
|
74
|
+
names = node_flatten_selections(node.selections).map { |s| s.respond_to?(:name) ? s.name : nil }
|
75
|
+
names = Set.new(names.compact)
|
41
76
|
|
42
|
-
|
77
|
+
unless names.include?("__typename")
|
78
|
+
node.selections = [GraphQL::Language::Nodes::Field.new(name: "__typename")] + node.selections
|
79
|
+
end
|
80
|
+
end
|
81
|
+
elsif type && type.unwrap.is_a?(GraphQL::ObjectType)
|
82
|
+
node.selections = [GraphQL::Language::Nodes::Field.new(name: "__typename")]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
visitor = GraphQL::Language::Visitor.new(document)
|
87
|
+
visitor[GraphQL::Language::Nodes::Field].leave << on_selections
|
88
|
+
visitor[GraphQL::Language::Nodes::FragmentDefinition].leave << on_selections
|
89
|
+
visitor[GraphQL::Language::Nodes::OperationDefinition].leave << on_selections
|
90
|
+
visitor.visit
|
91
|
+
|
92
|
+
document
|
93
|
+
end
|
43
94
|
end
|
44
95
|
|
45
96
|
def self.node_flatten_selections(selections)
|
@@ -22,8 +22,8 @@ module GraphQL
|
|
22
22
|
end
|
23
23
|
|
24
24
|
initializer "graphql.configure_erb_implementation" do |_app|
|
25
|
-
require "graphql/client/
|
26
|
-
ActionView::Template::Handlers::ERB.erb_implementation = GraphQL::Client::
|
25
|
+
require "graphql/client/erb"
|
26
|
+
ActionView::Template::Handlers::ERB.erb_implementation = GraphQL::Client::ERB
|
27
27
|
end
|
28
28
|
|
29
29
|
initializer "graphql.configure_views_namespace" do |app|
|
@@ -9,6 +9,34 @@ module GraphQL
|
|
9
9
|
class EnumType < Module
|
10
10
|
include BaseType
|
11
11
|
|
12
|
+
class EnumValue < String
|
13
|
+
def initialize(obj, enum_value, enum)
|
14
|
+
super(obj)
|
15
|
+
@enum_value = enum_value
|
16
|
+
@enum = enum
|
17
|
+
end
|
18
|
+
|
19
|
+
def respond_to_missing?(method_name, include_private = false)
|
20
|
+
if method_name[-1] == "?" && @enum.include?(method_name[0..-2])
|
21
|
+
true
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing(method_name, *args)
|
28
|
+
if method_name[-1] == "?"
|
29
|
+
queried_value = method_name[0..-2]
|
30
|
+
if @enum.include?(queried_value)
|
31
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0)" unless args.empty?
|
32
|
+
return @enum_value == queried_value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
12
40
|
# Internal: Construct enum wrapper from another GraphQL::EnumType.
|
13
41
|
#
|
14
42
|
# type - GraphQL::EnumType instance
|
@@ -21,22 +49,18 @@ module GraphQL
|
|
21
49
|
@values = {}
|
22
50
|
|
23
51
|
all_values = type.values.keys
|
52
|
+
comparison_set = all_values.map { |s| -s.downcase }.to_set
|
24
53
|
|
25
54
|
all_values.each do |value|
|
26
|
-
str = value.
|
27
|
-
all_values.each do |v|
|
28
|
-
str.define_singleton_method("#{v.downcase}?") { false }
|
29
|
-
end
|
30
|
-
str.define_singleton_method("#{value.downcase}?") { true }
|
31
|
-
str.freeze
|
55
|
+
str = EnumValue.new(-value, -value.downcase, comparison_set).freeze
|
32
56
|
const_set(value, str) if value =~ /^[A-Z]/
|
33
|
-
@values[str] = str
|
57
|
+
@values[str.to_s] = str
|
34
58
|
end
|
35
59
|
|
36
60
|
@values.freeze
|
37
61
|
end
|
38
62
|
|
39
|
-
def define_class(definition,
|
63
|
+
def define_class(definition, ast_nodes)
|
40
64
|
self
|
41
65
|
end
|
42
66
|
|
@@ -20,10 +20,12 @@ module GraphQL
|
|
20
20
|
PossibleTypes.new(type, types)
|
21
21
|
end
|
22
22
|
|
23
|
-
def define_class(definition,
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
def define_class(definition, ast_nodes)
|
24
|
+
possible_type_names = definition.client.schema.possible_types(type).map(&:graphql_name)
|
25
|
+
possible_types = possible_type_names.map { |concrete_type_name|
|
26
|
+
schema_module.get_class(concrete_type_name).define_class(definition, ast_nodes)
|
27
|
+
}
|
28
|
+
new(possible_types)
|
27
29
|
end
|
28
30
|
end
|
29
31
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
2
|
require "active_support/inflector"
|
4
3
|
require "graphql/client/error"
|
5
4
|
require "graphql/client/errors"
|
@@ -20,34 +19,83 @@ module GraphQL
|
|
20
19
|
end
|
21
20
|
end
|
22
21
|
|
23
|
-
def define_class(definition,
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
def define_class(definition, ast_nodes)
|
23
|
+
# First, gather all the ast nodes representing a certain selection, by name.
|
24
|
+
# We gather AST nodes into arrays so that multiple selections can be grouped, for example:
|
25
|
+
#
|
26
|
+
# {
|
27
|
+
# f1 { a b }
|
28
|
+
# f1 { b c }
|
29
|
+
# }
|
30
|
+
#
|
31
|
+
# should be treated like `f1 { a b c }`
|
32
|
+
field_nodes = {}
|
33
|
+
ast_nodes.each do |ast_node|
|
34
|
+
ast_node.selections.each do |selected_ast_node|
|
35
|
+
gather_selections(field_nodes, definition, selected_ast_node)
|
27
36
|
end
|
28
|
-
|
29
|
-
}
|
37
|
+
end
|
30
38
|
|
31
|
-
|
32
|
-
|
39
|
+
# After gathering all the nodes by name, prepare to create methods and classes for them.
|
40
|
+
field_classes = {}
|
41
|
+
field_nodes.each do |result_name, field_ast_nodes|
|
42
|
+
# `result_name` might be an alias, so make sure to get the proper name
|
43
|
+
field_name = field_ast_nodes.first.name
|
44
|
+
field_definition = definition.client.schema.get_field(type.name, field_name)
|
45
|
+
field_return_type = field_definition.type
|
46
|
+
field_classes[result_name.to_sym] = schema_module.define_class(definition, field_ast_nodes, field_return_type)
|
47
|
+
end
|
33
48
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
49
|
+
klass = Class.new(self)
|
50
|
+
klass.define_fields(field_classes)
|
51
|
+
klass.instance_variable_set(:@source_definition, definition)
|
52
|
+
klass.instance_variable_set(:@_spreads, definition.indexes[:spreads][ast_nodes.first])
|
53
|
+
|
54
|
+
if definition.client.enforce_collocated_callers
|
55
|
+
keys = field_classes.keys.map { |key| ActiveSupport::Inflector.underscore(key) }
|
56
|
+
Client.enforce_collocated_callers(klass, keys, definition.source_location[0])
|
57
|
+
end
|
58
|
+
|
59
|
+
klass
|
60
|
+
end
|
38
61
|
|
39
|
-
|
40
|
-
|
41
|
-
|
62
|
+
PREDICATE_CACHE = Hash.new { |h, name|
|
63
|
+
h[name] = -> { @data[name] ? true : false }
|
64
|
+
}
|
65
|
+
|
66
|
+
METHOD_CACHE = Hash.new { |h, key|
|
67
|
+
h[key] = -> {
|
68
|
+
name = key.to_s
|
69
|
+
type = self.class::FIELDS[key]
|
70
|
+
@casted_data.fetch(name) do
|
71
|
+
@casted_data[name] = type.cast(@data[name], @errors.filter_by_path(name))
|
42
72
|
end
|
73
|
+
}
|
74
|
+
}
|
43
75
|
|
44
|
-
|
45
|
-
|
76
|
+
MODULE_CACHE = Hash.new do |h, fields|
|
77
|
+
h[fields] = Module.new do
|
78
|
+
fields.each do |name|
|
79
|
+
GraphQL::Client::Schema::ObjectType.define_cached_field(name, self)
|
80
|
+
end
|
46
81
|
end
|
47
82
|
end
|
48
83
|
|
84
|
+
FIELDS_CACHE = Hash.new { |h, k| h[k] = k }
|
85
|
+
|
49
86
|
def define_fields(fields)
|
50
|
-
|
87
|
+
const_set :FIELDS, FIELDS_CACHE[fields]
|
88
|
+
mod = MODULE_CACHE[fields.keys.sort]
|
89
|
+
include mod
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.define_cached_field(name, ctx)
|
93
|
+
key = name
|
94
|
+
name = -name.to_s
|
95
|
+
method_name = ActiveSupport::Inflector.underscore(name)
|
96
|
+
|
97
|
+
ctx.send(:define_method, method_name, &METHOD_CACHE[key])
|
98
|
+
ctx.send(:define_method, "#{method_name}?", &PREDICATE_CACHE[name])
|
51
99
|
end
|
52
100
|
|
53
101
|
def define_field(name, type)
|
@@ -75,9 +123,67 @@ module GraphQL
|
|
75
123
|
raise InvariantError, "expected value to be a Hash, but was #{value.class}"
|
76
124
|
end
|
77
125
|
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Given an AST selection on this object, gather it into `fields` if it applies.
|
130
|
+
# If it's a fragment, continue recursively checking the selections on the fragment.
|
131
|
+
def gather_selections(fields, definition, selected_ast_node)
|
132
|
+
case selected_ast_node
|
133
|
+
when GraphQL::Language::Nodes::InlineFragment
|
134
|
+
continue_selection = if selected_ast_node.type.nil?
|
135
|
+
true
|
136
|
+
else
|
137
|
+
schema = definition.client.schema
|
138
|
+
type_condition = schema.types[selected_ast_node.type.name]
|
139
|
+
applicable_types = schema.possible_types(type_condition)
|
140
|
+
# continue if this object type is one of the types matching the fragment condition
|
141
|
+
applicable_types.include?(type)
|
142
|
+
end
|
143
|
+
|
144
|
+
if continue_selection
|
145
|
+
selected_ast_node.selections.each do |next_selected_ast_node|
|
146
|
+
gather_selections(fields, definition, next_selected_ast_node)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
150
|
+
fragment_definition = definition.document.definitions.find do |defn|
|
151
|
+
defn.is_a?(GraphQL::Language::Nodes::FragmentDefinition) && defn.name == selected_ast_node.name
|
152
|
+
end
|
153
|
+
|
154
|
+
schema = definition.client.schema
|
155
|
+
type_condition = schema.types[fragment_definition.type.name]
|
156
|
+
applicable_types = schema.possible_types(type_condition)
|
157
|
+
# continue if this object type is one of the types matching the fragment condition
|
158
|
+
continue_selection = applicable_types.include?(type)
|
159
|
+
|
160
|
+
if continue_selection
|
161
|
+
fragment_definition.selections.each do |next_selected_ast_node|
|
162
|
+
gather_selections(fields, definition, next_selected_ast_node)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
when GraphQL::Language::Nodes::Field
|
166
|
+
operation_definition_for_field = definition.indexes[:definitions][selected_ast_node]
|
167
|
+
# Ignore fields defined in other documents.
|
168
|
+
if definition.source_document.definitions.include?(operation_definition_for_field)
|
169
|
+
field_method_name = selected_ast_node.alias || selected_ast_node.name
|
170
|
+
ast_nodes = fields[field_method_name] ||= []
|
171
|
+
ast_nodes << selected_ast_node
|
172
|
+
end
|
173
|
+
else
|
174
|
+
raise "Unexpected selection node: #{selected_ast_node}"
|
175
|
+
end
|
176
|
+
end
|
78
177
|
end
|
79
178
|
|
80
179
|
class ObjectClass
|
180
|
+
module ClassMethods
|
181
|
+
attr_reader :source_definition
|
182
|
+
attr_reader :_spreads
|
183
|
+
end
|
184
|
+
|
185
|
+
extend ClassMethods
|
186
|
+
|
81
187
|
def initialize(data = {}, errors = Errors.new)
|
82
188
|
@data = data
|
83
189
|
@casted_data = {}
|
@@ -41,7 +41,7 @@ module GraphQL
|
|
41
41
|
if type = possible_types[typename]
|
42
42
|
type.cast(value, errors)
|
43
43
|
else
|
44
|
-
raise InvariantError, "expected value to be one of (#{possible_types.keys.join(", ")}), but was #{typename}"
|
44
|
+
raise InvariantError, "expected value to be one of (#{possible_types.keys.join(", ")}), but was #{typename.inspect}"
|
45
45
|
end
|
46
46
|
when NilClass
|
47
47
|
nil
|
@@ -20,10 +20,12 @@ module GraphQL
|
|
20
20
|
PossibleTypes.new(type, types)
|
21
21
|
end
|
22
22
|
|
23
|
-
def define_class(definition,
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
def define_class(definition, ast_nodes)
|
24
|
+
possible_type_names = definition.client.schema.possible_types(type).map(&:graphql_name)
|
25
|
+
possible_types = possible_type_names.map { |concrete_type_name|
|
26
|
+
schema_module.get_class(concrete_type_name).define_class(definition, ast_nodes)
|
27
|
+
}
|
28
|
+
new(possible_types)
|
27
29
|
end
|
28
30
|
end
|
29
31
|
end
|
@@ -15,23 +15,25 @@ module GraphQL
|
|
15
15
|
class Client
|
16
16
|
module Schema
|
17
17
|
module ClassMethods
|
18
|
-
def define_class(definition,
|
19
|
-
|
18
|
+
def define_class(definition, ast_nodes, type)
|
19
|
+
type_class = case type
|
20
20
|
when GraphQL::NonNullType
|
21
|
-
define_class(definition,
|
21
|
+
define_class(definition, ast_nodes, type.of_type).to_non_null_type
|
22
22
|
when GraphQL::ListType
|
23
|
-
define_class(definition,
|
23
|
+
define_class(definition, ast_nodes, type.of_type).to_list_type
|
24
24
|
else
|
25
|
-
get_class(type.name).define_class(definition,
|
25
|
+
get_class(type.name).define_class(definition, ast_nodes)
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
directive.
|
31
|
-
|
32
|
-
|
28
|
+
ast_nodes.each do |ast_node|
|
29
|
+
ast_node.directives.each do |directive|
|
30
|
+
if directive = self.directives[directive.name.to_sym]
|
31
|
+
type_class = directive.new(type_class)
|
32
|
+
end
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
type_class
|
35
37
|
end
|
36
38
|
|
37
39
|
def get_class(type_name)
|
@@ -50,6 +52,13 @@ module GraphQL
|
|
50
52
|
const_set(class_name, klass)
|
51
53
|
end
|
52
54
|
|
55
|
+
DIRECTIVES = { include: IncludeDirective,
|
56
|
+
skip: SkipDirective }.freeze
|
57
|
+
|
58
|
+
def directives
|
59
|
+
DIRECTIVES
|
60
|
+
end
|
61
|
+
|
53
62
|
private
|
54
63
|
|
55
64
|
def normalize_type_name(type_name)
|
@@ -61,10 +70,6 @@ module GraphQL
|
|
61
70
|
mod = Module.new
|
62
71
|
mod.extend ClassMethods
|
63
72
|
|
64
|
-
mod.define_singleton_method :schema do
|
65
|
-
schema
|
66
|
-
end
|
67
|
-
|
68
73
|
cache = {}
|
69
74
|
schema.types.each do |name, type|
|
70
75
|
next if name.start_with?("__")
|
@@ -74,11 +79,6 @@ module GraphQL
|
|
74
79
|
end
|
75
80
|
end
|
76
81
|
|
77
|
-
directives = {}
|
78
|
-
mod.define_singleton_method(:directives) { directives }
|
79
|
-
directives[:include] = IncludeDirective
|
80
|
-
directives[:skip] = SkipDirective
|
81
|
-
|
82
82
|
mod
|
83
83
|
end
|
84
84
|
|
data/lib/graphql/client.rb
CHANGED
@@ -12,8 +12,8 @@ require "graphql/client/operation_definition"
|
|
12
12
|
require "graphql/client/query_typename"
|
13
13
|
require "graphql/client/response"
|
14
14
|
require "graphql/client/schema"
|
15
|
-
require "graphql/language/nodes/deep_freeze_ext"
|
16
15
|
require "json"
|
16
|
+
require "delegate"
|
17
17
|
|
18
18
|
module GraphQL
|
19
19
|
# GraphQL Client helps build and execute queries against a GraphQL backend.
|
@@ -67,9 +67,9 @@ module GraphQL
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
IntrospectionDocument = GraphQL.parse(GraphQL::Introspection::INTROSPECTION_QUERY)
|
70
|
+
IntrospectionDocument = GraphQL.parse(GraphQL::Introspection::INTROSPECTION_QUERY)
|
71
71
|
|
72
|
-
def self.dump_schema(schema, io = nil)
|
72
|
+
def self.dump_schema(schema, io = nil, context: {})
|
73
73
|
unless schema.respond_to?(:execute)
|
74
74
|
raise TypeError, "expected schema to respond to #execute(), but was #{schema.class}"
|
75
75
|
end
|
@@ -78,7 +78,7 @@ module GraphQL
|
|
78
78
|
document: IntrospectionDocument,
|
79
79
|
operation_name: "IntrospectionQuery",
|
80
80
|
variables: {},
|
81
|
-
context:
|
81
|
+
context: context
|
82
82
|
).to_h
|
83
83
|
|
84
84
|
if io
|
@@ -120,13 +120,21 @@ module GraphQL
|
|
120
120
|
|
121
121
|
definition_dependencies = Set.new
|
122
122
|
|
123
|
+
# Replace Ruby constant reference with GraphQL fragment names,
|
124
|
+
# while populating `definition_dependencies` with
|
125
|
+
# GraphQL Fragment ASTs which this operation depends on
|
123
126
|
str = str.gsub(/\.\.\.([a-zA-Z0-9_]+(::[a-zA-Z0-9_]+)*)/) do
|
124
127
|
match = Regexp.last_match
|
125
128
|
const_name = match[1]
|
126
129
|
|
127
130
|
if str.match(/fragment\s*#{const_name}/)
|
131
|
+
# It's a fragment _definition_, not a fragment usage
|
128
132
|
match[0]
|
129
133
|
else
|
134
|
+
# It's a fragment spread, so we should load the fragment
|
135
|
+
# which corresponds to the spread.
|
136
|
+
# We depend on ActiveSupport to either find the already-loaded
|
137
|
+
# constant, or to load the constant by name
|
130
138
|
begin
|
131
139
|
fragment = ActiveSupport::Inflector.constantize(const_name)
|
132
140
|
rescue NameError
|
@@ -135,6 +143,10 @@ module GraphQL
|
|
135
143
|
|
136
144
|
case fragment
|
137
145
|
when FragmentDefinition
|
146
|
+
# We found the fragment definition that this fragment spread belongs to.
|
147
|
+
# So, register the AST of this fragment in `definition_dependencies`
|
148
|
+
# and update the query string to valid GraphQL syntax,
|
149
|
+
# replacing the Ruby constant
|
138
150
|
definition_dependencies.merge(fragment.document.definitions)
|
139
151
|
"...#{fragment.definition_name}"
|
140
152
|
else
|
@@ -157,10 +169,17 @@ module GraphQL
|
|
157
169
|
doc = GraphQL.parse(str)
|
158
170
|
|
159
171
|
document_types = DocumentTypes.analyze_types(self.schema, doc).freeze
|
160
|
-
QueryTypename.insert_typename_fields(doc, types: document_types)
|
172
|
+
doc = QueryTypename.insert_typename_fields(doc, types: document_types)
|
161
173
|
|
162
174
|
doc.definitions.each do |node|
|
163
|
-
node.name
|
175
|
+
if node.name.nil?
|
176
|
+
if node.respond_to?(:merge) # GraphQL 1.9 +
|
177
|
+
node_with_name = node.merge(name: "__anonymous__")
|
178
|
+
doc = doc.replace_child(node, node_with_name)
|
179
|
+
else
|
180
|
+
node.name = "__anonymous__"
|
181
|
+
end
|
182
|
+
end
|
164
183
|
end
|
165
184
|
|
166
185
|
document_dependencies = Language::Nodes::Document.new(definitions: doc.definitions + definition_dependencies.to_a)
|
@@ -183,39 +202,34 @@ module GraphQL
|
|
183
202
|
|
184
203
|
definitions = {}
|
185
204
|
doc.definitions.each do |node|
|
186
|
-
irep_node = case node
|
187
|
-
when GraphQL::Language::Nodes::OperationDefinition
|
188
|
-
errors[:irep].operation_definitions[node.name]
|
189
|
-
when GraphQL::Language::Nodes::FragmentDefinition
|
190
|
-
errors[:irep].fragment_definitions[node.name]
|
191
|
-
else
|
192
|
-
raise TypeError, "unexpected #{node.class}"
|
193
|
-
end
|
194
|
-
|
195
|
-
node.name = nil if node.name == "__anonymous__"
|
196
205
|
sliced_document = Language::DefinitionSlice.slice(document_dependencies, node.name)
|
197
206
|
definition = Definition.for(
|
198
207
|
client: self,
|
199
|
-
|
208
|
+
ast_node: node,
|
200
209
|
document: sliced_document,
|
210
|
+
source_document: doc,
|
201
211
|
source_location: source_location
|
202
212
|
)
|
203
213
|
definitions[node.name] = definition
|
204
214
|
end
|
205
215
|
|
206
216
|
name_hook = RenameNodeHook.new(definitions)
|
207
|
-
visitor = Language::Visitor.new(
|
217
|
+
visitor = Language::Visitor.new(document_dependencies)
|
208
218
|
visitor[Language::Nodes::FragmentDefinition].leave << name_hook.method(:rename_node)
|
209
219
|
visitor[Language::Nodes::OperationDefinition].leave << name_hook.method(:rename_node)
|
210
220
|
visitor[Language::Nodes::FragmentSpread].leave << name_hook.method(:rename_node)
|
211
221
|
visitor.visit
|
212
222
|
|
213
|
-
|
214
|
-
|
215
|
-
|
223
|
+
if document_tracking_enabled
|
224
|
+
if @document.respond_to?(:merge) # GraphQL 1.9+
|
225
|
+
@document = @document.merge(definitions: @document.definitions + doc.definitions)
|
226
|
+
else
|
227
|
+
@document.definitions.concat(doc.definitions)
|
228
|
+
end
|
229
|
+
end
|
216
230
|
|
217
|
-
if definitions[
|
218
|
-
definitions[
|
231
|
+
if definitions["__anonymous__"]
|
232
|
+
definitions["__anonymous__"]
|
219
233
|
else
|
220
234
|
Module.new do
|
221
235
|
definitions.each do |name, definition|
|
@@ -234,7 +248,7 @@ module GraphQL
|
|
234
248
|
definition = @definitions[node.name]
|
235
249
|
if definition
|
236
250
|
node.extend(LazyName)
|
237
|
-
node.
|
251
|
+
node._definition = definition
|
238
252
|
end
|
239
253
|
end
|
240
254
|
end
|
@@ -357,8 +371,10 @@ module GraphQL
|
|
357
371
|
# name to point to a lazily defined Proc instead of a static string.
|
358
372
|
module LazyName
|
359
373
|
def name
|
360
|
-
@
|
374
|
+
@_definition.definition_name
|
361
375
|
end
|
376
|
+
|
377
|
+
attr_writer :_definition
|
362
378
|
end
|
363
379
|
|
364
380
|
private
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -17,9 +17,6 @@ dependencies:
|
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '3.0'
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '6.0'
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,23 +24,20 @@ dependencies:
|
|
27
24
|
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '3.0'
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '6.0'
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: graphql
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
36
30
|
requirements:
|
37
31
|
- - "~>"
|
38
32
|
- !ruby/object:Gem::Version
|
39
|
-
version: '1.
|
33
|
+
version: '1.8'
|
40
34
|
type: :runtime
|
41
35
|
prerelease: false
|
42
36
|
version_requirements: !ruby/object:Gem::Requirement
|
43
37
|
requirements:
|
44
38
|
- - "~>"
|
45
39
|
- !ruby/object:Gem::Version
|
46
|
-
version: '1.
|
40
|
+
version: '1.8'
|
47
41
|
- !ruby/object:Gem::Dependency
|
48
42
|
name: actionpack
|
49
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -51,9 +45,6 @@ dependencies:
|
|
51
45
|
- - ">="
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: 3.2.22
|
54
|
-
- - "<"
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
version: '6.0'
|
57
48
|
type: :development
|
58
49
|
prerelease: false
|
59
50
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -61,9 +52,6 @@ dependencies:
|
|
61
52
|
- - ">="
|
62
53
|
- !ruby/object:Gem::Version
|
63
54
|
version: 3.2.22
|
64
|
-
- - "<"
|
65
|
-
- !ruby/object:Gem::Version
|
66
|
-
version: '6.0'
|
67
55
|
- !ruby/object:Gem::Dependency
|
68
56
|
name: erubi
|
69
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -126,28 +114,28 @@ dependencies:
|
|
126
114
|
requirements:
|
127
115
|
- - "~>"
|
128
116
|
- !ruby/object:Gem::Version
|
129
|
-
version: '0.
|
117
|
+
version: '0.10'
|
130
118
|
type: :development
|
131
119
|
prerelease: false
|
132
120
|
version_requirements: !ruby/object:Gem::Requirement
|
133
121
|
requirements:
|
134
122
|
- - "~>"
|
135
123
|
- !ruby/object:Gem::Version
|
136
|
-
version: '0.
|
124
|
+
version: '0.10'
|
137
125
|
- !ruby/object:Gem::Dependency
|
138
126
|
name: rubocop
|
139
127
|
requirement: !ruby/object:Gem::Requirement
|
140
128
|
requirements:
|
141
129
|
- - "~>"
|
142
130
|
- !ruby/object:Gem::Version
|
143
|
-
version: '0.
|
131
|
+
version: '0.55'
|
144
132
|
type: :development
|
145
133
|
prerelease: false
|
146
134
|
version_requirements: !ruby/object:Gem::Requirement
|
147
135
|
requirements:
|
148
136
|
- - "~>"
|
149
137
|
- !ruby/object:Gem::Version
|
150
|
-
version: '0.
|
138
|
+
version: '0.55'
|
151
139
|
description: A Ruby library for declaring, composing and executing GraphQL queries
|
152
140
|
email: engineering@github.com
|
153
141
|
executables: []
|
@@ -161,6 +149,7 @@ files:
|
|
161
149
|
- lib/graphql/client/definition.rb
|
162
150
|
- lib/graphql/client/definition_variables.rb
|
163
151
|
- lib/graphql/client/document_types.rb
|
152
|
+
- lib/graphql/client/erb.rb
|
164
153
|
- lib/graphql/client/error.rb
|
165
154
|
- lib/graphql/client/errors.rb
|
166
155
|
- lib/graphql/client/erubi_enhancer.rb
|
@@ -188,7 +177,6 @@ files:
|
|
188
177
|
- lib/graphql/client/schema/skip_directive.rb
|
189
178
|
- lib/graphql/client/schema/union_type.rb
|
190
179
|
- lib/graphql/client/view_module.rb
|
191
|
-
- lib/graphql/language/nodes/deep_freeze_ext.rb
|
192
180
|
- lib/rubocop/cop/graphql/heredoc.rb
|
193
181
|
- lib/rubocop/cop/graphql/overfetch.rb
|
194
182
|
homepage: https://github.com/github/graphql-client
|
@@ -210,8 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
210
198
|
- !ruby/object:Gem::Version
|
211
199
|
version: '0'
|
212
200
|
requirements: []
|
213
|
-
|
214
|
-
rubygems_version: 2.6.11
|
201
|
+
rubygems_version: 3.0.3
|
215
202
|
signing_key:
|
216
203
|
specification_version: 4
|
217
204
|
summary: GraphQL Client
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "graphql"
|
3
|
-
|
4
|
-
module GraphQL
|
5
|
-
module Language
|
6
|
-
module Nodes
|
7
|
-
# :nodoc:
|
8
|
-
class AbstractNode
|
9
|
-
# Public: Freeze entire Node tree
|
10
|
-
#
|
11
|
-
# Returns self Node.
|
12
|
-
def deep_freeze
|
13
|
-
children.each(&:deep_freeze)
|
14
|
-
scalars.each { |s| s && s.freeze }
|
15
|
-
freeze
|
16
|
-
self
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|