graphql-rails 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8386088857c27bbcdea43d4891771e8bea89e8f7
4
- data.tar.gz: 2bedc9fc92d44483612f9253df2dfde7ce2f8973
3
+ metadata.gz: 3a7c5384076b014463594f7ff514f7575c158d7e
4
+ data.tar.gz: ffc42b92d06723a1ea3605d1c11f2ccb2fd1f880
5
5
  SHA512:
6
- metadata.gz: df543107f1fa308fb2228f7951b70ac28c263f0aea93024d121dfcf093d75e515b146bd71655fc0e6e23beb548924d28c7700284833c3f8725b4c573c8c2eec7
7
- data.tar.gz: 06057d2317334ed33e8a16e4f7f35e9b6f8209c2f8207ae03c57421a1f28a3b076879f2fd924e74ac76828caf07b6c875b1feae2b8de7efd32d5545cc12f05b2
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
- query_string,
14
- variables: query_variables,
17
+ params[:query],
18
+ variables: to_hash(params[:variables]),
15
19
  context: context,
16
- debug: true
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
@@ -1 +1,3 @@
1
+ # There is no apparent harm to enabling CSRF token-passing for GraphiQL, even
2
+ # if the Rails app doesn't use CSRF protection.
1
3
  GraphiQL::Rails.config.csrf = true
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, &blk|
15
- insert_callbacks(names, blk) do |name, options|
16
- set_callback(:perform_operation, callback, name, options)
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)
@@ -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
- callbacks.push block
10
+ extensions.push block
8
11
  end
9
12
 
10
13
  def included(base)
11
- callbacks.each do |callback|
12
- base.class_eval(&callback)
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 callbacks
19
- @callbacks ||= []
21
+ def extensions
22
+ @extensions ||= []
20
23
  end
21
24
  end
22
25
  end
@@ -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)
@@ -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
- ctx[:current_user]
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
- NAMESPACE = 'MG'
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 if Types.use_namespaces? && !type_name.starts_with?(NAMESPACE)
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
- # TODO: Support parent types/interfaces.
46
- type_name = to_name(type)
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,5 +1,6 @@
1
1
  module GraphQL
2
2
  module Rails
3
+ # Implements globally-unique object IDs for Relay compatibility.
3
4
  NodeIdentification = GraphQL::Relay::GlobalNodeIdentification.define do
4
5
  object_from_id -> (id, ctx) do
5
6
  Types.lookup(*NodeIdentification.from_global_id(id))
@@ -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: #{to_name(hash[:name])}"
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
- def self.extract_pair(hash)
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 = to_name(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 = to_name(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
@@ -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({}) do |schema, type|
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
@@ -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: Remove this hack.
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
@@ -1,5 +1,5 @@
1
1
  module GraphQL
2
2
  module Rails
3
- VERSION = '0.0.1'
3
+ VERSION = '0.0.2'
4
4
  end
5
5
  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.1
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-07 00:00:00.000000000 Z
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
- - Adds a route to process GraphQL operations.
73
- - Provides a visual editor (GraphiQL) for development.
74
- - Allows you to specify GraphQL queries and mutations as though they were controller actions.
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: '0'
111
+ version: 2.1.0
114
112
  required_rubygems_version: !ruby/object:Gem::Requirement
115
113
  requirements:
116
114
  - - '>='