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 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
  - - '>='