Skip to content

Commit

Permalink
Add documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesreggio committed Jun 8, 2016
1 parent 2207cec commit 7960602
Show file tree
Hide file tree
Showing 16 changed files with 180 additions and 70 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Build
/*.gem
14 changes: 9 additions & 5 deletions app/controllers/graphql/rails/schema_controller.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
module GraphQL
module Rails
class SchemaController < ActionController::Base
# Extensions are dynamically loaded once during engine initialization;
# however, this controller can be reloaded at any time by Rails. To
# preserve extensions, we use the ControllerExtensions module as a cache.
include ControllerExtensions

# Defined in order of increasing specificity.
rescue_from Exception, :with => :internal_error
rescue_from GraphQL::ParseError, :with => :invalid_query
rescue_from JSON::ParserError, :with => :invalid_variables

# Execute a GraphQL query against the current schema.
def execute
query_string = params[:query]
query_variables = to_hash(params[:variables])
render json: Schema.instance.execute(
query_string,
variables: query_variables,
params[:query],
variables: to_hash(params[:variables]),
context: context,
debug: true
debug: Rails.config.debug
)
end

Expand Down
2 changes: 2 additions & 0 deletions config/initializers/graphiql.rb
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
# There is no apparent harm to enabling CSRF token-passing for GraphiQL, even
# if the Rails app doesn't use CSRF protection.
GraphiQL::Rails.config.csrf = true
14 changes: 10 additions & 4 deletions graphql-rails.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@ Gem::Specification.new do |s|
s.email = ['james.reggio@gmail.com']
s.homepage = 'https://github.com/jamesreggio/graphql-rails'
s.summary = 'Zero-configuration GraphQL + Relay support for Rails'
s.description = 'TODO'
s.description = <<-EOM
Zero-configuration GraphQL + Relay support for Rails. Adds a route to process
GraphQL operations and provides a visual editor (GraphiQL) during development.
Allows you to specify GraphQL queries and mutations as though they were
controller actions. Automatically maps Mongoid models to GraphQL types.
Seamlessly integrates with CanCan.
EOM
s.license = 'MIT'

s.files = Dir['{app,config,lib}/**/*', 'README.md', 'LICENSE']
s.files = Dir['{app,config,lib}/**/*', 'LICENSE']
s.required_ruby_version = '>= 2.1.0'

s.add_dependency 'rails', '~> 4'
s.add_dependency 'graphql', '~> 0.13'
s.add_dependency 'graphql-relay', '~> 0.9'
# s.add_dependency 'graphiql-rails', '~> 1.2'
s.add_development_dependency 'sqlite3'
s.add_dependency 'graphiql-rails', '~> 1.2'
end
12 changes: 7 additions & 5 deletions lib/graphql/rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
require 'graphql/relay'
require 'graphiql/rails'

# Order dependent.

require 'graphql/rails/version'
require 'graphql/rails/dsl'
require 'graphql/rails/engine'
require 'graphql/rails/config'
require 'graphql/rails/config'
require 'graphql/rails/engine'

require 'graphql/rails/dsl'
require 'graphql/rails/types'
require 'graphql/rails/node_identification'
require 'graphql/rails/controller_extensions'
require 'graphql/rails/schema'
require 'graphql/rails/callbacks'
require 'graphql/rails/operations'
require 'graphql/rails/node_identification'
require 'graphql/rails/controller_extensions'
17 changes: 14 additions & 3 deletions lib/graphql/rails/callbacks.rb
Original file line number Diff line number Diff line change
@@ -1,31 +1,41 @@
module GraphQL
module Rails
class Operations
# Implement callback methods on Operations.
# These are akin to the 'filters' available on ActionController::Base.
# http://api.rubyonrails.org/classes/AbstractController/Callbacks.html
module Callbacks
extend ActiveSupport::Concern
include ActiveSupport::Callbacks

# All callbacks are registered under the :perform_operation event.
included do
define_callbacks :perform_operation
end

module ClassMethods
# Callbacks can be registered with the following methods:
# before_operation, before_filter
# around_operation, around_filter
# after_operation, after_filter
[:before, :after, :around].each do |callback|
define_method "#{callback}_operation" do |*names, &blk|
insert_callbacks(names, blk) do |name, options|
set_callback(:perform_operation, callback, name, options)
define_method "#{callback}_operation" do |*names, &block|
insert_callbacks(names, block) do |target, options|
set_callback :perform_operation, callback, target, options
end
end
alias_method :"#{callback}_filter", :"#{callback}_operation"
end

private

# Convert :only and :except options into :if and :unless blocks.
def normalize_callback_options(options)
normalize_callback_option(options, :only, :if)
normalize_callback_option(options, :except, :unless)
end

# Convert an operation name-based condition into an executable block.
def normalize_callback_option(options, from, to)
return unless options[from]
check = -> do
Expand All @@ -34,6 +44,7 @@ def normalize_callback_option(options, from, to)
options[to] = Array(options[to]) + [check]
end

# Normalize the arguments passed during callback registration.
def insert_callbacks(callbacks, block = nil)
options = callbacks.extract_options!
normalize_callback_options(options)
Expand Down
9 changes: 9 additions & 0 deletions lib/graphql/rails/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ module GraphQL
module Rails
extend self

# Yields the configuration object to a block, per convention.
def configure
yield config
end

# Configuration for this gem.
def config
@config ||= OpenStruct.new({
# Should graphql-ruby be placed into debug mode?
:debug => ::Rails.env.development?,

# Should the GraphiQL web interface be served?
:graphiql => ::Rails.env.development?,

Expand All @@ -19,6 +24,10 @@ def config
# This is necessary to conform to the Relay Global Object ID spec.
:global_ids => true,

# Maximum nesting for GraphQL queries.
# Specify nil for unlimited nesting depth.
:max_depth => 8,

# Should the following extensions be loaded?
:mongoid => defined?(::Mongoid),
:cancan => defined?(::CanCan),
Expand Down
13 changes: 8 additions & 5 deletions lib/graphql/rails/controller_extensions.rb
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
module GraphQL
module Rails
# Extensions are dynamically loaded once during engine initialization;
# however, SchemaController can be reloaded at any time by Rails. To
# preserve extensions to SchemaController, they're registered here.
module ControllerExtensions
extend self

def add(&block)
callbacks.push block
extensions.push block
end

def included(base)
callbacks.each do |callback|
base.class_eval(&callback)
extensions.each do |extensions|
base.class_eval(&extensions)
end
end

private

def callbacks
@callbacks ||= []
def extensions
@extensions ||= []
end
end
end
Expand Down
7 changes: 7 additions & 0 deletions lib/graphql/rails/dsl.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
module GraphQL
module Rails
# Object that runs a block in the context of itself, but delegates unknown
# methods back to the block's original context. This is useful for creating
# DSLs to aid with object initialization.
#
# Note that this class extends from BasicObject, which means that _all_
# global classes and modules must be prefixed by a double-colon (::) in
# order to resolve.
class DSL < BasicObject
def run(&block)
@self = eval('self', block.binding)
Expand Down
12 changes: 8 additions & 4 deletions lib/graphql/rails/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@ module Rails
class Engine < ::Rails::Engine
isolate_namespace GraphQL::Rails

# Even though we aren't using symbolic autoloading of operations, they
# must be included in autoload_paths in order to be unloaded during
# reload operations.
initializer 'graphql-rails.autoload', :before => :set_autoload_paths do |app|
# Even though we aren't using symbolic autoloading of operations, they
# must be included in autoload_paths in order to be unloaded during
# reload operations.
@graph_path = app.root.join('app', 'graph')
app.config.autoload_paths += [
@graph_path.join('types'),
@graph_path.join('operations'),
]
end

# Extend the Rails logger with a facility for logging exceptions.
initializer 'graphql-rails.logger', :after => :initialize_logger do |app|
logger = ::Rails.logger.clone
logger.class_eval do
Expand All @@ -37,14 +38,16 @@ def exception(e)
Rails.logger.debug 'Initialized logger'
end

# Extensions depend upon a loaded Rails app, so we load them dynamically.
initializer 'graphql-rails.extensions', :after => :load_config_initializers do |app|
# These depend upon a loaded Rails app, so we load them dynamically.
extensions = File.join(File.dirname(__FILE__), 'extensions', '*.rb')
Dir[extensions].each do |file|
require file
end
end

# Hook into Rails reloading in order to clear state from internal
# stateful modules and reload operations from the Rails app.
initializer 'graphql-rails.prepare', :before => :add_to_prepare_blocks do
# The block executes in the context of the reloader, so we have to
# preserve a reference to the engine instance.
Expand All @@ -54,6 +57,7 @@ def exception(e)
end
end

# Clear state and load operations from the Rails app.
def reload!
Types.clear
Schema.clear
Expand Down
8 changes: 6 additions & 2 deletions lib/graphql/rails/extensions/cancan.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module Rails
if Rails.config.cancan
Rails.logger.debug 'Loading CanCan extensions'

# Implement methods from CanCan::ControllerAdditions in Operations.
# http://www.rubydoc.info/github/ryanb/cancan/CanCan/ControllerAdditions
Operations.class_eval do
extend Forwardable
def_delegators :current_ability, :can?, :cannot?
Expand All @@ -26,7 +28,7 @@ def authorize!(*args)
begin
@authorized = true
current_ability.authorize!(*args)
rescue CanCan::AccessDenied
rescue ::CanCan::AccessDenied
raise 'You are not authorized to perform this operation'
end
end
Expand All @@ -36,10 +38,12 @@ def current_ability
end

def current_user
ctx[:current_user]
context[:current_user]
end
end

# Make the current_user available during GraphQL execution via the
# operation context object.
ControllerExtensions.add do
before_filter do
context[:current_user] = current_user
Expand Down
Loading

0 comments on commit 7960602

Please sign in to comment.