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