graphiti_gql 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85aecb8badace989fe7f60f3187c189a2d73ce0e99fd4725a8388ed01f2a9cd0
4
- data.tar.gz: 488756eb837219bde85b23a015bd6b6081fa2857c753c3e1837ab0fb7c758dc8
3
+ metadata.gz: 2a19fd68dc698b8a7284fe80686da9f50ee137365508241cd374e32aa6c505aa
4
+ data.tar.gz: d509f137452b90e94986c46267f8728160021de305e599ba47512af9366fe109
5
5
  SHA512:
6
- metadata.gz: ef8b222b5b932bbcb6f21cb8a9480341499086b3daffbb85657af89e208c7b639c4b75fc1ec7f432edb818673ae1e971689fc3f940d11176721a651220f60325
7
- data.tar.gz: 867db73b474375ab9189aa421c953f1ebb008f94ea906d163a4e70f6059ec7df3a699199322cd276f0227d6eebb80d15958587530a8e9e2282ad010139e8e926
6
+ metadata.gz: 22f8643e779d272d806d53179e5f83e6ca6157fc8c261e03ea3232fdf685faa35ad50c70bd3f5f0f445b8bd4578e616b9a538c9205e330a301a30f370b2c612a
7
+ data.tar.gz: ec6e08b56162dc2bb077bfd3cfc5fb5dcf8edcdc3680d940347eb33949f8530967e6d828e0b914f0920a406bad4807d83c643981f9ab0136dd7a3e9e422e068a
data/README.md CHANGED
@@ -1,3 +1,90 @@
1
1
  # GraphitiGql
2
2
 
3
- asdf
3
+ GraphQL bindings for [Graphiti](www.graphiti.dev).
4
+
5
+ Write code like this:
6
+
7
+ ```ruby
8
+ class EmployeeResource < ApplicationResource
9
+ attribute :first_name, :string
10
+ attribute :age, :integer
11
+
12
+ has_many :positions
13
+ end
14
+ ```
15
+
16
+ Get an API like this:
17
+
18
+ ```gql
19
+ query {
20
+ employees(
21
+ filter: { firstName: { match: "arha" } },
22
+ sort: [{ att: age, dir: desc }],
23
+ first: 10,
24
+ after: "abc123"
25
+ ) {
26
+ edges {
27
+ node {
28
+ id
29
+ firstName
30
+ age
31
+ positions {
32
+ nodes {
33
+ title
34
+ }
35
+ }
36
+ }
37
+ cursor
38
+ }
39
+ stats {
40
+ total {
41
+ count
42
+ }
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ ### Getting Started
49
+
50
+ ```ruby
51
+ # Gemfile
52
+ gem 'graphiti'
53
+ gem "graphiti-rails"
54
+ gem 'graphiti_gql'
55
+ ```
56
+
57
+ ```ruby
58
+ # config/routes.rb
59
+
60
+ Rails.application.routes.draw do
61
+ scope path: ApplicationResource.endpoint_namespace do
62
+ mount GraphitiGql::Engine, at: "/gql"
63
+ end
64
+ end
65
+ ```
66
+
67
+ Write your Graphiti code as normal, omit controllers.
68
+
69
+ ### How does it work?
70
+
71
+ This autogenerates `graphql-ruby` code by introspecting Graphiti Resources. Something like this happens under-the-hood:
72
+
73
+ ```ruby
74
+ field :employees, [EmployeeType], null: false do
75
+ argument :filter, EmployeeFilter, required: false
76
+ # ... etc ...
77
+ end
78
+
79
+ def employees(**arguments)
80
+ EmployeeResource.all(**arguments).to_a
81
+ end
82
+ ```
83
+
84
+ In practice it's more complicated, but this is the basic premise - use Graphiti resources to handle query and persistence operations; autogenerate `graphql-ruby` code to expose those Resources as an API. This means we play nicely with e.g. telemetry and error-handling libraries because it's all `graphql-ruby` under-the-hood...except for actually **performing** the operations, which is really more a Ruby thing than a GraphQL thing.
85
+
86
+ ### Caveats
87
+
88
+ This rethinks the responsibilities of Graphiti, coupling the execution cycle to `graphql-ruby`. We do this so we can play nicely with other gems in the GQL ecosystem, and saves on development time by offloading responsibilities. The downside is we can no longer run a `JSON:API` with the same codebase, and certain documentation may be out of date.
89
+
90
+ Longer-term, we should rip out only the parts of Graphiti we really need and redocument.
@@ -2,6 +2,7 @@ module GraphitiGql
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace GraphitiGql
4
4
 
5
+ # TODO improvable?
5
6
  config.after_initialize do
6
7
  # initializer "graphiti_gql.generate_schema" do
7
8
  Dir.glob("#{Rails.root}/app/resources/**/*").each { |f| require(f) }
@@ -10,11 +11,10 @@ module GraphitiGql
10
11
 
11
12
  initializer "graphiti_gql.define_controller" do
12
13
  require "#{Rails.root}/app/controllers/application_controller"
13
- app_controller = ::ApplicationController # todo
14
+ app_controller = GraphitiGql.config.application_controller || ::ApplicationController
14
15
 
15
16
  # rubocop:disable Lint/ConstantDefinitionInBlock(Standard)
16
17
  class GraphitiGql::ExecutionController < app_controller
17
- # register_exception Graphiti::Errors::UnreadableAttribute, message: true
18
18
  def execute
19
19
  params = request.params # avoid strong_parameters
20
20
  variables = params[:variables] || {}
@@ -17,5 +17,21 @@ module GraphitiGql
17
17
  "You are not authorized to read field #{@field}"
18
18
  end
19
19
  end
20
+
21
+ class NullFilter < Base
22
+ def initialize(name)
23
+ @name = name
24
+ end
25
+
26
+ def message
27
+ "Filter '#{@name}' does not support null"
28
+ end
29
+ end
30
+
31
+ class UnsupportedLast < Base
32
+ def message
33
+ "We do not currently support combining 'last' with 'before' or 'after'"
34
+ end
35
+ end
20
36
  end
21
37
  end
@@ -1,71 +1,218 @@
1
1
  # These should be in Graphiti itself, but can't do it quite yet b/c GQL coupling.
2
2
  # Ideally we eventually rip out the parts of Graphiti we need and roll this into
3
3
  # that effort.
4
- module ResourceExtras
5
- extend ActiveSupport::Concern
4
+ module GraphitiGql
5
+ module ResourceExtras
6
+ extend ActiveSupport::Concern
6
7
 
7
- included do
8
- class << self
9
- attr_accessor :graphql_name
8
+ included do
9
+ class << self
10
+ attr_accessor :graphql_name
11
+ end
12
+ end
13
+
14
+ class_methods do
15
+ def attribute(*args)
16
+ super(*args).tap do
17
+ opts = args.extract_options!
18
+ att = config[:attributes][args[0]]
19
+ att[:deprecation_reason] = opts[:deprecation_reason]
20
+ att[:null] = opts.key?(:null) ? opts[:null] : args[0] != :id
21
+ att[:name] = args.first # for easier lookup
22
+ end
23
+ end
10
24
  end
11
25
  end
26
+ Graphiti::Resource.send(:include, ResourceExtras)
27
+
28
+ module FilterExtras
29
+ def filter_param
30
+ default_filter = resource.default_filter if resource.respond_to?(:default_filter)
31
+ default_filter ||= {}
32
+ default_filter.merge(super)
33
+ end
34
+
35
+ def each_filter
36
+ super do |filter, operator, value|
37
+ unless filter.values[0][:allow_nil]
38
+ has_nil = value.nil? || value.is_a?(Array) && value.any?(&:nil?)
39
+ raise Errors::NullFilter.new(filter.keys.first) if has_nil
40
+ end
41
+ yield filter, operator, value
42
+ end
43
+ end
44
+
45
+ # Only for alias, tiny diff
46
+ def filter_via_adapter(filter, operator, value)
47
+ type_name = ::Graphiti::Types.name_for(filter.values.first[:type])
48
+ method = :"filter_#{type_name}_#{operator}"
49
+ name = filter.keys.first
50
+ name = resource.all_attributes[name][:alias] || name
51
+
52
+ if resource.adapter.respond_to?(method)
53
+ resource.adapter.send(method, @scope, name, value)
54
+ else
55
+ raise ::Graphiti::Errors::AdapterNotImplemented.new \
56
+ resource.adapter, name, method
57
+ end
58
+ end
59
+ end
60
+ Graphiti::Scoping::Filter.send(:prepend, FilterExtras)
61
+
62
+ module SortAliasExtras
63
+ def each_sort
64
+ sort_param.each do |sort_hash|
65
+ name = sort_hash.keys.first
66
+ name = resource.all_attributes[name][:alias] || name
67
+ direction = sort_hash.values.first
68
+ yield name, direction
69
+ end
70
+ end
71
+ end
72
+ Graphiti::Scoping::Sort.send(:prepend, SortAliasExtras)
73
+
74
+ module PaginateExtras
75
+ def apply
76
+ if query_hash[:reverse] && (before_cursor || after_cursor)
77
+ raise ::GraphitiGql::Errors::UnsupportedLast
78
+ end
79
+ super
80
+ end
81
+
82
+ def offset
83
+ offset = 0
84
+
85
+ if (value = page_param[:offset])
86
+ offset = value.to_i
87
+ end
12
88
 
13
- class_methods do
14
- def attribute(*args)
15
- super(*args).tap do
16
- opts = args.extract_options!
17
- att = config[:attributes][args[0]]
18
- att[:deprecation_reason] = opts[:deprecation_reason]
19
- att[:null] = opts.key?(:null) ? opts[:null] : args[0] != :id
89
+ if before_cursor&.key?(:offset)
90
+ if page_param.key?(:number)
91
+ raise Errors::UnsupportedBeforeCursor
92
+ end
93
+
94
+ offset = before_cursor[:offset] - (size * number) - 1
95
+ offset = 0 if offset.negative?
96
+ end
97
+
98
+ if after_cursor&.key?(:offset)
99
+ offset = after_cursor[:offset]
100
+ end
101
+
102
+ offset
103
+ end
104
+
105
+ # TODO memoize
106
+ def size
107
+ size = super
108
+ if before_cursor && after_cursor
109
+ diff = before_cursor[:offset] - after_cursor[:offset] - 1
110
+ size = [size, diff].min
111
+ elsif before_cursor
112
+ comparator = query_hash[:reverse] ? :>= : :<=
113
+ if before_cursor[:offset].send(comparator, size)
114
+ diff = before_cursor[:offset] - size
115
+ size = [size, diff].min
116
+ size = 1 if size.zero?
117
+ end
20
118
  end
119
+ size
21
120
  end
22
121
  end
23
- end
122
+ Graphiti::Scoping::Paginate.send(:prepend, PaginateExtras)
24
123
 
25
- Graphiti::Resource.send(:include, ResourceExtras)
124
+ module StatsExtras
125
+ def calculate_stat(name, function)
126
+ config = @resource.all_attributes[name] || {}
127
+ name = config[:alias] || name
128
+ super(name, function)
129
+ end
130
+ end
131
+ Graphiti::Stats::Payload.send(:prepend, StatsExtras)
132
+
133
+ Graphiti::Types[:big_integer] = Graphiti::Types[:integer].dup
134
+ Graphiti::Types[:big_integer][:graphql_type] = ::GraphQL::Types::BigInt
135
+
136
+ ######## support precise_datetime ###########
137
+ #############################################
138
+ definition = Dry::Types::Nominal.new(String)
139
+ _out = definition.constructor do |input|
140
+ input.utc.round(10).iso8601(6)
141
+ end
26
142
 
27
- module FilterExtras
28
- def filter_param
29
- default_filter = resource.default_filter if resource.respond_to?(:default_filter)
30
- default_filter ||= {}
31
- default_filter.merge(super)
143
+ _in = definition.constructor do |input|
144
+ Time.zone.parse(input)
32
145
  end
33
- end
34
- Graphiti::Scoping::Filter.send(:prepend, FilterExtras)
35
-
36
- # ==================================================
37
- # Below is all to support pagination argument 'last'
38
- # ==================================================
39
- module SortExtras
40
- def sort_param
41
- param = super
42
- if query_hash[:reverse]
43
- param = [{ id: :asc }] if param == []
44
- param = param.map do |p|
45
- {}.tap do |hash|
46
- dir = p[p.keys.first]
47
- dir = dir == :asc ? :desc : :asc
48
- hash[p.keys.first] = dir
146
+
147
+ # Register it with Graphiti
148
+ Graphiti::Types[:precise_datetime] = {
149
+ params: _in,
150
+ read: _out,
151
+ write: _in,
152
+ kind: 'scalar',
153
+ canonical_name: :precise_datetime,
154
+ description: 'Datetime with milliseconds'
155
+ }
156
+
157
+ module ActiveRecordAdapterExtras
158
+ extend ActiveSupport::Concern
159
+
160
+ included do
161
+ alias_method :filter_precise_datetime_lt, :filter_lt
162
+ alias_method :filter_precise_datetime_lte, :filter_lte
163
+ alias_method :filter_precise_datetime_gt, :filter_gt
164
+ alias_method :filter_precise_datetime_gte, :filter_gte
165
+ alias_method :filter_precise_datetime_eq, :filter_eq
166
+ alias_method :filter_precise_datetime_not_eq, :filter_not_eq
167
+ end
168
+ end
169
+ Graphiti::Adapters::ActiveRecord.send(:include, ActiveRecordAdapterExtras)
170
+
171
+ Graphiti::Adapters::Abstract.class_eval do
172
+ class << self
173
+ alias :old_default_operators :default_operators
174
+ def default_operators
175
+ old_default_operators.merge(precise_datetime: numerical_operators)
176
+ end
177
+ end
178
+ end
179
+ ########## end support precise_datetime ############
180
+
181
+ # ==================================================
182
+ # Below is all to support pagination argument 'last'
183
+ # ==================================================
184
+ module SortExtras
185
+ def sort_param
186
+ param = super
187
+ if query_hash[:reverse]
188
+ param = [{ id: :asc }] if param == []
189
+ param = param.map do |p|
190
+ {}.tap do |hash|
191
+ dir = p[p.keys.first]
192
+ dir = dir == :asc ? :desc : :asc
193
+ hash[p.keys.first] = dir
194
+ end
49
195
  end
50
196
  end
197
+ param
51
198
  end
52
- param
53
199
  end
54
- end
55
- Graphiti::Scoping::Sort.send(:prepend, SortExtras)
56
- module QueryExtras
57
- def hash
58
- hash = super
59
- hash[:reverse] = true if @params[:reverse]
60
- hash
200
+ Graphiti::Scoping::Sort.send(:prepend, SortExtras)
201
+ module QueryExtras
202
+ def hash
203
+ hash = super
204
+ hash[:reverse] = true if @params[:reverse]
205
+ hash
206
+ end
61
207
  end
62
- end
63
- Graphiti::Query.send(:prepend, QueryExtras)
64
- module ScopeExtras
65
- def resolve(*args)
66
- results = super
67
- results.reverse! if @query.hash[:reverse]
68
- results
208
+
209
+ Graphiti::Query.send(:prepend, QueryExtras)
210
+ module ScopeExtras
211
+ def resolve(*args)
212
+ results = super
213
+ results.reverse! if @query.hash[:reverse]
214
+ results
215
+ end
69
216
  end
70
- end
71
- Graphiti::Scope.send(:prepend, ScopeExtras)
217
+ Graphiti::Scope.send(:prepend, ScopeExtras)
218
+ end
@@ -3,17 +3,19 @@ module GraphitiGql
3
3
  module Fields
4
4
  class Attribute
5
5
  def initialize(name, config)
6
- @name = name
7
6
  @config = config
7
+ @name = name
8
+ @alias = config[:alias]
8
9
  end
9
10
 
10
11
  def apply(type)
11
12
  is_nullable = !!@config[:null]
12
13
  _config = @config
13
14
  _name = @name
15
+ _alias = @alias
14
16
  opts = @config.slice(:null, :deprecation_reason)
15
- type.field(@name, field_type, **opts)
16
- type.define_method @name do
17
+ type.field(_name, field_type, **opts)
18
+ type.define_method _name do
17
19
  if (readable = _config[:readable]).is_a?(Symbol)
18
20
  resource = object.instance_variable_get(:@__graphiti_resource)
19
21
  unless resource.send(readable)
@@ -24,7 +26,7 @@ module GraphitiGql
24
26
  value = if _config[:proc]
25
27
  instance_eval(&_config[:proc])
26
28
  else
27
- object.send(_name)
29
+ object.send(_alias || _name)
28
30
  end
29
31
  return if value.nil?
30
32
  Graphiti::Types[_config[:type]][:read].call(value)
@@ -58,18 +58,21 @@ module GraphitiGql
58
58
  filter_graphql_name = "#{type_name}Filter#{filter_name.to_s.camelize(:lower)}"
59
59
  klass.graphql_name(filter_graphql_name)
60
60
  filter_config[:operators].keys.each do |operator|
61
- canonical_graphiti_type = Graphiti::Types
62
- .name_for(filter_config[:type])
63
- type = GQL_TYPE_MAP[canonical_graphiti_type]
64
- type = String if filter_name == :id
65
- required = !!filter_config[:required] && operator == "eq"
66
-
61
+ graphiti_type = Graphiti::Types[filter_config[:type]]
62
+ type = graphiti_type[:graphql_type]
63
+ if !type
64
+ canonical_graphiti_type = Graphiti::Types
65
+ .name_for(filter_config[:type])
66
+ type = GQL_TYPE_MAP[canonical_graphiti_type]
67
+ type = String if filter_name == :id
68
+ end
69
+
67
70
  if (allowlist = filter_config[:allow])
68
71
  type = define_allowlist_type(filter_graphql_name, allowlist)
69
72
  end
70
73
 
71
74
  type = [type] unless !!filter_config[:single]
72
- klass.argument operator, type, required: required
75
+ klass.argument operator, type, required: false
73
76
  end
74
77
  klass
75
78
  end
@@ -1,21 +1,28 @@
1
1
  module GraphitiGql
2
2
  class Schema
3
+ class PreciseDatetime < GraphQL::Types::ISO8601DateTime
4
+ self.time_precision = 6
5
+ end
6
+
3
7
  GQL_TYPE_MAP = {
4
8
  integer_id: String,
5
9
  string: String,
6
10
  uuid: String,
7
11
  integer: Integer,
12
+ big_integer: GraphQL::Types::BigInt,
8
13
  float: Float,
9
14
  boolean: GraphQL::Schema::Member::GraphQLTypeNames::Boolean,
10
15
  date: GraphQL::Types::ISO8601Date,
11
16
  datetime: GraphQL::Types::ISO8601DateTime,
17
+ precise_datetime: PreciseDatetime,
12
18
  hash: GraphQL::Types::JSON,
13
19
  array: [GraphQL::Types::JSON],
14
20
  array_of_strings: [String],
15
21
  array_of_integers: [Integer],
16
22
  array_of_floats: [Float],
17
23
  array_of_dates: [GraphQL::Types::ISO8601Date],
18
- array_of_datetimes: [GraphQL::Types::ISO8601DateTime]
24
+ array_of_datetimes: [GraphQL::Types::ISO8601DateTime],
25
+ array_of_precise_datetimes: [PreciseDatetime]
19
26
  }
20
27
 
21
28
  class RelayConnectionExtension < GraphQL::Schema::Field::ConnectionExtension
@@ -25,6 +32,14 @@ module GraphitiGql
25
32
  end
26
33
  end
27
34
 
35
+ def self.registry
36
+ Registry.instance
37
+ end
38
+
39
+ def self.print
40
+ GraphQL::Schema::Printer.print_schema(GraphitiGql.schema)
41
+ end
42
+
28
43
  def initialize(resources)
29
44
  @resources = resources
30
45
  end
@@ -35,6 +50,7 @@ module GraphitiGql
35
50
  klass.use(GraphQL::Batch)
36
51
  klass.connections.add(ResponseShim, Connection)
37
52
  klass.connections.add(Array, ToManyConnection)
53
+ klass.orphan_types [GraphQL::Types::JSON]
38
54
  klass
39
55
  end
40
56
  end
@@ -0,0 +1,150 @@
1
+ module GraphitiGql
2
+ module SpecHelper
3
+ extend ActiveSupport::Concern
4
+
5
+ class Node < OpenStruct
6
+ def decoded_id
7
+ Base64.decode64(self.id)
8
+ end
9
+
10
+ def int_id
11
+ decoded_id.to_i
12
+ end
13
+ end
14
+
15
+ class Util
16
+ def self.underscore(hash)
17
+ hash.deep_transform_keys { |k| k.to_s.underscore.to_sym }
18
+ end
19
+ end
20
+
21
+ def query
22
+ name = Schema.registry.key_for(resource)
23
+ q = %|
24
+ query #{name} (
25
+ $filter: #{name}Filter,
26
+ $sort: [#{name}Sort!],
27
+ $first: Int,
28
+ $last: Int,
29
+ $before: String,
30
+ $after: String,
31
+ ) {
32
+ #{resource.graphql_entrypoint} (
33
+ filter: $filter,
34
+ sort: $sort,
35
+ first: $first,
36
+ last: $last,
37
+ before: $before,
38
+ after: $after,
39
+ ) {
40
+ edges {
41
+ node {|
42
+
43
+ fields.each do |name|
44
+ q << %|
45
+ #{name.to_s.camelize(:lower)}|
46
+ end
47
+
48
+ q << %|
49
+ }
50
+ }
51
+ pageInfo {
52
+ startCursor
53
+ endCursor
54
+ hasNextPage
55
+ hasPreviousPage
56
+ }|
57
+
58
+ if params[:stats]
59
+ q << %|
60
+ stats {|
61
+ params[:stats].each_pair do |name, calculations|
62
+ q << %|
63
+ #{name.to_s.camelize(:lower)} {|
64
+ Array(calculations).each do |calc|
65
+ q << %|
66
+ #{calc.to_s.camelize(:lower)}|
67
+ end
68
+
69
+ q << %|
70
+ }|
71
+ end
72
+ q << %|
73
+ }|
74
+ end
75
+
76
+ q << %|
77
+ }
78
+ }
79
+ |
80
+
81
+ q
82
+ end
83
+
84
+ def run
85
+ lambda do
86
+ gql_params = params.deep_transform_keys { |key| key.to_s.camelize(:lower).to_sym }
87
+ (gql_params[:sort] || []).each do |sort|
88
+ sort[:att] = sort[:att].to_s.camelize(:lower)
89
+ sort[:dir] = sort[:dir].to_s
90
+ end
91
+ GraphitiGql.run(query, gql_params, ctx).deep_symbolize_keys
92
+ end
93
+ end
94
+
95
+ def run!
96
+ @response = run.call
97
+ end
98
+
99
+ def response
100
+ @response ||= run!
101
+ end
102
+
103
+ def errors
104
+ response[:errors]
105
+ end
106
+
107
+ def error_messages
108
+ response[:errors].map { |e| e[:message] }
109
+ end
110
+
111
+ def nodes
112
+ return [] unless data
113
+ nodes = edges.map { |e| Util.underscore(e[:node]) }
114
+ nodes.map { |n| ::GraphitiGql::SpecHelper::Node.new(n) }
115
+ end
116
+
117
+ def data
118
+ if response.key?(:data)
119
+ response[:data]
120
+ else
121
+ raise "Tried to access 'data', but these errors were returned instead: #{error_messages.join(". ")}."
122
+ end
123
+ end
124
+
125
+ def edges
126
+ data[data.keys.first][:edges]
127
+ end
128
+
129
+ def stats
130
+ Util.underscore(data[data.keys.first][:stats])
131
+ end
132
+
133
+ def page_info
134
+ Util.underscore(data[data.keys.first][:pageInfo])
135
+ end
136
+
137
+ included do
138
+ let(:params) { {} }
139
+ let(:resource) { described_class }
140
+ let(:ctx) { {} }
141
+ let(:fields) do
142
+ fields = []
143
+ resource.attributes.each_pair do |name, config|
144
+ (fields << name) if config[:readable]
145
+ end
146
+ fields
147
+ end
148
+ end
149
+ end
150
+ end
@@ -1,3 +1,3 @@
1
1
  module GraphitiGql
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/graphiti_gql.rb CHANGED
@@ -29,6 +29,10 @@ require "graphiti_gql/engine" if defined?(Rails)
29
29
  module GraphitiGql
30
30
  class Error < StandardError; end
31
31
 
32
+ class Configuration
33
+ attr_accessor :application_controller
34
+ end
35
+
32
36
  def self.schema!
33
37
  Schema::Registry.instance.clear
34
38
  resources ||= Graphiti.resources.reject(&:abstract_class?)
@@ -39,6 +43,14 @@ module GraphitiGql
39
43
  @schema
40
44
  end
41
45
 
46
+ def self.config
47
+ @config ||= Configuration.new
48
+ end
49
+
50
+ def self.configure
51
+ yield config
52
+ end
53
+
42
54
  def self.entrypoints=(val)
43
55
  @entrypoints = val
44
56
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphiti_gql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-06-03 00:00:00.000000000 Z
11
+ date: 2022-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -108,7 +108,7 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '7.0'
111
- description:
111
+ description:
112
112
  email:
113
113
  - richmolj@gmail.com
114
114
  executables: []
@@ -160,13 +160,14 @@ files:
160
160
  - lib/graphiti_gql/schema/registry.rb
161
161
  - lib/graphiti_gql/schema/resource_type.rb
162
162
  - lib/graphiti_gql/schema/util.rb
163
+ - lib/graphiti_gql/spec_helper.rb
163
164
  - lib/graphiti_gql/version.rb
164
165
  homepage: https://www.graphiti.dev
165
166
  licenses:
166
167
  - MIT
167
168
  metadata:
168
169
  homepage_uri: https://www.graphiti.dev
169
- post_install_message:
170
+ post_install_message:
170
171
  rdoc_options: []
171
172
  require_paths:
172
173
  - lib
@@ -181,8 +182,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
181
182
  - !ruby/object:Gem::Version
182
183
  version: '0'
183
184
  requirements: []
184
- rubygems_version: 3.3.7
185
- signing_key:
185
+ rubygems_version: 3.0.3.1
186
+ signing_key:
186
187
  specification_version: 4
187
188
  summary: GraphQL support for Graphiti
188
189
  test_files: []