graphql_rails 0.1.0 → 0.2.0

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile.lock +2 -2
  4. data/README.md +76 -8
  5. data/graphql_rails.gemspec +1 -1
  6. data/lib/graphql_rails/attribute.rb +7 -42
  7. data/lib/graphql_rails/attribute/attribute_type_parser.rb +99 -0
  8. data/lib/graphql_rails/controller.rb +17 -5
  9. data/lib/graphql_rails/controller/action.rb +93 -0
  10. data/lib/graphql_rails/controller/action_configuration.rb +1 -2
  11. data/lib/graphql_rails/controller/controller_function.rb +7 -6
  12. data/lib/graphql_rails/controller/request.rb +3 -3
  13. data/lib/graphql_rails/model.rb +1 -1
  14. data/lib/graphql_rails/model/configuration.rb +14 -50
  15. data/lib/graphql_rails/model/graphql_type_builder.rb +37 -0
  16. data/lib/graphql_rails/router.rb +36 -19
  17. data/lib/graphql_rails/router/{mutation_action.rb → mutation_route.rb} +2 -2
  18. data/lib/graphql_rails/router/{query_action.rb → query_route.rb} +2 -2
  19. data/lib/graphql_rails/router/resource_routes_builder.rb +82 -0
  20. data/lib/graphql_rails/router/route.rb +40 -0
  21. data/lib/graphql_rails/router/schema_builder.rb +9 -5
  22. data/lib/graphql_rails/rspec_controller_helpers.rb +94 -0
  23. data/lib/graphql_rails/version.rb +1 -1
  24. metadata +14 -29
  25. data/lib/graphiti.rb +0 -10
  26. data/lib/graphiti/attribute.rb +0 -86
  27. data/lib/graphiti/controller.rb +0 -54
  28. data/lib/graphiti/controller/action_configuration.rb +0 -46
  29. data/lib/graphiti/controller/configuration.rb +0 -32
  30. data/lib/graphiti/controller/controller_function.rb +0 -41
  31. data/lib/graphiti/controller/request.rb +0 -40
  32. data/lib/graphiti/errors/execution_error.rb +0 -18
  33. data/lib/graphiti/errors/validation_error.rb +0 -26
  34. data/lib/graphiti/model.rb +0 -33
  35. data/lib/graphiti/model/configuration.rb +0 -81
  36. data/lib/graphiti/router.rb +0 -65
  37. data/lib/graphiti/router/action.rb +0 -21
  38. data/lib/graphiti/router/mutation_action.rb +0 -18
  39. data/lib/graphiti/router/query_action.rb +0 -18
  40. data/lib/graphiti/router/resource_actions_builder.rb +0 -82
  41. data/lib/graphiti/router/schema_builder.rb +0 -37
  42. data/lib/graphiti/version.rb +0 -5
  43. data/lib/graphql_rails/controller/action_path_parser.rb +0 -75
  44. data/lib/graphql_rails/router/action.rb +0 -21
  45. data/lib/graphql_rails/router/resource_actions_builder.rb +0 -82
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'graphql_rails/controller/action_path_parser'
4
- require_relative 'request'
5
-
6
- module GraphqlRails
7
- class Controller
8
- # graphql resolver which redirects actions to appropriate controller and controller action
9
- class ControllerFunction < GraphQL::Function
10
- # accepts path of given format "controller_name#action"
11
- attr_reader :type
12
-
13
- def initialize(controller, action_name, return_type)
14
- @controller = controller
15
- @action_name = action_name
16
- @type = return_type
17
- end
18
-
19
- def self.build(action_path, **options)
20
- action_parser = ActionPathParser.new(action_path, **options)
21
-
22
- action_function = Class.new(self) do
23
- action_parser.arguments.each do |action_attribute|
24
- argument(action_attribute.field_name, action_attribute.graphql_field_type)
25
- end
26
- end
27
-
28
- action_function.new(action_parser.controller, action_parser.action_name, action_parser.return_type)
29
- end
30
-
31
- def call(object, inputs, ctx)
32
- request = Request.new(object, inputs, ctx)
33
- controller.new(request).call(action_name)
34
- end
35
-
36
- private
37
-
38
- attr_reader :controller, :action_name
39
- end
40
- end
41
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../errors/execution_error'
4
-
5
- module GraphqlRails
6
- class Controller
7
- # Contains all info related with single request to controller
8
- class Request
9
- attr_accessor :object_to_return
10
- attr_reader :errors
11
-
12
- def initialize(graphql_object, inputs, context)
13
- @graphql_object = graphql_object
14
- @inputs = inputs
15
- @context = context
16
- end
17
-
18
- def errors=(new_errors)
19
- @errors = new_errors
20
-
21
- new_errors.each do |error|
22
- error_message = error.is_a?(String) ? error : error.message
23
- context.add_error(ExecutionError.new(error_message))
24
- end
25
- end
26
-
27
- def no_object_to_return?
28
- !defined?(@object_to_return)
29
- end
30
-
31
- def params
32
- inputs.to_h
33
- end
34
-
35
- private
36
-
37
- attr_reader :graphql_object, :inputs, :context
38
- end
39
- end
40
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module GraphqlRails
4
- # base class which is returned in case something bad happens. Contains all error rendering tructure
5
- class ExecutionError < GraphQL::ExecutionError
6
- def to_h
7
- super.except('locations').merge('type' => type, 'http_status_code' => http_status_code)
8
- end
9
-
10
- def type
11
- 'system_error'
12
- end
13
-
14
- def http_status_code
15
- 500
16
- end
17
- end
18
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module GraphqlRails
4
- # GrapqhQL error that is raised when invalid data is given
5
- class ValidationError < ExecutionError
6
- attr_reader :short_message, :field
7
-
8
- def initialize(short_message, field)
9
- super([field.presence, short_message].compact.join(' '))
10
- @short_message = short_message
11
- @field = field
12
- end
13
-
14
- def type
15
- 'validation_error'
16
- end
17
-
18
- def http_status_code
19
- 422
20
- end
21
-
22
- def to_h
23
- super.merge('field' => field, 'short_message' => short_message)
24
- end
25
- end
26
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'model/configuration'
4
-
5
- module GraphqlRails
6
- # this module allows to convert any ruby class in to grapql type object
7
- #
8
- # usage:
9
- # class YourModel
10
- # include GraphqlRails::Model
11
- #
12
- # graphql do
13
- # attribute :id
14
- # attribute :title
15
- # end
16
- # end
17
- #
18
- # YourModel.new.grapql_type # => type with [:id, :title] attributes
19
- module Model
20
- def self.included(base)
21
- base.extend(ClassMethods)
22
- end
23
-
24
- # static methods for GraphqlRails::Model
25
- module ClassMethods
26
- def graphql
27
- @graphql ||= Model::Configuration.new(self)
28
- yield(@graphql) if block_given?
29
- @graphql
30
- end
31
- end
32
- end
33
- end
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'graphql_rails/attribute'
4
-
5
- module GraphqlRails
6
- module Model
7
- # stores information about model specific config, like attributes and types
8
- class Configuration
9
- attr_reader :attributes
10
-
11
- def initialize(model_class)
12
- @model_class = model_class
13
- @attributes = {}
14
- end
15
-
16
- def attribute(attribute_name, type: nil, hidden: false)
17
- attributes[attribute_name.to_s] = Attribute.new(attribute_name, type, hidden: hidden)
18
- end
19
-
20
- def include_model_attributes(except: [])
21
- except = Array(except).map(&:to_s)
22
-
23
- if defined?(Mongoid) && model_class < Mongoid::Document
24
- assign_default_mongoid_attributes(except: except)
25
- elsif defined?(ActiveRecord) && model_class < ActiveRecord::Base
26
- assign_default_active_record_attributes(except: except)
27
- end
28
- end
29
-
30
- def graphql_type
31
- @graphql_type ||= generate_graphql_type(graphql_type_name, visible_attributes)
32
- end
33
-
34
- private
35
-
36
- attr_reader :model_class
37
-
38
- def visible_attributes
39
- attributes.reject { |_name, attribute| attribute.hidden? }
40
- end
41
-
42
- def graphql_type_name
43
- model_class.name.split('::').last
44
- end
45
-
46
- def generate_graphql_type(type_name, attributes)
47
- GraphQL::ObjectType.define do
48
- name(type_name)
49
- description("Generated programmatically from model: #{type_name}")
50
-
51
- attributes.each_value do |attribute|
52
- field(attribute.field_name, attribute.graphql_field_type, property: attribute.name.to_sym)
53
- end
54
- end
55
- end
56
-
57
- def assign_default_mongoid_attributes(except: [])
58
- allowed_fields = model_class.fields.except('_type', '_id', *except)
59
-
60
- attribute('id', type: 'id')
61
-
62
- allowed_fields.each_value do |field|
63
- attribute(field.name, type: field.type.to_s.split('::').last)
64
- end
65
- end
66
-
67
- def assign_default_active_record_attributes(except: [])
68
- allowed_fields = model_class.columns.index_by(&:name).except('type', *except)
69
-
70
- allowed_fields.each_value do |field|
71
- field_type = field.cast_type.class.to_s.downcase.split('::').last
72
- field_type = 'string' if field_type.ends_with?('string')
73
- field_type = 'date' if field_type.include?('date')
74
- field_type = 'time' if field_type.include?('time')
75
-
76
- attribute(field.name, type: field_type.to_s.split('::').last)
77
- end
78
- end
79
- end
80
- end
81
- end
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/core_ext/string/inflections'
4
-
5
- require_relative 'router/schema_builder'
6
- require_relative 'router/mutation_action'
7
- require_relative 'router/query_action'
8
- require_relative 'router/resource_actions_builder'
9
-
10
- module GraphqlRails
11
- # graphql router that mimics Rails.application.routes
12
- class Router
13
- def self.draw(&block)
14
- router = new
15
- router.instance_eval(&block)
16
- router.graphql_schema
17
- end
18
-
19
- attr_reader :actions, :namespace_name
20
-
21
- def initialize(module_name: '')
22
- @module_name = module_name
23
- @actions ||= Set.new
24
- end
25
-
26
- def scope(**options, &block)
27
- full_module_name = [module_name, options[:module]].reject(&:empty?).join('/')
28
- scoped_router = self.class.new(module_name: full_module_name)
29
- scoped_router.instance_eval(&block)
30
- actions.merge(scoped_router.actions)
31
- end
32
-
33
- def resources(name, **options, &block)
34
- builder_options = default_action_options.merge(options)
35
- actions_builder = ResourceActionsBuilder.new(name, **builder_options)
36
- actions_builder.instance_eval(&block) if block
37
- actions.merge(actions_builder.actions)
38
- end
39
-
40
- def query(name, **options)
41
- actions << build_action(QueryAction, name, **options)
42
- end
43
-
44
- def mutation(name, **options)
45
- actions << build_action(MutationAction, name, **options)
46
- end
47
-
48
- def graphql_schema
49
- SchemaBuilder.new(queries: actions.select(&:query?), mutations: actions.select(&:mutation?)).call
50
- end
51
-
52
- private
53
-
54
- attr_reader :module_name
55
-
56
- def build_action(action_builder, name, **options)
57
- action_options = default_action_options.merge(options)
58
- action_builder.new(name, action_options)
59
- end
60
-
61
- def default_action_options
62
- { module: module_name }
63
- end
64
- end
65
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../controller/controller_function'
4
-
5
- module GraphqlRails
6
- class Router
7
- # Generic class for any type graphql action. Should not be used directly
8
- class Action
9
- attr_reader :name, :controller_action_path
10
-
11
- def initialize(name, to:, **options)
12
- @name = name.to_s.camelize(:lower)
13
- @controller_action_path = [options[:module].to_s, to].reject(&:empty?).join('/')
14
- end
15
-
16
- def options
17
- { function: Controller::ControllerFunction.build(controller_action_path) }
18
- end
19
- end
20
- end
21
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'action'
4
-
5
- module GraphqlRails
6
- class Router
7
- # stores mutation type graphql action info
8
- class MutationAction < Action
9
- def query?
10
- false
11
- end
12
-
13
- def mutation?
14
- true
15
- end
16
- end
17
- end
18
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'action'
4
-
5
- module GraphqlRails
6
- class Router
7
- # stores query type graphql action info
8
- class QueryAction < Action
9
- def query?
10
- true
11
- end
12
-
13
- def mutation?
14
- false
15
- end
16
- end
17
- end
18
- end
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'query_action'
4
- require_relative 'mutation_action'
5
-
6
- module GraphqlRails
7
- class Router
8
- # Generates graphql actions based on resource name and options
9
- class ResourceActionsBuilder
10
- AVAILABLE_ACTIONS = %i[show index create update destroy].freeze
11
-
12
- def initialize(name, only: nil, except: [], **options)
13
- @name = name.to_s
14
-
15
- @options = options
16
- @autogenerated_action_names = initial_action_names(only, except, AVAILABLE_ACTIONS)
17
- end
18
-
19
- def actions
20
- @actions ||= initial_actions
21
- end
22
-
23
- def query(*args)
24
- actions << build_query(*args)
25
- end
26
-
27
- def mutation(*args)
28
- actions << build_mutation(*args)
29
- end
30
-
31
- private
32
-
33
- attr_reader :autogenerated_action_names, :name, :options
34
-
35
- def initial_actions
36
- actions = initial_query_actions
37
- actions << build_mutation(:create, on: :member) if autogenerated_action_names.include?(:create)
38
- actions << build_mutation(:update, on: :member) if autogenerated_action_names.include?(:update)
39
- actions << build_mutation(:destroy, on: :member) if autogenerated_action_names.include?(:destroy)
40
- actions
41
- end
42
-
43
- def initial_query_actions
44
- actions = Set.new
45
-
46
- if autogenerated_action_names.include?(:show)
47
- actions << build_action(QueryAction, 'show', to: "#{name}#show", prefix: '', on: :member)
48
- end
49
-
50
- if autogenerated_action_names.include?(:index)
51
- actions << build_action(QueryAction, 'index', to: "#{name}#index", prefix: '', on: :collection)
52
- end
53
-
54
- actions
55
- end
56
-
57
- def build_mutation(*args)
58
- build_action(MutationAction, *args)
59
- end
60
-
61
- def build_query(*args)
62
- build_action(QueryAction, *args)
63
- end
64
-
65
- def build_action(builder, action, prefix: action, on: :member, **custom_options)
66
- action_options = options.merge(custom_options)
67
- action_name = [prefix, resource_name(on)].reject(&:empty?).join('_')
68
- builder.new(action_name, to: "#{name}##{action}", **action_options)
69
- end
70
-
71
- def initial_action_names(only, except, available)
72
- alowed_actions = Array(only || available) & available
73
- only_actions = alowed_actions.map(&:to_sym) - Array(except).map(&:to_sym)
74
- Set.new(only_actions)
75
- end
76
-
77
- def resource_name(type)
78
- type.to_sym == :member ? name.singularize : name
79
- end
80
- end
81
- end
82
- end