graphiform 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +27 -0
- data/lib/graphiform.rb +19 -0
- data/lib/graphiform/active_record_helpers.rb +18 -0
- data/lib/graphiform/core.rb +172 -0
- data/lib/graphiform/fields.rb +224 -0
- data/lib/graphiform/helpers.rb +74 -0
- data/lib/graphiform/skeleton.rb +107 -0
- data/lib/graphiform/version.rb +3 -0
- data/lib/tasks/graphiform_tasks.rake +4 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8626c94d7e632cf64e3052e0e1edd4fdbcd0aa520c67458a69d0f686dcfab261
|
4
|
+
data.tar.gz: c06a1caae7f897f315f5c32606e6cc6180531518904fee20e76f2beae4498b7a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6db928db886d71484bc930d6e42917e1cadbc72e2d6d360aa7c46bf005aa66a0eacb62957d81497d6d01d132ec1be1de85c2da1a76fb8bed372048e262c8316f
|
7
|
+
data.tar.gz: 2bebd2c05fa86e751e3b7902651fa82071a22a4a43fad562b7af852dafe971d3db0dd275606650bd681b9596326f270d4c07c6b5e380fcdd387dd50288150022
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2020 jayce.pulsipher
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Graphiform
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
How to use my plugin.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'graphiform'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install graphiform
|
22
|
+
```
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
Contribution directions go here.
|
26
|
+
|
27
|
+
## License
|
28
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Graphiform'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'bundler/gem_tasks'
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
|
21
|
+
Rake::TestTask.new(:test) do |t|
|
22
|
+
t.libs << 'test'
|
23
|
+
t.pattern = 'test/**/*_test.rb'
|
24
|
+
t.verbose = false
|
25
|
+
end
|
26
|
+
|
27
|
+
task default: :test
|
data/lib/graphiform.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'scopiform'
|
2
|
+
|
3
|
+
require 'graphiform/skeleton'
|
4
|
+
|
5
|
+
require 'graphiform/active_record_helpers'
|
6
|
+
require 'graphiform/core'
|
7
|
+
require 'graphiform/fields'
|
8
|
+
|
9
|
+
module Graphiform
|
10
|
+
def self.included(base)
|
11
|
+
base.class_eval do
|
12
|
+
include Scopiform
|
13
|
+
|
14
|
+
include Graphiform::ActiveRecordHelpers
|
15
|
+
include Graphiform::Core
|
16
|
+
include Graphiform::Fields
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
module Graphiform
|
6
|
+
module ActiveRecordHelpers
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def preferred_name(name_to_prefer = nil)
|
11
|
+
@preferred_name ||= nil # Define to avoid instance variable not initialized warnings
|
12
|
+
@preferred_name = name_to_prefer if name_to_prefer.present?
|
13
|
+
|
14
|
+
@preferred_name || name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
require 'graphiform/helpers'
|
6
|
+
|
7
|
+
module Graphiform
|
8
|
+
module Core
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def graphql_type
|
13
|
+
local_demodulized_name = demodulized_name
|
14
|
+
Helpers.get_const_or_create(local_demodulized_name, ::Types) do
|
15
|
+
Class.new(::Types::BaseObject) do
|
16
|
+
graphql_name local_demodulized_name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def graphql_input
|
22
|
+
local_demodulized_name = demodulized_name
|
23
|
+
Helpers.get_const_or_create(local_demodulized_name, ::Inputs) do
|
24
|
+
Class.new(::Inputs::BaseInput) do
|
25
|
+
graphql_name "#{local_demodulized_name}Input"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def graphql_filter
|
31
|
+
unless defined? @filter
|
32
|
+
local_demodulized_name = demodulized_name
|
33
|
+
@filter = Helpers.get_const_or_create(local_demodulized_name, ::Inputs::Filters) do
|
34
|
+
Class.new(::Inputs::Filters::BaseFilter) do
|
35
|
+
graphql_name "#{local_demodulized_name}Filter"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
@filter.class_eval do
|
39
|
+
argument 'OR', [self], required: false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
@filter
|
44
|
+
end
|
45
|
+
|
46
|
+
def graphql_sort
|
47
|
+
local_demodulized_name = demodulized_name
|
48
|
+
Helpers.get_const_or_create(local_demodulized_name, ::Inputs::Sorts) do
|
49
|
+
Class.new(::Inputs::Sorts::BaseSort) do
|
50
|
+
graphql_name "#{local_demodulized_name}Sort"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def graphql_edge
|
56
|
+
Helpers.get_const_or_create("#{demodulized_name}Edge", ::Types) do
|
57
|
+
node_type = graphql_type
|
58
|
+
Class.new(::Types::BaseEdge) do
|
59
|
+
node_type(node_type)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def graphql_connection
|
65
|
+
connection_name = "#{demodulized_name}Connection"
|
66
|
+
Helpers.get_const_or_create(connection_name, ::Types) do
|
67
|
+
edge_type = graphql_edge
|
68
|
+
Class.new(::Types::BaseConnection) do
|
69
|
+
graphql_name connection_name
|
70
|
+
edge_type(edge_type)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def graphql_base_resolver
|
76
|
+
unless defined? @base_resolver
|
77
|
+
@base_resolver = Helpers.get_const_or_create(demodulized_name, ::Resolvers) do
|
78
|
+
Class.new(::Resolvers::BaseResolver) do
|
79
|
+
# Default resolver just returns the object to prevent exceptions
|
80
|
+
define_method :resolve do |**_args|
|
81
|
+
object
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
local_graphql_filter = graphql_filter
|
87
|
+
local_graphql_sort = graphql_sort
|
88
|
+
|
89
|
+
@base_resolver.class_eval do
|
90
|
+
argument :where, local_graphql_filter, required: false
|
91
|
+
argument :sort, local_graphql_sort, required: false unless local_graphql_sort.arguments.empty?
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
@base_resolver
|
96
|
+
end
|
97
|
+
|
98
|
+
def graphql_query
|
99
|
+
Helpers.get_const_or_create(demodulized_name, ::Resolvers::Queries) do
|
100
|
+
model = self
|
101
|
+
local_graphql_type = graphql_type
|
102
|
+
Class.new(graphql_base_resolver) do
|
103
|
+
type local_graphql_type, null: false
|
104
|
+
|
105
|
+
define_method :resolve do |where: nil|
|
106
|
+
val = model.all
|
107
|
+
val = val.apply_filters(where.to_h) if where.present? && val.respond_to?(:apply_filters)
|
108
|
+
|
109
|
+
val.first
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def graphql_connection_query
|
116
|
+
Helpers.get_const_or_create(demodulized_name, ::Resolvers::ConnectionQueries) do
|
117
|
+
model = self
|
118
|
+
connection_type = graphql_connection
|
119
|
+
Class.new(graphql_base_resolver) do
|
120
|
+
type connection_type, null: false
|
121
|
+
|
122
|
+
define_method :resolve do |where: nil, sort: nil|
|
123
|
+
val = model.all
|
124
|
+
val = val.apply_filters(where.to_h) if where.present? && val.respond_to?(:apply_filters)
|
125
|
+
val = val.apply_sorts(sort.to_h) if sort.present? && val.respond_to?(:apply_sorts)
|
126
|
+
|
127
|
+
val
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def graphql_create_resolver(method_name, resolver_type = graphql_type)
|
134
|
+
Class.new(graphql_base_resolver) do
|
135
|
+
type resolver_type, null: false
|
136
|
+
|
137
|
+
define_method :resolve do |where: nil, **args|
|
138
|
+
where_hash = where.to_h
|
139
|
+
|
140
|
+
val = super(**args)
|
141
|
+
|
142
|
+
val = val.public_send(method_name) if val.respond_to? method_name
|
143
|
+
|
144
|
+
return val.apply_filters(where_hash) if val.respond_to? :apply_filters
|
145
|
+
|
146
|
+
val
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def graphql_create_enum(enum_name)
|
152
|
+
enum_name = enum_name.to_s
|
153
|
+
enum_options = defined_enums[enum_name] || {}
|
154
|
+
|
155
|
+
enum_class_name = "#{demodulized_name}#{enum_name.pluralize.capitalize}"
|
156
|
+
Helpers.get_const_or_create(enum_class_name, ::Enums) do
|
157
|
+
Class.new(::Enums::BaseEnum) do
|
158
|
+
enum_options.each_key do |key|
|
159
|
+
value key
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
def demodulized_name
|
168
|
+
preferred_name.demodulize
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
module Graphiform
|
6
|
+
module Fields
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def graphql_readable_field(
|
11
|
+
name,
|
12
|
+
**options
|
13
|
+
)
|
14
|
+
column_def = column(name)
|
15
|
+
association_def = association(name)
|
16
|
+
|
17
|
+
graphql_add_column_field(name, column_def, **options) if column_def.present?
|
18
|
+
graphql_add_association_field(name, association_def, **options) if association_def.present?
|
19
|
+
graphql_add_method_field(name, **options) unless column_def.present? || association_def.present?
|
20
|
+
|
21
|
+
graphql_add_scopes_to_filter(name)
|
22
|
+
graphql_field_to_sort(name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def graphql_writable_field(
|
26
|
+
name,
|
27
|
+
type: nil,
|
28
|
+
required: false,
|
29
|
+
**_options
|
30
|
+
)
|
31
|
+
name = name.to_sym
|
32
|
+
argument_name = graphql_resolve_argument_name(name)
|
33
|
+
argument_type = graphql_resolve_argument_type(name, type)
|
34
|
+
|
35
|
+
return Helpers.logger.warn "Graphiform: Missing `type` for argument #{name}" if argument_type.nil?
|
36
|
+
|
37
|
+
graphql_input.class_eval do
|
38
|
+
argument argument_name, argument_type, required: required
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def graphql_field(
|
43
|
+
name,
|
44
|
+
write_name: nil,
|
45
|
+
readable: true,
|
46
|
+
writable: false,
|
47
|
+
**options
|
48
|
+
)
|
49
|
+
graphql_readable_field(name, **options) if readable
|
50
|
+
graphql_writable_field(write_name || name, **options) if writable
|
51
|
+
end
|
52
|
+
|
53
|
+
def graphql_fields(*names, **options)
|
54
|
+
names.each do |name|
|
55
|
+
graphql_field(name, **options)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def graphql_readable_fields(*names, **options)
|
60
|
+
names.each do |name|
|
61
|
+
graphql_readable_field(name, **options)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def graphql_writable_fields(*names, **options)
|
66
|
+
names.each do |name|
|
67
|
+
graphql_writable_field(name, **options)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def graphql_resolve_argument_name(name)
|
74
|
+
attributes_name = "#{name}_attributes"
|
75
|
+
|
76
|
+
return attributes_name.to_sym if instance_methods.include?("#{attributes_name}=".to_sym)
|
77
|
+
|
78
|
+
name
|
79
|
+
end
|
80
|
+
|
81
|
+
def graphql_resolve_argument_type(name, type)
|
82
|
+
return type if type.present?
|
83
|
+
|
84
|
+
column_def = column(name)
|
85
|
+
|
86
|
+
return Helpers.graphql_type(column_def.type) if column_def.present?
|
87
|
+
|
88
|
+
association_def = association(name)
|
89
|
+
|
90
|
+
return nil unless Helpers.association_arguments_valid?(association_def, :graphql_input)
|
91
|
+
|
92
|
+
has_many = association_def.macro == :has_many
|
93
|
+
has_many ? [association_def.klass.graphql_input] : association_def.klass.graphql_input
|
94
|
+
end
|
95
|
+
|
96
|
+
def graphql_add_scopes_to_filter(name)
|
97
|
+
added_scopes = auto_scopes_by_attribute(name)
|
98
|
+
|
99
|
+
return if added_scopes.empty?
|
100
|
+
|
101
|
+
association_def = association(name)
|
102
|
+
|
103
|
+
if association_def.present?
|
104
|
+
return unless Helpers.association_arguments_valid?(association_def, :graphql_filter)
|
105
|
+
|
106
|
+
type = association_def.klass.graphql_filter
|
107
|
+
end
|
108
|
+
|
109
|
+
non_sort_by_scopes = added_scopes.select { |scope_def| !scope_def.options || scope_def.options[:type] != :sort }
|
110
|
+
|
111
|
+
non_sort_by_scopes.each do |added_scope|
|
112
|
+
scope_argument_type = type || added_scope.options[:argument_type]
|
113
|
+
if added_scope.options[:type] == :enum
|
114
|
+
enum = graphql_create_enum(name)
|
115
|
+
scope_argument_type = scope_argument_type.is_a?(Array) ? [enum] : enum
|
116
|
+
end
|
117
|
+
add_scope_def_to_filter(added_scope, scope_argument_type)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_scope_def_to_filter(scope_def, argument_type)
|
122
|
+
return unless argument_type
|
123
|
+
|
124
|
+
argument_type = Helpers.graphql_type(argument_type)
|
125
|
+
camelized_attribute = scope_def.attribute.to_s.camelize(:lower)
|
126
|
+
argument_prefix = scope_def.prefix
|
127
|
+
argument_suffix = scope_def.suffix
|
128
|
+
argument_suffix = argument_suffix == '_is' ? '' : argument_suffix
|
129
|
+
argument_name = "#{argument_prefix}#{camelized_attribute}#{argument_suffix}"
|
130
|
+
scope_name = scope_def.name
|
131
|
+
|
132
|
+
graphql_filter.class_eval do
|
133
|
+
argument(
|
134
|
+
argument_name,
|
135
|
+
argument_type,
|
136
|
+
required: false,
|
137
|
+
camelize: false,
|
138
|
+
as: scope_name
|
139
|
+
)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def graphql_field_to_sort(name)
|
144
|
+
column_def = column(name)
|
145
|
+
association_def = association(name)
|
146
|
+
|
147
|
+
type = ::Enums::Sort if column_def.present?
|
148
|
+
type = association_def.klass.graphql_sort if Helpers.association_arguments_valid?(association_def, :graphql_sort)
|
149
|
+
|
150
|
+
return if type.blank?
|
151
|
+
|
152
|
+
local_graphql_sort = graphql_sort
|
153
|
+
|
154
|
+
local_graphql_sort.class_eval do
|
155
|
+
argument(
|
156
|
+
name,
|
157
|
+
type,
|
158
|
+
required: false
|
159
|
+
)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def graphql_add_field_to_type(field_name, type, null = nil)
|
164
|
+
field_name = field_name.to_sym
|
165
|
+
field_options = {}
|
166
|
+
|
167
|
+
if Helpers.resolver?(type)
|
168
|
+
field_options[:resolver] = type
|
169
|
+
else
|
170
|
+
field_options[:type] = type
|
171
|
+
field_options[:null] = null
|
172
|
+
end
|
173
|
+
|
174
|
+
graphql_type.class_eval do
|
175
|
+
field(field_name, **field_options)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def graphql_add_column_field(field_name, column_def, type: nil, null: nil, **_options)
|
180
|
+
type = :string if type.blank? && enum_attribute?(field_name)
|
181
|
+
type = Helpers.graphql_type(type || column_def.type)
|
182
|
+
null = column_def.null if null.nil?
|
183
|
+
|
184
|
+
graphql_add_field_to_type(field_name, type, null)
|
185
|
+
end
|
186
|
+
|
187
|
+
def graphql_add_association_field(field_name, association_def, type: nil, null: nil, **_options)
|
188
|
+
unless association_def.klass.respond_to?(:graphql_type)
|
189
|
+
return Helpers.logger.warn(
|
190
|
+
"Graphiform: `#{name}` trying to add association `#{field_name}` - `#{association_def.klass.name}` does not include Graphiform"
|
191
|
+
)
|
192
|
+
end
|
193
|
+
|
194
|
+
if association_def.klass.graphql_type.fields.empty?
|
195
|
+
return Helpers.logger.warn(
|
196
|
+
"Graphiform: `#{name}` trying to add association `#{field_name}` - `#{association_def.klass.name}` has no fields defined"
|
197
|
+
)
|
198
|
+
end
|
199
|
+
|
200
|
+
has_many = association_def.macro == :has_many
|
201
|
+
klass = association_def.klass
|
202
|
+
|
203
|
+
if has_many
|
204
|
+
graphql_add_field_to_type(
|
205
|
+
"#{field_name}_connection",
|
206
|
+
klass.graphql_create_resolver(association_def.name, graphql_connection),
|
207
|
+
false
|
208
|
+
)
|
209
|
+
end
|
210
|
+
|
211
|
+
type = has_many ? klass.graphql_create_resolver(association_def.name, [klass.graphql_type]) : klass.graphql_type if type.nil?
|
212
|
+
null = association_def.macro != :has_many if null.nil?
|
213
|
+
|
214
|
+
graphql_add_field_to_type(field_name, type, null)
|
215
|
+
end
|
216
|
+
|
217
|
+
def graphql_add_method_field(field_name, type:, null: true, **_options)
|
218
|
+
return Helpers.logger.warn "Graphiform: Missing `type` for field `#{field_name}` in model `#{name}`" if type.nil?
|
219
|
+
|
220
|
+
graphql_add_field_to_type(field_name, type, null)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Graphiform
|
4
|
+
module Helpers
|
5
|
+
def self.logger
|
6
|
+
return Rails.logger if Rails.logger.present?
|
7
|
+
|
8
|
+
@logger ||= Logger.new(STDOUT)
|
9
|
+
@logger
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.graphql_type(active_record_type)
|
13
|
+
is_array = active_record_type.is_a? Array
|
14
|
+
active_record_type = is_array ? active_record_type[0] : active_record_type
|
15
|
+
graphql_type = graphql_type_single(active_record_type)
|
16
|
+
is_array ? [graphql_type] : graphql_type
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.graphql_type_single(active_record_type)
|
20
|
+
return active_record_type unless active_record_type.respond_to?(:to_sym)
|
21
|
+
|
22
|
+
case active_record_type.to_sym
|
23
|
+
when :string, :text
|
24
|
+
GraphQL::Types::String
|
25
|
+
when :date
|
26
|
+
GraphQL::Types::ISO8601Date
|
27
|
+
when :time, :datetime, :timestamp
|
28
|
+
GraphQL::Types::ISO8601DateTime
|
29
|
+
when :integer
|
30
|
+
GraphQL::Types::Int
|
31
|
+
when :float, :decimal
|
32
|
+
GraphQL::Types::Float
|
33
|
+
when :boolean
|
34
|
+
GraphQL::Types::Boolean
|
35
|
+
else
|
36
|
+
active_record_type
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.resolver?(val)
|
41
|
+
val.respond_to?(:ancestors) &&
|
42
|
+
val.ancestors.include?(GraphQL::Schema::Resolver)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.get_const_or_create(const, mod = Object)
|
46
|
+
new_full_const_name = full_const_name("#{mod}::#{const}")
|
47
|
+
new_full_const_name.constantize
|
48
|
+
Object.const_get(new_full_const_name)
|
49
|
+
rescue NameError => e
|
50
|
+
unless full_const_name(e.missing_name) == new_full_const_name.to_s
|
51
|
+
logger.warn "Failed to load #{e.missing_name} when loading constant #{new_full_const_name}"
|
52
|
+
return Object.const_get(new_full_const_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
val = yield
|
56
|
+
mod.const_set(const, val)
|
57
|
+
val
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.full_const_name(name)
|
61
|
+
name = "Object#{name}" if name.starts_with?('::')
|
62
|
+
name = "Object::#{name}" unless name.starts_with?('Object::')
|
63
|
+
|
64
|
+
name
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.association_arguments_valid?(association_def, method)
|
68
|
+
association_def.present? &&
|
69
|
+
association_def.klass.respond_to?(method) &&
|
70
|
+
association_def.klass.send(method).respond_to?(:arguments) &&
|
71
|
+
!association_def.klass.send(method).arguments.empty?
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'graphql'
|
2
|
+
require 'graphiform/helpers'
|
3
|
+
|
4
|
+
module Graphiform
|
5
|
+
# Types
|
6
|
+
Helpers.get_const_or_create('Types') do
|
7
|
+
Module.new
|
8
|
+
end
|
9
|
+
|
10
|
+
Helpers.get_const_or_create('BaseObject', ::Types) do
|
11
|
+
Class.new(::GraphQL::Schema::Object)
|
12
|
+
end
|
13
|
+
|
14
|
+
Helpers.get_const_or_create('BaseEdge', ::Types) do
|
15
|
+
Class.new(::GraphQL::Types::Relay::BaseEdge)
|
16
|
+
end
|
17
|
+
|
18
|
+
Helpers.get_const_or_create('BaseConnection', ::Types) do
|
19
|
+
Class.new(::GraphQL::Types::Relay::BaseConnection)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Inputs
|
23
|
+
Helpers.get_const_or_create('Inputs') do
|
24
|
+
Module.new
|
25
|
+
end
|
26
|
+
|
27
|
+
Helpers.get_const_or_create('BaseInput', ::Inputs) do
|
28
|
+
Class.new(::GraphQL::Schema::InputObject)
|
29
|
+
end
|
30
|
+
|
31
|
+
Helpers.get_const_or_create('Filters', ::Inputs) do
|
32
|
+
Module.new
|
33
|
+
end
|
34
|
+
|
35
|
+
Helpers.get_const_or_create('BaseFilter', ::Inputs::Filters) do
|
36
|
+
Class.new(::GraphQL::Schema::InputObject)
|
37
|
+
end
|
38
|
+
|
39
|
+
Helpers.get_const_or_create('Sorts', ::Inputs) do
|
40
|
+
Module.new
|
41
|
+
end
|
42
|
+
|
43
|
+
Helpers.get_const_or_create('BaseSort', ::Inputs::Sorts) do
|
44
|
+
Class.new(::GraphQL::Schema::InputObject)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Resolvers
|
48
|
+
Helpers.get_const_or_create('Resolvers') do
|
49
|
+
Module.new
|
50
|
+
end
|
51
|
+
|
52
|
+
Helpers.get_const_or_create('BaseResolver', ::Resolvers) do
|
53
|
+
Class.new(::GraphQL::Schema::Resolver)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Queries
|
57
|
+
Helpers.get_const_or_create('Queries', ::Resolvers) do
|
58
|
+
Module.new
|
59
|
+
end
|
60
|
+
|
61
|
+
Helpers.get_const_or_create('BaseQuery', ::Resolvers::Queries) do
|
62
|
+
Class.new(::GraphQL::Schema::Resolver)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Connection Queries
|
66
|
+
Helpers.get_const_or_create('ConnectionQueries', ::Resolvers) do
|
67
|
+
Module.new
|
68
|
+
end
|
69
|
+
|
70
|
+
Helpers.get_const_or_create('BaseQuery', ::Resolvers::ConnectionQueries) do
|
71
|
+
Class.new(::GraphQL::Schema::Resolver)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Enums
|
75
|
+
Helpers.get_const_or_create('Enums') do
|
76
|
+
Module.new
|
77
|
+
end
|
78
|
+
|
79
|
+
Helpers.get_const_or_create('BaseEnum', ::Enums) do
|
80
|
+
Class.new(::GraphQL::Schema::Enum)
|
81
|
+
end
|
82
|
+
|
83
|
+
Helpers.get_const_or_create('Sort', ::Enums) do
|
84
|
+
Class.new(::Enums::BaseEnum) do
|
85
|
+
value 'ASC', 'Sort results in ascending order'
|
86
|
+
value 'DESC', 'Sort results in descending order'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Types
|
92
|
+
# BaseObject
|
93
|
+
# BaseEdge
|
94
|
+
# BaseConnection
|
95
|
+
# Inputs
|
96
|
+
# BaseInput
|
97
|
+
# Filters
|
98
|
+
# BaseFilter
|
99
|
+
# Resolvers
|
100
|
+
# BaseResolver
|
101
|
+
# ConnectionQueries
|
102
|
+
|
103
|
+
# Helpers.get_const_or_create('Types') do
|
104
|
+
# Class.new(::Types::BaseObject) do
|
105
|
+
# graphql_name demodulized_name
|
106
|
+
# end
|
107
|
+
# end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: graphiform
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- jayce.pulsipher
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-03-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.2.7
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.2.7
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: graphql
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.8'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: scopiform
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.2.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.2.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: appraisal
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sqlite3
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: warning
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
- jayce.pulsipher@3-form.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- MIT-LICENSE
|
105
|
+
- README.md
|
106
|
+
- Rakefile
|
107
|
+
- lib/graphiform.rb
|
108
|
+
- lib/graphiform/active_record_helpers.rb
|
109
|
+
- lib/graphiform/core.rb
|
110
|
+
- lib/graphiform/fields.rb
|
111
|
+
- lib/graphiform/helpers.rb
|
112
|
+
- lib/graphiform/skeleton.rb
|
113
|
+
- lib/graphiform/version.rb
|
114
|
+
- lib/tasks/graphiform_tasks.rake
|
115
|
+
homepage: https://github.com/3-form/graphiform
|
116
|
+
licenses:
|
117
|
+
- MIT
|
118
|
+
metadata: {}
|
119
|
+
post_install_message:
|
120
|
+
rdoc_options: []
|
121
|
+
require_paths:
|
122
|
+
- lib
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
requirements: []
|
134
|
+
rubygems_version: 3.0.3
|
135
|
+
signing_key:
|
136
|
+
specification_version: 4
|
137
|
+
summary: Generate GraphQL types, inputs, resolvers, queries, and connections based
|
138
|
+
off whitelisted column and association definitions
|
139
|
+
test_files: []
|