graphql 0.0.1 → 0.0.2
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.rb +78 -9
- data/lib/graphql/call.rb +7 -0
- data/lib/graphql/connection.rb +44 -0
- data/lib/graphql/field.rb +117 -0
- data/lib/graphql/field_definer.rb +29 -0
- data/lib/graphql/introspection/call_node.rb +13 -0
- data/lib/graphql/introspection/connection.rb +9 -0
- data/lib/graphql/introspection/field_node.rb +19 -0
- data/lib/graphql/introspection/root_call_argument_node.rb +5 -0
- data/lib/graphql/introspection/root_call_node.rb +16 -0
- data/lib/graphql/introspection/schema_call.rb +8 -0
- data/lib/graphql/introspection/schema_node.rb +17 -0
- data/lib/graphql/introspection/type_call.rb +8 -0
- data/lib/graphql/introspection/type_node.rb +16 -0
- data/lib/graphql/node.rb +141 -34
- data/lib/graphql/parser.rb +19 -8
- data/lib/graphql/query.rb +64 -21
- data/lib/graphql/root_call.rb +176 -0
- data/lib/graphql/root_call_argument.rb +8 -0
- data/lib/graphql/root_call_argument_definer.rb +20 -0
- data/lib/graphql/schema.rb +99 -0
- data/lib/graphql/syntax/call.rb +3 -12
- data/lib/graphql/syntax/field.rb +4 -2
- data/lib/graphql/syntax/node.rb +3 -10
- data/lib/graphql/syntax/query.rb +7 -0
- data/lib/graphql/syntax/variable.rb +7 -0
- data/lib/graphql/transform.rb +14 -5
- data/lib/graphql/types/boolean_field.rb +3 -0
- data/lib/graphql/types/connection_field.rb +30 -0
- data/lib/graphql/types/cursor_field.rb +9 -0
- data/lib/graphql/types/number_field.rb +3 -0
- data/lib/graphql/types/object_field.rb +8 -0
- data/lib/graphql/types/string_field.rb +3 -0
- data/lib/graphql/types/type_field.rb +6 -0
- data/lib/graphql/version.rb +3 -0
- data/readme.md +142 -10
- data/spec/graphql/field_spec.rb +66 -0
- data/spec/graphql/node_spec.rb +68 -0
- data/spec/graphql/parser_spec.rb +75 -25
- data/spec/graphql/query_spec.rb +185 -83
- data/spec/graphql/root_call_spec.rb +55 -0
- data/spec/graphql/schema_spec.rb +128 -0
- data/spec/graphql/transform_spec.rb +124 -39
- data/spec/spec_helper.rb +2 -1
- data/spec/support/dummy_app.rb +43 -23
- data/spec/support/nodes.rb +145 -32
- metadata +78 -16
- data/lib/graphql/collection_edge.rb +0 -62
- data/lib/graphql/syntax/edge.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 290e890d0a805c3587000af2fbbacf910cf8baf0
|
4
|
+
data.tar.gz: 745511749686efb279e0ee7b7af2484d0ccd82ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81ba92e0aed50365b5ccd78ab02c035bc0a1ae212669f7d5c668cbe7fec3e48158f9a53a5859b2ece3ebc54d3634187635477a85cf50f2b8d24a822476517b53
|
7
|
+
data.tar.gz: 3a46fb67c848c7b85768979071d28df3b2e21e29087395b5a3ab2297d0660bd51ba84c628678e098a1faae392ef9a4e30d8be0007575ef32468f98c2c043badf
|
data/lib/graphql.rb
CHANGED
@@ -1,26 +1,95 @@
|
|
1
|
-
require "parslet"
|
2
|
-
require "active_support/core_ext/string/inflections"
|
3
1
|
require "active_support/core_ext/object/blank"
|
2
|
+
require "active_support/core_ext/string/inflections"
|
3
|
+
require "json"
|
4
|
+
require "parslet"
|
5
|
+
require "singleton"
|
4
6
|
|
5
7
|
module GraphQL
|
6
|
-
|
7
|
-
|
8
|
-
autoload(:
|
8
|
+
autoload(:Call, "graphql/call")
|
9
|
+
autoload(:Connection, "graphql/connection")
|
10
|
+
autoload(:Field, "graphql/field")
|
11
|
+
autoload(:FieldDefiner, "graphql/field_definer")
|
12
|
+
autoload(:Node, "graphql/node")
|
9
13
|
autoload(:Parser, "graphql/parser")
|
10
14
|
autoload(:Query, "graphql/query")
|
11
|
-
autoload(:
|
15
|
+
autoload(:RootCall, "graphql/root_call")
|
16
|
+
autoload(:RootCallArgument, "graphql/root_call_argument")
|
17
|
+
autoload(:RootCallArgumentDefiner, "graphql/root_call_argument_definer")
|
18
|
+
autoload(:Schema, "graphql/schema")
|
12
19
|
autoload(:Transform, "graphql/transform")
|
20
|
+
autoload(:VERSION, "graphql/version")
|
21
|
+
|
22
|
+
module Introspection
|
23
|
+
autoload(:CallNode, "graphql/introspection/call_node")
|
24
|
+
autoload(:Connection, "graphql/introspection/connection")
|
25
|
+
autoload(:FieldNode, "graphql/introspection/field_node")
|
26
|
+
autoload(:RootCallArgumentNode, "graphql/introspection/root_call_argument_node")
|
27
|
+
autoload(:RootCallNode, "graphql/introspection/root_call_node")
|
28
|
+
autoload(:SchemaCall, "graphql/introspection/schema_call")
|
29
|
+
autoload(:SchemaNode, "graphql/introspection/schema_node")
|
30
|
+
autoload(:TypeCall, "graphql/introspection/type_call")
|
31
|
+
autoload(:TypeNode, "graphql/introspection/type_node")
|
32
|
+
end
|
33
|
+
|
13
34
|
|
14
35
|
module Syntax
|
15
36
|
autoload(:Call, "graphql/syntax/call")
|
16
|
-
autoload(:Edge, "graphql/syntax/edge")
|
17
37
|
autoload(:Field, "graphql/syntax/field")
|
38
|
+
autoload(:Query, "graphql/syntax/query")
|
18
39
|
autoload(:Node, "graphql/syntax/node")
|
40
|
+
autoload(:Variable, "graphql/syntax/variable")
|
41
|
+
end
|
42
|
+
|
43
|
+
module Types
|
44
|
+
autoload(:BooleanField, "graphql/types/boolean_field")
|
45
|
+
autoload(:ConnectionField, "graphql/types/connection_field")
|
46
|
+
autoload(:CursorField, "graphql/types/cursor_field")
|
47
|
+
autoload(:NumberField, "graphql/types/number_field")
|
48
|
+
autoload(:ObjectField, "graphql/types/object_field")
|
49
|
+
autoload(:StringField, "graphql/types/string_field")
|
50
|
+
autoload(:TypeField, "graphql/types/type_field")
|
51
|
+
end
|
52
|
+
|
53
|
+
class Error < RuntimeError; end
|
54
|
+
class FieldNotDefinedError < Error
|
55
|
+
def initialize(class_name, field_name)
|
56
|
+
super("#{class_name}##{field_name} was requested, but it isn't defined. Defined fields are: #{SCHEMA.field_names}")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
class NodeNotDefinedError < Error
|
60
|
+
def initialize(node_name)
|
61
|
+
super("#{node_name} was requested but was not found. Defined nodes are: #{SCHEMA.type_names}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
class ConnectionNotDefinedError < Error
|
65
|
+
def initialize(node_name)
|
66
|
+
super("#{node_name} was requested but was not found. Defined connections are: #{SCHEMA.connection_names}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
class RootCallNotDefinedError < Error
|
70
|
+
def initialize(name)
|
71
|
+
super("Call '#{name}' was requested but was not found. Defined calls are: #{SCHEMA.call_names}")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
class SyntaxError < Error
|
75
|
+
def initialize(line, col, string)
|
76
|
+
lines = string.split("\n")
|
77
|
+
super("Syntax Error at (#{line}, #{col}), check usage: #{string}")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class RootCallArgumentError < Error
|
82
|
+
def initialize(declaration, actual)
|
83
|
+
super("Wrong type for #{declaration.name}: expected a #{declaration.type} but got #{actual}")
|
84
|
+
end
|
19
85
|
end
|
20
86
|
|
21
87
|
PARSER = Parser.new
|
88
|
+
SCHEMA = Schema.instance
|
22
89
|
TRANSFORM = Transform.new
|
23
|
-
|
24
|
-
|
90
|
+
# preload these so they're in SCHEMA
|
91
|
+
["types", "introspection"].each do |preload_dir|
|
92
|
+
Dir["#{File.dirname(__FILE__)}/graphql/#{preload_dir}/*.rb"].each { |f| require f }
|
25
93
|
end
|
94
|
+
Node.field.__type__(:__type__)
|
26
95
|
end
|
data/lib/graphql/call.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
class GraphQL::Connection < GraphQL::Node
|
2
|
+
exposes "Array"
|
3
|
+
field.any(:edges)
|
4
|
+
|
5
|
+
attr_reader :calls, :syntax_fields, :query
|
6
|
+
|
7
|
+
def initialize(items, query:, fields: [])
|
8
|
+
@target = items
|
9
|
+
@syntax_fields = fields
|
10
|
+
@query = query
|
11
|
+
end
|
12
|
+
|
13
|
+
def items
|
14
|
+
@target
|
15
|
+
end
|
16
|
+
|
17
|
+
def edge_fields
|
18
|
+
@edge_fields ||= syntax_fields.find { |f| f.identifier == "edges" }.fields
|
19
|
+
end
|
20
|
+
|
21
|
+
def edges
|
22
|
+
raise "#{self.class} expected a connection, but got `nil`" if items.nil?
|
23
|
+
items.map do |item|
|
24
|
+
node_class = GraphQL::SCHEMA.type_for_object(item)
|
25
|
+
node = node_class.new(item, fields: edge_fields, query: query)
|
26
|
+
res = node.as_result
|
27
|
+
res
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
def default_schema_name
|
33
|
+
name.split("::").last.sub(/Connection$/, '').underscore
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_accessor :default_connection
|
37
|
+
def default_connection!
|
38
|
+
GraphQL::Connection.default_connection = self
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
self.default_connection!
|
44
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
class GraphQL::Field
|
2
|
+
attr_reader :query, :owner, :calls, :fields
|
3
|
+
def initialize(query: nil, owner: nil, calls: [], fields: [])
|
4
|
+
@query = query
|
5
|
+
@owner = owner
|
6
|
+
@calls = calls
|
7
|
+
@fields = fields
|
8
|
+
end
|
9
|
+
|
10
|
+
def raw_value
|
11
|
+
owner.send(method)
|
12
|
+
end
|
13
|
+
|
14
|
+
def as_result
|
15
|
+
finished_value
|
16
|
+
end
|
17
|
+
|
18
|
+
def finished_value
|
19
|
+
@finished_value ||= begin
|
20
|
+
val = raw_value
|
21
|
+
calls.each do |call|
|
22
|
+
registered_call = self.class.calls[call.identifier]
|
23
|
+
if registered_call.nil?
|
24
|
+
raise "Call not found: #{self.class.name}##{call.identifier}"
|
25
|
+
end
|
26
|
+
val = registered_call.lambda.call(val, *call.arguments)
|
27
|
+
end
|
28
|
+
val
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# instance `const_get` reaches up to class namespace
|
33
|
+
def const_get(const_name)
|
34
|
+
self.class.const_get(const_name)
|
35
|
+
rescue
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# delegate to class constant
|
40
|
+
["name", "description"].each do |method_name|
|
41
|
+
define_method(method_name) do
|
42
|
+
const_get(method_name.upcase)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def method; name; end
|
47
|
+
|
48
|
+
class << self
|
49
|
+
def inherited(child_class)
|
50
|
+
GraphQL::SCHEMA.add_field(child_class)
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_class(name:, owner_class:, type:, description: nil, connection_class_name: nil, node_class_name: nil)
|
54
|
+
if type.is_a?(Symbol)
|
55
|
+
type = GraphQL::SCHEMA.get_field(type)
|
56
|
+
end
|
57
|
+
|
58
|
+
field_superclass = type || self
|
59
|
+
new_class = Class.new(field_superclass)
|
60
|
+
new_class.const_set :NAME, name
|
61
|
+
new_class.const_set :OWNER_CLASS, owner_class
|
62
|
+
new_class.const_set :DESCRIPTION , description
|
63
|
+
new_class.const_set :CONNECTION_CLASS_NAME, connection_class_name
|
64
|
+
new_class.const_set :NODE_CLASS_NAME, node_class_name
|
65
|
+
new_class
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
if const_defined?(:NAME)
|
70
|
+
"<FieldClass: #{const_get(:OWNER_CLASS).name}::#{const_get(:NAME)}>"
|
71
|
+
else
|
72
|
+
super
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def type(value_type_name)
|
77
|
+
@value_type = value_type_name.to_s
|
78
|
+
GraphQL::SCHEMA.add_field(self)
|
79
|
+
end
|
80
|
+
|
81
|
+
def value_type
|
82
|
+
@value_type || superclass.value_type
|
83
|
+
end
|
84
|
+
|
85
|
+
def schema_name
|
86
|
+
@value_type || (name.present? ? default_schema_name : nil)
|
87
|
+
end
|
88
|
+
|
89
|
+
def default_schema_name
|
90
|
+
name.split("::").last.sub(/Field$/, '').underscore
|
91
|
+
end
|
92
|
+
|
93
|
+
def field_name
|
94
|
+
const_get(:NAME)
|
95
|
+
end
|
96
|
+
|
97
|
+
def description
|
98
|
+
const_get(:DESCRIPTION)
|
99
|
+
end
|
100
|
+
|
101
|
+
def calls
|
102
|
+
superclass.calls.merge(_calls)
|
103
|
+
rescue NoMethodError
|
104
|
+
{}
|
105
|
+
end
|
106
|
+
|
107
|
+
def _calls
|
108
|
+
@calls ||= {}
|
109
|
+
end
|
110
|
+
|
111
|
+
def call(name, lambda)
|
112
|
+
_calls[name.to_s] = GraphQL::Call.new(name: name.to_s, lambda: lambda)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
type :any
|
117
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class GraphQL::FieldDefiner
|
2
|
+
attr_reader :owner_class
|
3
|
+
def initialize(owner_class)
|
4
|
+
@owner_class = owner_class
|
5
|
+
end
|
6
|
+
|
7
|
+
def method_missing(method_name, *args, &block)
|
8
|
+
type = GraphQL::SCHEMA.get_field(method_name)
|
9
|
+
if type.present?
|
10
|
+
create_field(args[0], type: type, description: args[1])
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_field(field_name, type: nil, description: nil)
|
17
|
+
field_name = field_name.to_s
|
18
|
+
field_class = GraphQL::Field.create_class({
|
19
|
+
name: field_name,
|
20
|
+
type: type,
|
21
|
+
owner_class: owner_class,
|
22
|
+
description: description,
|
23
|
+
})
|
24
|
+
|
25
|
+
field_class_name = field_name.camelize + "Field"
|
26
|
+
owner_class.const_set(field_class_name, field_class)
|
27
|
+
owner_class.own_fields[field_name] = field_class
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class GraphQL::Introspection::CallNode < GraphQL::Node
|
2
|
+
exposes "GraphQL::Call"
|
3
|
+
field.string(:name)
|
4
|
+
field.string(:arguments)
|
5
|
+
|
6
|
+
def arguments
|
7
|
+
args = target.lambda.parameters
|
8
|
+
args.shift
|
9
|
+
args.map { |p| "#{p[1]} (#{p[0]})" }.join(", ")
|
10
|
+
rescue StandardError => e
|
11
|
+
raise "Errored on #{name} (#{self}): #{e}"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class GraphQL::Introspection::FieldNode < GraphQL::Node
|
2
|
+
exposes "GraphQL::Field"
|
3
|
+
field.string(:name)
|
4
|
+
field.string(:type)
|
5
|
+
field.string(:description)
|
6
|
+
field.connection(:calls)
|
7
|
+
|
8
|
+
def calls
|
9
|
+
target.calls.values
|
10
|
+
end
|
11
|
+
|
12
|
+
def name
|
13
|
+
schema_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def type
|
17
|
+
value_type
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class GraphQL::Introspection::RootCallNode < GraphQL::Node
|
2
|
+
exposes "GraphQL::RootCall"
|
3
|
+
field.string(:name)
|
4
|
+
field.string(:returns)
|
5
|
+
field.connection(:arguments)
|
6
|
+
|
7
|
+
|
8
|
+
def returns
|
9
|
+
return_declarations = @target.return_declarations
|
10
|
+
return_declarations.keys.map(&:to_s)
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
schema_name
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class GraphQL::Introspection::SchemaNode < GraphQL::Node
|
2
|
+
exposes "GraphQL::Schema"
|
3
|
+
field.connection(:calls)
|
4
|
+
field.connection(:types)
|
5
|
+
|
6
|
+
def cursor
|
7
|
+
"schema"
|
8
|
+
end
|
9
|
+
|
10
|
+
def types
|
11
|
+
@target.types.values
|
12
|
+
end
|
13
|
+
|
14
|
+
def calls
|
15
|
+
target.calls.values
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class GraphQL::Introspection::TypeNode < GraphQL::Node
|
2
|
+
exposes "GraphQL::Node"
|
3
|
+
field.string(:name)
|
4
|
+
field.string(:description)
|
5
|
+
field.connection(:fields)
|
6
|
+
|
7
|
+
cursor :name
|
8
|
+
|
9
|
+
def fields
|
10
|
+
target.all_fields.values
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
schema_name
|
15
|
+
end
|
16
|
+
end
|