graphql-rails 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/app/controllers/graphql/rails/schema_controller.rb +12 -6
- data/config/initializers/graphiql.rb +2 -0
- data/lib/graphql/rails.rb +7 -5
- data/lib/graphql/rails/callbacks.rb +14 -3
- data/lib/graphql/rails/config.rb +9 -0
- data/lib/graphql/rails/controller_extensions.rb +8 -5
- data/lib/graphql/rails/dsl.rb +7 -0
- data/lib/graphql/rails/engine.rb +8 -4
- data/lib/graphql/rails/extensions/cancan.rb +6 -2
- data/lib/graphql/rails/extensions/mongoid.rb +31 -16
- data/lib/graphql/rails/node_identification.rb +1 -0
- data/lib/graphql/rails/operations.rb +37 -21
- data/lib/graphql/rails/schema.rb +11 -3
- data/lib/graphql/rails/types.rb +32 -3
- data/lib/graphql/rails/version.rb +1 -1
- metadata +8 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a7c5384076b014463594f7ff514f7575c158d7e
|
4
|
+
data.tar.gz: ffc42b92d06723a1ea3605d1c11f2ccb2fd1f880
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba44e7314732609e798b8bff7bbf931f26673af2aa835c5af3e65be9fa35a230c66f79c2ce2e0a2b2d854e7892029268ac15ead3ebb78bbb8a475ff1d4903068
|
7
|
+
data.tar.gz: 25341849de64f37b1691a3735749a695a8769eaf6c3076aa1249fe7797d7939dda2e10d36c1d0a9229484627f3483de58cdab6a3f3a78cc12b8a45014577cd9b
|
@@ -1,19 +1,23 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Rails
|
3
3
|
class SchemaController < ActionController::Base
|
4
|
+
# Extensions are dynamically loaded once during engine initialization;
|
5
|
+
# however, this controller can be reloaded at any time by Rails. To
|
6
|
+
# preserve extensions, we use the ControllerExtensions module as a cache.
|
4
7
|
include ControllerExtensions
|
8
|
+
|
9
|
+
# Defined in order of increasing specificity.
|
5
10
|
rescue_from Exception, :with => :internal_error
|
6
11
|
rescue_from GraphQL::ParseError, :with => :invalid_query
|
7
12
|
rescue_from JSON::ParserError, :with => :invalid_variables
|
8
13
|
|
14
|
+
# Execute a GraphQL query against the current schema.
|
9
15
|
def execute
|
10
|
-
query_string = params[:query]
|
11
|
-
query_variables = to_hash(params[:variables])
|
12
16
|
render json: Schema.instance.execute(
|
13
|
-
|
14
|
-
variables:
|
17
|
+
params[:query],
|
18
|
+
variables: to_hash(params[:variables]),
|
15
19
|
context: context,
|
16
|
-
debug:
|
20
|
+
debug: Rails.config.debug
|
17
21
|
)
|
18
22
|
end
|
19
23
|
|
@@ -51,7 +55,9 @@ module GraphQL
|
|
51
55
|
invalid_request 'Unable to parse variables'
|
52
56
|
end
|
53
57
|
|
54
|
-
def internal_error
|
58
|
+
def internal_error(e)
|
59
|
+
Rails.logger.error 'Unexpected exception during execution'
|
60
|
+
Rails.logger.exception e
|
55
61
|
render_error 500, 'Internal error'
|
56
62
|
end
|
57
63
|
end
|
data/lib/graphql/rails.rb
CHANGED
@@ -3,14 +3,16 @@ require 'graphql'
|
|
3
3
|
require 'graphql/relay'
|
4
4
|
require 'graphiql/rails'
|
5
5
|
|
6
|
+
# Order dependent.
|
7
|
+
|
6
8
|
require 'graphql/rails/version'
|
7
|
-
require 'graphql/rails/dsl'
|
8
|
-
require 'graphql/rails/engine'
|
9
|
-
require 'graphql/rails/config'
|
10
9
|
require 'graphql/rails/config'
|
10
|
+
require 'graphql/rails/engine'
|
11
|
+
|
12
|
+
require 'graphql/rails/dsl'
|
11
13
|
require 'graphql/rails/types'
|
12
|
-
require 'graphql/rails/node_identification'
|
13
|
-
require 'graphql/rails/controller_extensions'
|
14
14
|
require 'graphql/rails/schema'
|
15
15
|
require 'graphql/rails/callbacks'
|
16
16
|
require 'graphql/rails/operations'
|
17
|
+
require 'graphql/rails/node_identification'
|
18
|
+
require 'graphql/rails/controller_extensions'
|
@@ -1,19 +1,27 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Rails
|
3
3
|
class Operations
|
4
|
+
# Implement callback methods on Operations.
|
5
|
+
# These are akin to the 'filters' available on ActionController::Base.
|
6
|
+
# http://api.rubyonrails.org/classes/AbstractController/Callbacks.html
|
4
7
|
module Callbacks
|
5
8
|
extend ActiveSupport::Concern
|
6
9
|
include ActiveSupport::Callbacks
|
7
10
|
|
11
|
+
# All callbacks are registered under the :perform_operation event.
|
8
12
|
included do
|
9
13
|
define_callbacks :perform_operation
|
10
14
|
end
|
11
15
|
|
12
16
|
module ClassMethods
|
17
|
+
# Callbacks can be registered with the following methods:
|
18
|
+
# before_operation, before_filter
|
19
|
+
# around_operation, around_filter
|
20
|
+
# after_operation, after_filter
|
13
21
|
[:before, :after, :around].each do |callback|
|
14
|
-
define_method "#{callback}_operation" do |*names, &
|
15
|
-
insert_callbacks(names,
|
16
|
-
set_callback
|
22
|
+
define_method "#{callback}_operation" do |*names, &block|
|
23
|
+
insert_callbacks(names, block) do |target, options|
|
24
|
+
set_callback :perform_operation, callback, target, options
|
17
25
|
end
|
18
26
|
end
|
19
27
|
alias_method :"#{callback}_filter", :"#{callback}_operation"
|
@@ -21,11 +29,13 @@ module GraphQL
|
|
21
29
|
|
22
30
|
private
|
23
31
|
|
32
|
+
# Convert :only and :except options into :if and :unless blocks.
|
24
33
|
def normalize_callback_options(options)
|
25
34
|
normalize_callback_option(options, :only, :if)
|
26
35
|
normalize_callback_option(options, :except, :unless)
|
27
36
|
end
|
28
37
|
|
38
|
+
# Convert an operation name-based condition into an executable block.
|
29
39
|
def normalize_callback_option(options, from, to)
|
30
40
|
return unless options[from]
|
31
41
|
check = -> do
|
@@ -34,6 +44,7 @@ module GraphQL
|
|
34
44
|
options[to] = Array(options[to]) + [check]
|
35
45
|
end
|
36
46
|
|
47
|
+
# Normalize the arguments passed during callback registration.
|
37
48
|
def insert_callbacks(callbacks, block = nil)
|
38
49
|
options = callbacks.extract_options!
|
39
50
|
normalize_callback_options(options)
|
data/lib/graphql/rails/config.rb
CHANGED
@@ -2,12 +2,17 @@ module GraphQL
|
|
2
2
|
module Rails
|
3
3
|
extend self
|
4
4
|
|
5
|
+
# Yields the configuration object to a block, per convention.
|
5
6
|
def configure
|
6
7
|
yield config
|
7
8
|
end
|
8
9
|
|
10
|
+
# Configuration for this gem.
|
9
11
|
def config
|
10
12
|
@config ||= OpenStruct.new({
|
13
|
+
# Should graphql-ruby be placed into debug mode?
|
14
|
+
:debug => ::Rails.env.development?,
|
15
|
+
|
11
16
|
# Should the GraphiQL web interface be served?
|
12
17
|
:graphiql => ::Rails.env.development?,
|
13
18
|
|
@@ -19,6 +24,10 @@ module GraphQL
|
|
19
24
|
# This is necessary to conform to the Relay Global Object ID spec.
|
20
25
|
:global_ids => true,
|
21
26
|
|
27
|
+
# Maximum nesting for GraphQL queries.
|
28
|
+
# Specify nil for unlimited nesting depth.
|
29
|
+
:max_depth => 8,
|
30
|
+
|
22
31
|
# Should the following extensions be loaded?
|
23
32
|
:mongoid => defined?(::Mongoid),
|
24
33
|
:cancan => defined?(::CanCan),
|
@@ -1,22 +1,25 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Rails
|
3
|
+
# Extensions are dynamically loaded once during engine initialization;
|
4
|
+
# however, SchemaController can be reloaded at any time by Rails. To
|
5
|
+
# preserve extensions to SchemaController, they're registered here.
|
3
6
|
module ControllerExtensions
|
4
7
|
extend self
|
5
8
|
|
6
9
|
def add(&block)
|
7
|
-
|
10
|
+
extensions.push block
|
8
11
|
end
|
9
12
|
|
10
13
|
def included(base)
|
11
|
-
|
12
|
-
base.class_eval(&
|
14
|
+
extensions.each do |extensions|
|
15
|
+
base.class_eval(&extensions)
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
16
19
|
private
|
17
20
|
|
18
|
-
def
|
19
|
-
@
|
21
|
+
def extensions
|
22
|
+
@extensions ||= []
|
20
23
|
end
|
21
24
|
end
|
22
25
|
end
|
data/lib/graphql/rails/dsl.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Rails
|
3
|
+
# Object that runs a block in the context of itself, but delegates unknown
|
4
|
+
# methods back to the block's original context. This is useful for creating
|
5
|
+
# DSLs to aid with object initialization.
|
6
|
+
#
|
7
|
+
# Note that this class extends from BasicObject, which means that _all_
|
8
|
+
# global classes and modules must be prefixed by a double-colon (::) in
|
9
|
+
# order to resolve.
|
3
10
|
class DSL < BasicObject
|
4
11
|
def run(&block)
|
5
12
|
@self = eval('self', block.binding)
|
data/lib/graphql/rails/engine.rb
CHANGED
@@ -11,10 +11,10 @@ module GraphQL
|
|
11
11
|
class Engine < ::Rails::Engine
|
12
12
|
isolate_namespace GraphQL::Rails
|
13
13
|
|
14
|
+
# Even though we aren't using symbolic autoloading of operations, they
|
15
|
+
# must be included in autoload_paths in order to be unloaded during
|
16
|
+
# reload operations.
|
14
17
|
initializer 'graphql-rails.autoload', :before => :set_autoload_paths do |app|
|
15
|
-
# Even though we aren't using symbolic autoloading of operations, they
|
16
|
-
# must be included in autoload_paths in order to be unloaded during
|
17
|
-
# reload operations.
|
18
18
|
@graph_path = app.root.join('app', 'graph')
|
19
19
|
app.config.autoload_paths += [
|
20
20
|
@graph_path.join('types'),
|
@@ -22,6 +22,7 @@ module GraphQL
|
|
22
22
|
]
|
23
23
|
end
|
24
24
|
|
25
|
+
# Extend the Rails logger with a facility for logging exceptions.
|
25
26
|
initializer 'graphql-rails.logger', :after => :initialize_logger do |app|
|
26
27
|
logger = ::Rails.logger.clone
|
27
28
|
logger.class_eval do
|
@@ -37,14 +38,16 @@ module GraphQL
|
|
37
38
|
Rails.logger.debug 'Initialized logger'
|
38
39
|
end
|
39
40
|
|
41
|
+
# Extensions depend upon a loaded Rails app, so we load them dynamically.
|
40
42
|
initializer 'graphql-rails.extensions', :after => :load_config_initializers do |app|
|
41
|
-
# These depend upon a loaded Rails app, so we load them dynamically.
|
42
43
|
extensions = File.join(File.dirname(__FILE__), 'extensions', '*.rb')
|
43
44
|
Dir[extensions].each do |file|
|
44
45
|
require file
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
49
|
+
# Hook into Rails reloading in order to clear state from internal
|
50
|
+
# stateful modules and reload operations from the Rails app.
|
48
51
|
initializer 'graphql-rails.prepare', :before => :add_to_prepare_blocks do
|
49
52
|
# The block executes in the context of the reloader, so we have to
|
50
53
|
# preserve a reference to the engine instance.
|
@@ -54,6 +57,7 @@ module GraphQL
|
|
54
57
|
end
|
55
58
|
end
|
56
59
|
|
60
|
+
# Clear state and load operations from the Rails app.
|
57
61
|
def reload!
|
58
62
|
Types.clear
|
59
63
|
Schema.clear
|
@@ -3,6 +3,8 @@ module GraphQL
|
|
3
3
|
if Rails.config.cancan
|
4
4
|
Rails.logger.debug 'Loading CanCan extensions'
|
5
5
|
|
6
|
+
# Implement methods from CanCan::ControllerAdditions in Operations.
|
7
|
+
# http://www.rubydoc.info/github/ryanb/cancan/CanCan/ControllerAdditions
|
6
8
|
Operations.class_eval do
|
7
9
|
extend Forwardable
|
8
10
|
def_delegators :current_ability, :can?, :cannot?
|
@@ -26,7 +28,7 @@ module GraphQL
|
|
26
28
|
begin
|
27
29
|
@authorized = true
|
28
30
|
current_ability.authorize!(*args)
|
29
|
-
rescue CanCan::AccessDenied
|
31
|
+
rescue ::CanCan::AccessDenied
|
30
32
|
raise 'You are not authorized to perform this operation'
|
31
33
|
end
|
32
34
|
end
|
@@ -36,10 +38,12 @@ module GraphQL
|
|
36
38
|
end
|
37
39
|
|
38
40
|
def current_user
|
39
|
-
|
41
|
+
context[:current_user]
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
45
|
+
# Make the current_user available during GraphQL execution via the
|
46
|
+
# operation context object.
|
43
47
|
ControllerExtensions.add do
|
44
48
|
before_filter do
|
45
49
|
context[:current_user] = current_user
|
@@ -3,26 +3,30 @@ module GraphQL
|
|
3
3
|
if Rails.config.mongoid
|
4
4
|
Rails.logger.debug 'Loading Mongoid extensions'
|
5
5
|
|
6
|
+
# Use the built-in RelationConnection to handle Mongoid relations.
|
6
7
|
GraphQL::Relay::BaseConnection.register_connection_implementation(
|
7
8
|
::Mongoid::Relations::Targets::Enumerable,
|
8
9
|
GraphQL::Relay::RelationConnection
|
9
10
|
)
|
10
11
|
|
12
|
+
# Mongoid type extension for the GraphQL type system.
|
11
13
|
module Mongoid
|
12
14
|
extend self
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
+
# Clear internal state, probably due to a Rails reload.
|
16
17
|
def clear
|
17
18
|
@types = nil
|
18
19
|
end
|
19
20
|
|
21
|
+
# Resolve an arbitrary type to a GraphQL type.
|
22
|
+
# Returns nil if the type isn't a Mongoid document.
|
20
23
|
def resolve(type)
|
21
24
|
types[type] || build_type(type)
|
22
25
|
end
|
23
26
|
|
27
|
+
# Lookup an arbitrary object from its GraphQL type name and ID.
|
24
28
|
def lookup(type_name, id)
|
25
|
-
return
|
29
|
+
return unless type_name.starts_with?(namespace)
|
26
30
|
types.each_pair do |type, graph_type|
|
27
31
|
return type.find(id) if graph_type.name == type_name
|
28
32
|
end
|
@@ -31,6 +35,17 @@ module GraphQL
|
|
31
35
|
|
32
36
|
private
|
33
37
|
|
38
|
+
# Namespace for Mongoid types, if namespaces are required.
|
39
|
+
def namespace
|
40
|
+
if Types.use_namespaces?
|
41
|
+
'MG'
|
42
|
+
else
|
43
|
+
''
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Cached mapping of Mongoid types to GraphQL types, initialized with
|
48
|
+
# mappings for common built-in scalar types.
|
34
49
|
def types
|
35
50
|
@types ||= {
|
36
51
|
::Mongoid::Boolean => GraphQL::BOOLEAN_TYPE,
|
@@ -38,27 +53,35 @@ module GraphQL
|
|
38
53
|
}
|
39
54
|
end
|
40
55
|
|
56
|
+
# Build a GraphQL type for a Mongoid document.
|
57
|
+
# Returns nil if the type isn't a Mongoid document.
|
41
58
|
def build_type(type)
|
42
59
|
return nil unless type.included_modules.include?(::Mongoid::Document)
|
43
60
|
Rails.logger.debug "Building Mongoid::Document type: #{type.name}"
|
44
61
|
|
45
|
-
#
|
46
|
-
|
62
|
+
# Build and cache the GraphQL type.
|
63
|
+
# TODO: Map type inheritance to GraphQL interfaces.
|
64
|
+
type_name = Types.to_type_name(type.name, namespace)
|
47
65
|
types[type] = GraphQL::ObjectType.define do
|
48
66
|
name type_name
|
49
67
|
|
68
|
+
# Add the global node ID, if enabled.
|
50
69
|
if Rails.config.global_ids
|
51
70
|
interfaces [NodeIdentification.interface]
|
52
71
|
global_id_field :id
|
53
72
|
end
|
54
73
|
|
74
|
+
# Add each field from the document.
|
75
|
+
# TODO: Support field exclusion and renaming.
|
55
76
|
type.fields.each_value do |field_value|
|
56
|
-
field field_value.name do
|
77
|
+
field Types.to_field_name(field_value.name) do
|
78
|
+
property field_value.name.to_sym
|
57
79
|
type -> { Types.resolve(field_value.type) }
|
58
80
|
description field_value.label unless field_value.label.blank?
|
59
81
|
end
|
60
82
|
end
|
61
83
|
|
84
|
+
# Add each relationship from the document as a Relay connection.
|
62
85
|
type.relations.each_value do |relationship|
|
63
86
|
# TODO: Add polymorphic support.
|
64
87
|
if relationship.polymorphic?
|
@@ -69,25 +92,17 @@ module GraphQL
|
|
69
92
|
end
|
70
93
|
|
71
94
|
if relationship.many?
|
72
|
-
connection relationship.name do
|
95
|
+
connection Types.to_field_name(relationship.name) do
|
73
96
|
type -> { Types.resolve(relationship.klass).connection_type }
|
74
97
|
end
|
75
98
|
else
|
76
|
-
field relationship.name do
|
99
|
+
field Types.to_field_name(relationship.name) do
|
77
100
|
type -> { Types.resolve(relationship.klass) }
|
78
101
|
end
|
79
102
|
end
|
80
103
|
end
|
81
104
|
end
|
82
105
|
end
|
83
|
-
|
84
|
-
def to_name(type)
|
85
|
-
if Types.use_namespaces?
|
86
|
-
NAMESPACE + type.name
|
87
|
-
else
|
88
|
-
type.name
|
89
|
-
end
|
90
|
-
end
|
91
106
|
end
|
92
107
|
|
93
108
|
Types.add_extension Mongoid
|
@@ -1,9 +1,16 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Rails
|
3
|
+
# Base type for operations classes in the Rails app.
|
4
|
+
# Operations are specified in a manner similar to controller actions, and
|
5
|
+
# can access variables and state localized to the current operation.
|
6
|
+
# Classes can define callbacks similar to controller 'filters'.
|
3
7
|
class Operations
|
4
8
|
extend Forwardable
|
5
9
|
include Callbacks
|
6
10
|
|
11
|
+
# Initialize an instance with state pertaining to the current operation.
|
12
|
+
# Accessors for this state are created and proxied through to the
|
13
|
+
# specified options hash.
|
7
14
|
def initialize(options = {})
|
8
15
|
@options = OpenStruct.new(options)
|
9
16
|
self.class.instance_eval do
|
@@ -11,9 +18,21 @@ module GraphQL
|
|
11
18
|
end
|
12
19
|
end
|
13
20
|
|
21
|
+
# Define a query operation.
|
22
|
+
# Definitions should have the following form:
|
23
|
+
#
|
24
|
+
# query :find_cats => [Cat] do
|
25
|
+
# description 'This query returns a list of Cat models'
|
26
|
+
# argument :age, Integer, :required
|
27
|
+
# argument :breed, String
|
28
|
+
# resolve do
|
29
|
+
# raise 'Too old' if args[:age] > 20
|
30
|
+
# Cat.find(age: args[:age], breed: args[:breed])
|
31
|
+
# end
|
32
|
+
# end
|
14
33
|
def self.query(hash, &block)
|
15
34
|
hash = extract_pair(hash)
|
16
|
-
Rails.logger.debug "Adding query: #{
|
35
|
+
Rails.logger.debug "Adding query: #{Types.to_field_name(hash[:name])}"
|
17
36
|
|
18
37
|
definition = QueryDefinition.new(self)
|
19
38
|
definition.run(&block)
|
@@ -25,26 +44,10 @@ module GraphQL
|
|
25
44
|
end
|
26
45
|
|
27
46
|
# TODO: Implement mutations and subscriptions.
|
28
|
-
# TODO: Implement model functions (only, exclude, rename, etc.)
|
29
47
|
|
30
48
|
private
|
31
49
|
|
32
|
-
|
33
|
-
unless hash.length == 1
|
34
|
-
raise 'Hash must contain a single :name => Type pair.'
|
35
|
-
end
|
36
|
-
{name: hash.keys.first, type: hash.values.first}
|
37
|
-
end
|
38
|
-
|
39
|
-
# TODO: Ensure consistent naming convention around everything.
|
40
|
-
def self.to_name(symbol)
|
41
|
-
if Rails.config.camel_case
|
42
|
-
symbol.to_s.camelize(:lower)
|
43
|
-
else
|
44
|
-
symbol.to_s
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
50
|
+
# DSL for query definition.
|
48
51
|
class QueryDefinition < DSL
|
49
52
|
attr_reader :field
|
50
53
|
|
@@ -55,7 +58,7 @@ module GraphQL
|
|
55
58
|
|
56
59
|
def name(name)
|
57
60
|
@name = name
|
58
|
-
@field.name =
|
61
|
+
@field.name = Types.to_field_name(name)
|
59
62
|
end
|
60
63
|
|
61
64
|
def type(type)
|
@@ -69,25 +72,30 @@ module GraphQL
|
|
69
72
|
|
70
73
|
def argument(name, type, required = false)
|
71
74
|
argument = ::GraphQL::Argument.new
|
72
|
-
argument.name =
|
75
|
+
argument.name = Types.to_field_name(name)
|
73
76
|
argument.type = Types.resolve(type, required == :required)
|
74
77
|
@field.arguments[argument.name] = argument
|
75
78
|
end
|
76
79
|
|
77
80
|
def resolve(&block)
|
78
81
|
field.resolve = -> (obj, args, ctx) do
|
82
|
+
# Instantiate the Operations class with state on this query.
|
79
83
|
instance = @klass.new({
|
80
84
|
op: :query, name: @name, type: @type,
|
81
|
-
obj: obj, args: args, ctx: ctx
|
85
|
+
obj: obj, args: args, ctx: ctx, context: ctx
|
82
86
|
})
|
83
87
|
|
84
88
|
begin
|
89
|
+
# Run callbacks for this Operations class.
|
85
90
|
instance.run_callbacks(:perform_operation) do
|
91
|
+
# Call out to the app-defined resolver.
|
86
92
|
instance.instance_eval(&block)
|
87
93
|
end
|
88
94
|
rescue => e
|
95
|
+
# Surface messages from standard errors in GraphQL response.
|
89
96
|
::GraphQL::ExecutionError.new(e.message)
|
90
97
|
rescue ::Exception => e
|
98
|
+
# Log and genericize other runtime errors.
|
91
99
|
Rails.logger.error "Unexpected exception during query: #{@name}"
|
92
100
|
Rails.logger.exception e
|
93
101
|
::GraphQL::ExecutionError.new('Internal error')
|
@@ -95,6 +103,14 @@ module GraphQL
|
|
95
103
|
end
|
96
104
|
end
|
97
105
|
end
|
106
|
+
|
107
|
+
# Extract parts from a hash passed to the operation definition DSL.
|
108
|
+
def self.extract_pair(hash)
|
109
|
+
unless hash.length == 1
|
110
|
+
raise 'Hash must contain a single :name => Type pair.'
|
111
|
+
end
|
112
|
+
{name: hash.keys.first, type: hash.values.first}
|
113
|
+
end
|
98
114
|
end
|
99
115
|
end
|
100
116
|
end
|
data/lib/graphql/rails/schema.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Rails
|
3
|
+
# Defines the GraphQL schema, consisting of
|
4
|
+
# queries, mutations, and subscriptions.
|
3
5
|
module Schema
|
4
6
|
extend self
|
5
7
|
|
8
|
+
# Clear internal state, probably due to a Rails reload.
|
6
9
|
def clear
|
7
10
|
@schema = nil
|
8
11
|
@fields = Hash.new { |hash, key| hash[key] = [] }
|
@@ -10,6 +13,7 @@ module GraphQL
|
|
10
13
|
|
11
14
|
TYPES = [:query, :mutation, :subscription]
|
12
15
|
|
16
|
+
# Register a field in the GraphQL schema.
|
13
17
|
TYPES.each do |type|
|
14
18
|
define_method "add_#{type.to_s}" do |field|
|
15
19
|
@schema = nil # Invalidate cached schema.
|
@@ -17,19 +21,23 @@ module GraphQL
|
|
17
21
|
end
|
18
22
|
end
|
19
23
|
|
24
|
+
# Lazily build the GraphQL schema instance.
|
20
25
|
def instance
|
21
|
-
# TODO: Support max_depth and types.
|
22
|
-
# TODO: Sweep available options and expose in config.
|
23
26
|
@schema ||= GraphQL::Schema.new begin
|
24
|
-
TYPES.reduce({
|
27
|
+
TYPES.reduce({
|
28
|
+
max_depth: Rails.config.max_depth,
|
29
|
+
}) do |schema, type|
|
25
30
|
fields = @fields[type]
|
26
31
|
unless fields.empty?
|
32
|
+
# Build an object for each operation type.
|
27
33
|
schema[type] = GraphQL::ObjectType.define do
|
28
34
|
name type.to_s.capitalize
|
29
35
|
description "Root #{type.to_s} for this schema"
|
36
|
+
# Add a field for each operation.
|
30
37
|
fields.each do |value|
|
31
38
|
field value.name, field: value
|
32
39
|
end
|
40
|
+
# Add the global node ID lookup query.
|
33
41
|
if Rails.config.global_ids && type == :query
|
34
42
|
field :node, field: NodeIdentification.field
|
35
43
|
end
|
data/lib/graphql/rails/types.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Rails
|
3
|
+
# Type system responsible for resolving GraphQL types.
|
4
|
+
# Delegates creation of GraphQL types to ORM-specific extensions.
|
3
5
|
module Types
|
4
6
|
extend self
|
5
7
|
|
8
|
+
# Clear internal state, probably due to a Rails reload.
|
6
9
|
def clear
|
7
10
|
@types = nil
|
8
11
|
extensions.each do |extension|
|
@@ -11,23 +14,25 @@ module GraphQL
|
|
11
14
|
end
|
12
15
|
|
13
16
|
# Resolve an arbitrary type to a GraphQL type.
|
17
|
+
# Lists can be specified with single-element arrays; for example:
|
18
|
+
# [String] resolves to a list of GraphQL::STRING_TYPE objects.
|
14
19
|
def resolve(type, required = false)
|
15
20
|
if type.nil?
|
16
|
-
raise 'Cannot resolve nil type
|
21
|
+
raise 'Cannot resolve nil type'
|
17
22
|
elsif required
|
18
23
|
resolve(type).to_non_null_type
|
19
24
|
elsif type.is_a?(GraphQL::BaseType)
|
20
25
|
type
|
21
26
|
elsif type.is_a?(Array)
|
22
27
|
unless type.length == 1
|
23
|
-
raise 'Lists must be specified with single-element arrays
|
28
|
+
raise 'Lists must be specified with single-element arrays'
|
24
29
|
end
|
25
30
|
resolve(type.first).to_list_type
|
26
31
|
elsif types.include?(type)
|
27
32
|
resolve(types[type])
|
28
33
|
else
|
29
34
|
resolve(try_extensions(:resolve, type) || begin
|
30
|
-
# TODO:
|
35
|
+
# TODO: Decide whether to use String as a fallback, or raise.
|
31
36
|
Rails.logger.warn "Unable to resolve type: #{type.name}"
|
32
37
|
String
|
33
38
|
end)
|
@@ -52,6 +57,29 @@ module GraphQL
|
|
52
57
|
extensions.push extension
|
53
58
|
end
|
54
59
|
|
60
|
+
# Convert a type name to a string with the correct convention,
|
61
|
+
# applying an optional namespace.
|
62
|
+
def to_type_name(name, namespace = '')
|
63
|
+
return namespace + to_type_name(name) unless namespace.blank?
|
64
|
+
if Rails.config.camel_case
|
65
|
+
name.to_s.camelize(:upper)
|
66
|
+
else
|
67
|
+
name.to_s
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Convert a field name to a string with the correct convention.
|
72
|
+
def to_field_name(name)
|
73
|
+
# camelize strips leading underscores, which is undesirable.
|
74
|
+
if name.to_s.starts_with?('_')
|
75
|
+
"_#{to_field_name(name.to_s[1..-1])}"
|
76
|
+
elsif Rails.config.camel_case
|
77
|
+
name.to_s.camelize(:lower)
|
78
|
+
else
|
79
|
+
name.to_s
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
55
83
|
private
|
56
84
|
|
57
85
|
# Default mapping of built-in scalar types to GraphQL types.
|
@@ -74,6 +102,7 @@ module GraphQL
|
|
74
102
|
}
|
75
103
|
end
|
76
104
|
|
105
|
+
# List of registered extensions.
|
77
106
|
def extensions
|
78
107
|
@extensions ||= []
|
79
108
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Reggio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-06-
|
11
|
+
date: 2016-06-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -67,13 +67,11 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.2'
|
69
69
|
description: |
|
70
|
-
Zero-configuration GraphQL + Relay support for Rails.
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
- Automatically maps Mongoid models to GraphQL types.
|
76
|
-
- Seamlessly integrates with CanCan.
|
70
|
+
Zero-configuration GraphQL + Relay support for Rails. Adds a route to process
|
71
|
+
GraphQL operations and provides a visual editor (GraphiQL) during development.
|
72
|
+
Allows you to specify GraphQL queries and mutations as though they were
|
73
|
+
controller actions. Automatically maps Mongoid models to GraphQL types.
|
74
|
+
Seamlessly integrates with CanCan.
|
77
75
|
email:
|
78
76
|
- james.reggio@gmail.com
|
79
77
|
executables: []
|
@@ -110,7 +108,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
110
108
|
requirements:
|
111
109
|
- - '>='
|
112
110
|
- !ruby/object:Gem::Version
|
113
|
-
version:
|
111
|
+
version: 2.1.0
|
114
112
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
113
|
requirements:
|
116
114
|
- - '>='
|