graphql 0.18.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/graphql/define.rb +19 -1
- data/lib/graphql/define/instance_definable.rb +60 -5
- data/lib/graphql/relay.rb +1 -0
- data/lib/graphql/relay/array_connection.rb +2 -5
- data/lib/graphql/relay/base_connection.rb +43 -33
- data/lib/graphql/relay/connection_field.rb +8 -21
- data/lib/graphql/relay/connection_resolve.rb +17 -0
- data/lib/graphql/relay/relation_connection.rb +2 -8
- data/lib/graphql/version.rb +1 -1
- data/readme.md +0 -7
- data/spec/graphql/analysis/analyze_query_spec.rb +40 -0
- data/spec/graphql/base_type_spec.rb +4 -0
- data/spec/graphql/define/instance_definable_spec.rb +8 -1
- data/spec/graphql/field_spec.rb +7 -1
- data/spec/support/dairy_app.rb +6 -0
- data/spec/support/star_wars_schema.rb +2 -2
- metadata +3 -3
- data/lib/graphql/define/assignment_dictionary.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4320e19052975ac0ba38fac054d7d56d044657e1
|
4
|
+
data.tar.gz: 2045f552c2acd454a57f4c0ec80f9547f7c6b875
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0db74452c453b954039a685aaa25ad875325de13c0f7eb152dd4ff09a139048b73c83ce637cc84b7824ffb4311ce1de0041b671444a4a208131bb49ed71c6dc9
|
7
|
+
data.tar.gz: bf2b4faf5545b8d13ee5ab3b370e9ea4ab4954e40f458a87b2551bf666e97e4d27270b618b2644b1cdc56f48d7681b48edf3ac74171ce9cc37d113a6bff5d7d6
|
data/lib/graphql/define.rb
CHANGED
@@ -3,8 +3,26 @@ require "graphql/define/assign_connection"
|
|
3
3
|
require "graphql/define/assign_enum_value"
|
4
4
|
require "graphql/define/assign_global_id_field"
|
5
5
|
require "graphql/define/assign_object_field"
|
6
|
-
require "graphql/define/assignment_dictionary"
|
7
6
|
require "graphql/define/defined_object_proxy"
|
8
7
|
require "graphql/define/instance_definable"
|
9
8
|
require "graphql/define/non_null_with_bang"
|
10
9
|
require "graphql/define/type_definer"
|
10
|
+
|
11
|
+
module GraphQL
|
12
|
+
module Define
|
13
|
+
# A helper for definitions that store their value in `#metadata`.
|
14
|
+
#
|
15
|
+
# @example Storing application classes with GraphQL types
|
16
|
+
# # Make a custom definition
|
17
|
+
# GraphQL::ObjectType.accepts_definitions(resolves_to_class_names: GraphQL::Define.assign_metadata_key(:resolves_to_class_names))
|
18
|
+
#
|
19
|
+
# # After definition, read the key from metadata
|
20
|
+
# PostType.metadata[:resolves_to_class_names] # => [...]
|
21
|
+
#
|
22
|
+
# @param [Object] the key to assign in metadata
|
23
|
+
# @return [#call(defn, value)] an assignment for `.accepts_definitions` which writes `key` to `#metadata`
|
24
|
+
def self.assign_metadata_key(key)
|
25
|
+
GraphQL::Define::InstanceDefinable::AssignMetadataKey.new(key)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,22 +1,36 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Define
|
3
|
-
# This module provides the `.define { ... }` API for
|
3
|
+
# This module provides the `.define { ... }` API for
|
4
|
+
# {GraphQL::BaseType}, {GraphQL::Field} and others.
|
5
|
+
#
|
6
|
+
# Calling `.accepts_definitions(...)` creates:
|
7
|
+
#
|
8
|
+
# - a keyword to the `.define` method
|
9
|
+
# - a helper method in the `.define { ... }` block
|
10
|
+
#
|
11
|
+
# The `.define { ... }` block will be called lazily. To be sure it has been
|
12
|
+
# called, use the private method `#ensure_defined`. That will call the
|
13
|
+
# definition block if it hasn't been called already.
|
4
14
|
#
|
5
15
|
# The goals are:
|
16
|
+
#
|
6
17
|
# - Minimal overhead in consuming classes
|
7
18
|
# - Independence between consuming classes
|
8
19
|
# - Extendable by third-party libraries without monkey-patching or other nastiness
|
9
20
|
#
|
10
21
|
# @example Make a class definable
|
11
22
|
# class Car
|
12
|
-
# attr_accessor :make, :model
|
13
|
-
#
|
23
|
+
# attr_accessor :make, :model
|
14
24
|
# accepts_definitions(
|
15
25
|
# # These attrs will be defined with plain setters, `{attr}=`
|
16
26
|
# :make, :model,
|
17
27
|
# # This attr has a custom definition which applies the config to the target
|
18
28
|
# doors: -> (car, doors_count) { doors_count.times { car.doors << Door.new } }
|
19
29
|
# )
|
30
|
+
#
|
31
|
+
# def initialize
|
32
|
+
# @doors = []
|
33
|
+
# end
|
20
34
|
# end
|
21
35
|
#
|
22
36
|
# # Create an instance with `.define`:
|
@@ -31,7 +45,7 @@ module GraphQL
|
|
31
45
|
#
|
32
46
|
# @example Extending the definition of a class
|
33
47
|
# # Add some definitions:
|
34
|
-
# Car.accepts_definitions(:all_wheel_drive)
|
48
|
+
# Car.accepts_definitions(all_wheel_drive: GraphQL::Define.assign_metadata_key(:all_wheel_drive))
|
35
49
|
#
|
36
50
|
# # Use it in a definition
|
37
51
|
# subaru_baja = Car.define do
|
@@ -39,6 +53,9 @@ module GraphQL
|
|
39
53
|
# all_wheel_drive true
|
40
54
|
# end
|
41
55
|
#
|
56
|
+
# # Access it from metadata
|
57
|
+
# subaru_baja.metadata[:all_wheel_drive] # => true
|
58
|
+
#
|
42
59
|
module InstanceDefinable
|
43
60
|
def self.included(base)
|
44
61
|
base.extend(ClassMethods)
|
@@ -50,6 +67,14 @@ module GraphQL
|
|
50
67
|
@definition_proc = defn_block
|
51
68
|
end
|
52
69
|
|
70
|
+
# `metadata` can store arbitrary key-values with an object.
|
71
|
+
#
|
72
|
+
# @return [Hash<Object, Object>] Hash for user-defined storage
|
73
|
+
def metadata
|
74
|
+
ensure_defined
|
75
|
+
@metadata ||= {}
|
76
|
+
end
|
77
|
+
|
53
78
|
private
|
54
79
|
|
55
80
|
# Run the definition block if it hasn't been run yet.
|
@@ -91,7 +116,17 @@ module GraphQL
|
|
91
116
|
# Each symbol in `accepts` will be assigned with `{key}=`.
|
92
117
|
# The last entry in accepts may be a hash of name-proc pairs for custom definitions.
|
93
118
|
def accepts_definitions(*accepts)
|
94
|
-
|
119
|
+
new_assignments = if accepts.last.is_a?(Hash)
|
120
|
+
accepts.pop.dup
|
121
|
+
else
|
122
|
+
{}
|
123
|
+
end
|
124
|
+
|
125
|
+
accepts.each do |key|
|
126
|
+
new_assignments[key] = AssignAttribute.new(key)
|
127
|
+
end
|
128
|
+
|
129
|
+
@own_dictionary = own_dictionary.merge(new_assignments)
|
95
130
|
end
|
96
131
|
|
97
132
|
# Define a reader and writer for each of `attr_names` which
|
@@ -125,6 +160,26 @@ module GraphQL
|
|
125
160
|
@own_dictionary ||= {}
|
126
161
|
end
|
127
162
|
end
|
163
|
+
|
164
|
+
class AssignMetadataKey
|
165
|
+
def initialize(key)
|
166
|
+
@key = key
|
167
|
+
end
|
168
|
+
|
169
|
+
def call(defn, value)
|
170
|
+
defn.metadata[@key] = value
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class AssignAttribute
|
175
|
+
def initialize(attr_name)
|
176
|
+
@attr_assign_method = :"#{attr_name}="
|
177
|
+
end
|
178
|
+
|
179
|
+
def call(defn, value)
|
180
|
+
defn.public_send(@attr_assign_method, value)
|
181
|
+
end
|
182
|
+
end
|
128
183
|
end
|
129
184
|
end
|
130
185
|
end
|
data/lib/graphql/relay.rb
CHANGED
@@ -18,7 +18,7 @@ module GraphQL
|
|
18
18
|
|
19
19
|
# apply first / last limit results
|
20
20
|
def paged_nodes
|
21
|
-
@paged_nodes
|
21
|
+
@paged_nodes ||= begin
|
22
22
|
items = sliced_nodes
|
23
23
|
|
24
24
|
if limit
|
@@ -31,10 +31,7 @@ module GraphQL
|
|
31
31
|
|
32
32
|
# Apply cursors to edges
|
33
33
|
def sliced_nodes
|
34
|
-
@sliced_nodes ||=
|
35
|
-
items = object
|
36
|
-
items[starting_offset..-1]
|
37
|
-
end
|
34
|
+
@sliced_nodes ||= nodes[starting_offset..-1]
|
38
35
|
end
|
39
36
|
|
40
37
|
def index_from_cursor(cursor)
|
@@ -6,11 +6,13 @@ module GraphQL
|
|
6
6
|
# - {#paged_nodes}, which applies `first` & `last` limits
|
7
7
|
#
|
8
8
|
# In a subclass, you have access to
|
9
|
-
# - {#
|
9
|
+
# - {#nodes}, the collection which the connection will wrap
|
10
10
|
# - {#first}, {#after}, {#last}, {#before} (arguments passed to the field)
|
11
11
|
# - {#max_page_size} (the specified maximum page size that can be returned from a connection)
|
12
12
|
#
|
13
13
|
class BaseConnection
|
14
|
+
extend Gem::Deprecate
|
15
|
+
|
14
16
|
# Just to encode data in the cursor, use something that won't conflict
|
15
17
|
CURSOR_SEPARATOR = "---"
|
16
18
|
|
@@ -18,49 +20,57 @@ module GraphQL
|
|
18
20
|
# eg {"Array" => ArrayConnection}
|
19
21
|
CONNECTION_IMPLEMENTATIONS = {}
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
#
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
23
|
+
class << self
|
24
|
+
extend Gem::Deprecate
|
25
|
+
|
26
|
+
# Find a connection implementation suitable for exposing `nodes`
|
27
|
+
#
|
28
|
+
# @param [Object] A collection of nodes (eg, Array, AR::Relation)
|
29
|
+
# @return [subclass of BaseConnection] a connection Class for wrapping `nodes`
|
30
|
+
def connection_for_nodes(nodes)
|
31
|
+
# Check for class _names_ because classes can be redefined in Rails development
|
32
|
+
ancestor_names = nodes.class.ancestors.map(&:name)
|
33
|
+
implementation = CONNECTION_IMPLEMENTATIONS.find do |nodes_class_name, connection_class|
|
34
|
+
ancestor_names.include? nodes_class_name
|
35
|
+
end
|
36
|
+
if implementation.nil?
|
37
|
+
raise("No connection implementation to wrap #{nodes.class} (#{nodes})")
|
38
|
+
else
|
39
|
+
implementation[1]
|
40
|
+
end
|
34
41
|
end
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
42
|
+
|
43
|
+
# Add `connection_class` as the connection wrapper for `nodes_class`
|
44
|
+
# eg, `RelationConnection` is the implementation for `AR::Relation`
|
45
|
+
# @param [Class] A class representing a collection (eg, Array, AR::Relation)
|
46
|
+
# @param [Class] A class implementing Connection methods
|
47
|
+
def register_connection_implementation(nodes_class, connection_class)
|
48
|
+
CONNECTION_IMPLEMENTATIONS[nodes_class.name] = connection_class
|
39
49
|
end
|
40
|
-
end
|
41
50
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
# @param [Class] A class implementing Connection methods
|
46
|
-
def self.register_connection_implementation(items_class, connection_class)
|
47
|
-
CONNECTION_IMPLEMENTATIONS[items_class.name] = connection_class
|
51
|
+
# @deprecated use {#connection_for_nodes} instead
|
52
|
+
alias :connection_for_items :connection_for_nodes
|
53
|
+
deprecate(:connection_for_items, :connection_for_nodes, 2016, 9)
|
48
54
|
end
|
49
55
|
|
50
|
-
attr_reader :
|
56
|
+
attr_reader :nodes, :arguments, :max_page_size, :parent
|
51
57
|
|
52
|
-
# Make a connection, wrapping `
|
53
|
-
# @param The collection of
|
58
|
+
# Make a connection, wrapping `nodes`
|
59
|
+
# @param [Object] The collection of nodes
|
54
60
|
# @param Query arguments
|
55
61
|
# @param max_page_size [Int] The maximum number of results to return
|
56
62
|
# @param parent [Object] The object which this collection belongs to
|
57
|
-
def initialize(
|
58
|
-
@
|
63
|
+
def initialize(nodes, arguments, max_page_size: nil, parent: nil)
|
64
|
+
@nodes = nodes
|
59
65
|
@arguments = arguments
|
60
66
|
@max_page_size = max_page_size
|
61
67
|
@parent = parent
|
62
68
|
end
|
63
69
|
|
70
|
+
# @deprecated use {#nodes} instead
|
71
|
+
alias :object :nodes
|
72
|
+
deprecate(:object, :nodes, 2016, 9)
|
73
|
+
|
64
74
|
# Provide easy access to provided arguments:
|
65
75
|
METHODS_FROM_ARGUMENTS = [:first, :after, :last, :before, :order]
|
66
76
|
|
@@ -80,7 +90,7 @@ module GraphQL
|
|
80
90
|
end
|
81
91
|
end
|
82
92
|
|
83
|
-
# These are the
|
93
|
+
# These are the nodes to render for this connection,
|
84
94
|
# probably wrapped by {GraphQL::Relay::Edge}
|
85
95
|
def edge_nodes
|
86
96
|
@edge_nodes ||= paged_nodes
|
@@ -127,11 +137,11 @@ module GraphQL
|
|
127
137
|
private
|
128
138
|
|
129
139
|
def paged_nodes
|
130
|
-
raise NotImplementedError, "must return
|
140
|
+
raise NotImplementedError, "must return nodes for this connection after paging"
|
131
141
|
end
|
132
142
|
|
133
143
|
def sliced_nodes
|
134
|
-
raise NotImplementedError, "must return all
|
144
|
+
raise NotImplementedError, "must return all nodes for this connection after chopping off first and last"
|
135
145
|
end
|
136
146
|
end
|
137
147
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Relay
|
3
|
-
# Provided a GraphQL field which returns a collection of
|
4
|
-
# `ConnectionField.create` modifies that field to expose those
|
3
|
+
# Provided a GraphQL field which returns a collection of nodes,
|
4
|
+
# `ConnectionField.create` modifies that field to expose those nodes
|
5
5
|
# as a collection.
|
6
6
|
#
|
7
|
-
# The original resolve proc is used to fetch
|
8
|
-
# then a connection implementation is fetched with {BaseConnection.
|
7
|
+
# The original resolve proc is used to fetch nodes,
|
8
|
+
# then a connection implementation is fetched with {BaseConnection.connection_for_nodes}.
|
9
9
|
class ConnectionField
|
10
10
|
ARGUMENT_DEFINITIONS = [
|
11
11
|
["first", GraphQL::INT_TYPE, "Returns the first _n_ elements from the list."],
|
@@ -27,28 +27,15 @@ module GraphQL
|
|
27
27
|
# Turn A GraphQL::Field into a connection by:
|
28
28
|
# - Merging in the default arguments
|
29
29
|
# - Transforming its resolve function to return a connection object
|
30
|
-
# @param [GraphQL::Field] A field which returns
|
31
|
-
# @param max_page_size [Integer] The maximum number of
|
32
|
-
# @return [GraphQL::Field]
|
30
|
+
# @param [GraphQL::Field] A field which returns nodes to be wrapped as a connection
|
31
|
+
# @param max_page_size [Integer] The maximum number of nodes which may be requested (if a larger page is requested, it is limited to this number)
|
32
|
+
# @return [GraphQL::Field] The same field, modified to resolve to a connection object
|
33
33
|
def self.create(underlying_field, max_page_size: nil)
|
34
34
|
underlying_field.arguments = DEFAULT_ARGUMENTS.merge(underlying_field.arguments)
|
35
35
|
original_resolve = underlying_field.resolve_proc
|
36
|
-
underlying_field.resolve =
|
36
|
+
underlying_field.resolve = GraphQL::Relay::ConnectionResolve.new(underlying_field.name, original_resolve, max_page_size: max_page_size)
|
37
37
|
underlying_field
|
38
38
|
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
# Wrap the original resolve proc
|
43
|
-
# so you capture its value, then wrap it in a
|
44
|
-
# connection implementation
|
45
|
-
def self.get_connection_resolve(field_name, underlying_resolve, max_page_size: nil)
|
46
|
-
-> (obj, args, ctx) {
|
47
|
-
items = underlying_resolve.call(obj, args, ctx)
|
48
|
-
connection_class = GraphQL::Relay::BaseConnection.connection_for_items(items)
|
49
|
-
connection_class.new(items, args, max_page_size: max_page_size, parent: obj)
|
50
|
-
}
|
51
|
-
end
|
52
39
|
end
|
53
40
|
end
|
54
41
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Relay
|
3
|
+
class ConnectionResolve
|
4
|
+
def initialize(field_name, underlying_resolve, max_page_size: nil)
|
5
|
+
@field_name = field_name
|
6
|
+
@underlying_resolve = underlying_resolve
|
7
|
+
@max_page_size = max_page_size
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(obj, args, ctx)
|
11
|
+
nodes = @underlying_resolve.call(obj, args, ctx)
|
12
|
+
connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(nodes)
|
13
|
+
connection_class.new(nodes, args, max_page_size: @max_page_size, parent: obj)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -22,18 +22,12 @@ module GraphQL
|
|
22
22
|
|
23
23
|
# apply first / last limit results
|
24
24
|
def paged_nodes
|
25
|
-
@paged_nodes ||=
|
26
|
-
items = sliced_nodes
|
27
|
-
items.limit(limit)
|
28
|
-
end
|
25
|
+
@paged_nodes ||= sliced_nodes.limit(limit)
|
29
26
|
end
|
30
27
|
|
31
28
|
# Apply cursors to edges
|
32
29
|
def sliced_nodes
|
33
|
-
@sliced_nodes ||=
|
34
|
-
items = object
|
35
|
-
items.offset(starting_offset)
|
36
|
-
end
|
30
|
+
@sliced_nodes ||= nodes.offset(starting_offset)
|
37
31
|
end
|
38
32
|
|
39
33
|
def offset_from_cursor(cursor)
|
data/lib/graphql/version.rb
CHANGED
data/readme.md
CHANGED
@@ -122,13 +122,7 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
|
|
122
122
|
|
123
123
|
## To Do
|
124
124
|
|
125
|
-
- Accept type name as `type` argument?
|
126
|
-
- Goal: accept `field :post, "Post"` to look up a type named `"Post"` in the schema
|
127
|
-
- Problem: how does a field know which schema to look up the name from?
|
128
|
-
- Problem: how can we load types in Rails without accessing the constant?
|
129
|
-
- Maybe support by third-party library? `type("Post!")` could implement "type_missing", keeps `graphql-ruby` very simple
|
130
125
|
- StaticValidation improvements
|
131
|
-
- Include `path: [...]` in validation errors
|
132
126
|
- Use catch-all type/field/argument definitions instead of terminating traversal
|
133
127
|
- Reduce ad-hoc traversals?
|
134
128
|
- Validators are order-dependent, is this a smell?
|
@@ -142,4 +136,3 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
|
|
142
136
|
- Reduce duplication in ArrayConnection / RelationConnection
|
143
137
|
- Improve API for creating edges (better RANGE_ADD support)
|
144
138
|
- If the new edge isn't a member of the connection's objects, raise a nice error
|
145
|
-
- Rename `Connection#object` => `Connection#collection` with deprecation
|
@@ -75,5 +75,45 @@ describe GraphQL::Analysis do
|
|
75
75
|
assert_equal "Variable cheeseId of type Int! was provided invalid value", error["message"]
|
76
76
|
end
|
77
77
|
end
|
78
|
+
|
79
|
+
describe "when processing fields" do
|
80
|
+
let(:connection_counter) {
|
81
|
+
-> (memo, visit_type, irep_node) {
|
82
|
+
memo ||= Hash.new { |h,k| h[k] = 0 }
|
83
|
+
if visit_type == :enter
|
84
|
+
if irep_node.ast_node.is_a?(GraphQL::Language::Nodes::Field)
|
85
|
+
irep_node.definitions.each do |type_defn, field_defn|
|
86
|
+
if field_defn.resolve_proc.is_a?(GraphQL::Relay::ConnectionResolve)
|
87
|
+
memo["connection"] += 1
|
88
|
+
else
|
89
|
+
memo["field"] += 1
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
memo
|
95
|
+
}
|
96
|
+
}
|
97
|
+
let(:analyzers) { [connection_counter] }
|
98
|
+
let(:reduce_result) { GraphQL::Analysis.analyze_query(query, analyzers) }
|
99
|
+
let(:query) { GraphQL::Query.new(StarWarsSchema, query_string, variables: variables) }
|
100
|
+
let(:query_string) {%|
|
101
|
+
query getBases {
|
102
|
+
empire {
|
103
|
+
basesByName(first: 30) { edges { cursor } }
|
104
|
+
bases(first: 30) { edges { cursor } }
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|}
|
108
|
+
|
109
|
+
it "knows which fields are connections" do
|
110
|
+
connection_counts = reduce_result.first
|
111
|
+
expected_connection_counts = {
|
112
|
+
"field" => 5,
|
113
|
+
"connection" => 2
|
114
|
+
}
|
115
|
+
assert_equal expected_connection_counts, connection_counts
|
116
|
+
end
|
117
|
+
end
|
78
118
|
end
|
79
119
|
end
|
@@ -12,7 +12,7 @@ module Garden
|
|
12
12
|
class Vegetable
|
13
13
|
include GraphQL::Define::InstanceDefinable
|
14
14
|
lazy_defined_attr_accessor :name, :start_planting_on, :end_planting_on
|
15
|
-
accepts_definitions :name, plant_between: DefinePlantBetween
|
15
|
+
accepts_definitions :name, plant_between: DefinePlantBetween, color: GraphQL::Define.assign_metadata_key(:color)
|
16
16
|
|
17
17
|
# definition added later:
|
18
18
|
lazy_defined_attr_accessor :height
|
@@ -61,4 +61,11 @@ describe GraphQL::Define::InstanceDefinable do
|
|
61
61
|
assert_equal Date.new(2000, 7, 1), okra.end_planting_on
|
62
62
|
end
|
63
63
|
end
|
64
|
+
|
65
|
+
describe "#metadata" do
|
66
|
+
it "gets values from definitions" do
|
67
|
+
arugula = Garden::Vegetable.define(name: "Arugula", color: :green)
|
68
|
+
assert_equal :green, arugula.metadata[:color]
|
69
|
+
end
|
70
|
+
end
|
64
71
|
end
|
data/spec/graphql/field_spec.rb
CHANGED
@@ -17,7 +17,6 @@ describe GraphQL::Field do
|
|
17
17
|
assert_equal(DairyProductUnion, field.type)
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
20
|
describe ".property " do
|
22
21
|
let(:field) do
|
23
22
|
GraphQL::Field.define do
|
@@ -107,4 +106,11 @@ describe GraphQL::Field do
|
|
107
106
|
assert_equal "Xyz", resolved_source
|
108
107
|
end
|
109
108
|
end
|
109
|
+
|
110
|
+
describe "#metadata" do
|
111
|
+
it "accepts user-defined metadata" do
|
112
|
+
similar_cheese_field = CheeseType.get_field("similarCheese")
|
113
|
+
assert_equal [:cheeses, :milks], similar_cheese_field.metadata[:joins]
|
114
|
+
end
|
115
|
+
end
|
110
116
|
end
|
data/spec/support/dairy_app.rb
CHANGED
@@ -2,6 +2,9 @@ require_relative "./dairy_data"
|
|
2
2
|
|
3
3
|
class NoSuchDairyError < StandardError; end
|
4
4
|
|
5
|
+
GraphQL::Field.accepts_definitions(joins: GraphQL::Define.assign_metadata_key(:joins))
|
6
|
+
GraphQL::BaseType.accepts_definitions(class_names: GraphQL::Define.assign_metadata_key(:class_names))
|
7
|
+
|
5
8
|
EdibleInterface = GraphQL::InterfaceType.define do
|
6
9
|
name "Edible"
|
7
10
|
description "Something you can eat, yum"
|
@@ -26,6 +29,7 @@ end
|
|
26
29
|
|
27
30
|
CheeseType = GraphQL::ObjectType.define do
|
28
31
|
name "Cheese"
|
32
|
+
class_names ["Cheese"]
|
29
33
|
description "Cultured dairy product"
|
30
34
|
interfaces [EdibleInterface, AnimalProductInterface]
|
31
35
|
|
@@ -39,6 +43,8 @@ CheeseType = GraphQL::ObjectType.define do
|
|
39
43
|
|
40
44
|
# Or can define by block, `resolve ->` should override `property:`
|
41
45
|
field :similarCheese, CheeseType, "Cheeses like this one", property: :this_should_be_overriden do
|
46
|
+
# metadata test
|
47
|
+
joins [:cheeses, :milks]
|
42
48
|
argument :source, !types[!DairyAnimalEnum]
|
43
49
|
resolve -> (t, a, c) {
|
44
50
|
# get the strings out:
|
@@ -47,7 +47,7 @@ BaseConnectionWithTotalCountType = BaseType.define_connection do
|
|
47
47
|
name "BasesConnectionWithTotalCount"
|
48
48
|
field :totalCount do
|
49
49
|
type types.Int
|
50
|
-
resolve -> (obj, args, ctx) { obj.
|
50
|
+
resolve -> (obj, args, ctx) { obj.nodes.count }
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
@@ -75,7 +75,7 @@ CustomEdgeBaseConnectionType = BaseType.define_connection(edge_class: CustomBase
|
|
75
75
|
|
76
76
|
field :totalCountTimes100 do
|
77
77
|
type types.Int
|
78
|
-
resolve -> (obj, args, ctx) { obj.
|
78
|
+
resolve -> (obj, args, ctx) { obj.nodes.count * 100 }
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.18.
|
4
|
+
version: 0.18.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: codeclimate-test-reporter
|
@@ -260,7 +260,6 @@ files:
|
|
260
260
|
- lib/graphql/define/assign_enum_value.rb
|
261
261
|
- lib/graphql/define/assign_global_id_field.rb
|
262
262
|
- lib/graphql/define/assign_object_field.rb
|
263
|
-
- lib/graphql/define/assignment_dictionary.rb
|
264
263
|
- lib/graphql/define/defined_object_proxy.rb
|
265
264
|
- lib/graphql/define/instance_definable.rb
|
266
265
|
- lib/graphql/define/non_null_with_bang.rb
|
@@ -333,6 +332,7 @@ files:
|
|
333
332
|
- lib/graphql/relay/array_connection.rb
|
334
333
|
- lib/graphql/relay/base_connection.rb
|
335
334
|
- lib/graphql/relay/connection_field.rb
|
335
|
+
- lib/graphql/relay/connection_resolve.rb
|
336
336
|
- lib/graphql/relay/connection_type.rb
|
337
337
|
- lib/graphql/relay/edge.rb
|
338
338
|
- lib/graphql/relay/edge_type.rb
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module GraphQL
|
2
|
-
module Define
|
3
|
-
# Create a hash of definitions out of provided arguments.
|
4
|
-
#
|
5
|
-
# @example Create definitions (some default, some custom)
|
6
|
-
# hash = AssignmentDictionary.create(:name, :description, field: (value, field_name) -> { value.create_field(field) })
|
7
|
-
#
|
8
|
-
module AssignmentDictionary
|
9
|
-
# Turn `keys` into a hash suitable for {GraphQL::Define::InstanceDefinable}
|
10
|
-
# @param Any number of symbols for default assignment, followed by an (optional) hash of custom assignment procs.
|
11
|
-
# @return [Hash] keys are attributes which may be defined. values are procs which assign values to the target object.
|
12
|
-
def self.create(*keys)
|
13
|
-
initial = if keys.last.is_a?(Hash)
|
14
|
-
keys.pop
|
15
|
-
else
|
16
|
-
{}
|
17
|
-
end
|
18
|
-
keys.inject(initial) do |memo, key|
|
19
|
-
assign_key = "#{key}="
|
20
|
-
memo[key] = -> (target, value) { target.public_send(assign_key, value) }
|
21
|
-
memo
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|