graphql-rails-resolver 0.1.2 → 0.1.3
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/LICENSE +21 -0
- data/README.md +49 -40
- data/lib/graphql/rails/resolver.rb +47 -25
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: daaae82f72286352c8b15b9c04ebe0f29cdb5c7d
|
4
|
+
data.tar.gz: 8d6d288781cbe6643e005a7d618f174f96324833
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1d3162e913409721fb111794ec0cc2d325957852d126513123945683d97ab220fc16475ae391feea818014d02604f9e91a35fda4c8b12025f01e9c99adc4e78
|
7
|
+
data.tar.gz: 2388a47c80fcfd9440322c14355587d6dd546866b95b9ff3402e3761c47faf459a517086c56470c3a850353f116736154bd75e22ce325cdff59f009021c9f152
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# GraphQL::Rails::Resolver
|
2
|
+
## CHANGELOG
|
3
|
+
|
4
|
+
### Version 0.1.3
|
5
|
+
Added `resolve_scope` for resolving model scopes.
|
6
|
+
Fixed `resolve_where` not being called and reworked class inheritance.
|
7
|
+
|
8
|
+
|
9
|
+
### Version 0.1.2
|
10
|
+
Initial release. Took a couple tries to figure out how to import to a new namespace on an existing gem.
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016 Cole Turner
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
#
|
2
|
-
A utility
|
1
|
+
# GraphQL::Rails::Resolver (graphql-rails-resolver)
|
2
|
+
A utility to ease graphql-ruby integration into a Rails project. This resolver offers a declarative approach to resolving Field arguments in a Rails environment.
|
3
3
|
|
4
|
-
# How it works
|
5
|
-
`
|
4
|
+
# How it works
|
5
|
+
`GraphQL::Rails::Resolver` serves as a base class for your GraphQL Ruby schema. When a resolver inherits from this base class, you can easily map arguments in a GraphQL Field to an attribute on an ActiveRecord model or a custom method.
|
6
6
|
|
7
7
|
## Why?
|
8
|
-
**tl;dr;
|
8
|
+
**tl;dr; To achieves three goals: maintainable query type, code re-use, and a declarative integration with Ruby on Rails.**
|
9
9
|
|
10
10
|
Take for example the following Rails model:
|
11
11
|
|
@@ -16,6 +16,7 @@ class Post < ApplicationRecord
|
|
16
16
|
|
17
17
|
scope :is_public, -> { where(is_public: true) }
|
18
18
|
scope :is_private, -> { where(is_public: false) }
|
19
|
+
scope :featured, -> (value) { where(created_at: value) }
|
19
20
|
|
20
21
|
def tags
|
21
22
|
["hello", "world"]
|
@@ -28,11 +29,11 @@ The standard implementation for resolving a `Post` is as follows:
|
|
28
29
|
|
29
30
|
```
|
30
31
|
field :post, PostType do
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
argument :is_public, types.Boolean, default_value: true
|
33
|
+
resolve -> (obj, args, ctx) {
|
34
|
+
post.is_public if args[:is_public]
|
35
|
+
post.is_private unless args[:is_public]
|
36
|
+
}
|
36
37
|
end
|
37
38
|
```
|
38
39
|
|
@@ -43,8 +44,8 @@ Using the pattern from this article, our Field becomes much simpler:
|
|
43
44
|
**/app/graph/types/query_type.rb**
|
44
45
|
```
|
45
46
|
field :post, PostType do
|
46
|
-
|
47
|
-
|
47
|
+
argument :is_public, types.Boolean, default_value: true
|
48
|
+
resolve Resolvers::Post.new
|
48
49
|
end
|
49
50
|
```
|
50
51
|
|
@@ -65,19 +66,27 @@ module Resolvers
|
|
65
66
|
end
|
66
67
|
end
|
67
68
|
```
|
68
|
-
This solution addresses code re-use,
|
69
|
+
This solution addresses code re-use, but these series of conditionals do not allow you to resolve more than one argument, and it may become difficult to maintain this imperative approach.
|
69
70
|
|
70
71
|
|
71
|
-
## Hello Active Resolver
|
72
|
+
## Hello "Active" Resolver
|
72
73
|
**Out with imperative, in with declarative.**
|
73
74
|
|
74
|
-
|
75
|
+
To begin, we install the gem by adding it to our `Gemfile`:
|
76
|
+
|
77
|
+
`
|
78
|
+
gem 'graphql-rails-resolver'
|
79
|
+
`
|
80
|
+
|
81
|
+
This will load a class by the name of `GraphQL::Rails::Resolver`
|
82
|
+
|
83
|
+
Take the Resolver from the previous example. Using `GraphQL::Rails::Resolver`, we inherit and use declarations for arguments and how they will be resolved. These declarations will be mapped to the attributes on the resolved model.
|
75
84
|
|
76
85
|
```
|
77
86
|
# Class name must match the Rails model name exactly.
|
78
87
|
|
79
|
-
class Post < GraphQL::Rails::
|
80
|
-
# ID argument is resolved
|
88
|
+
class Post < GraphQL::Rails::Resolver
|
89
|
+
# ID argument is resolved in base class
|
81
90
|
|
82
91
|
# Resolve :is_public to a class method
|
83
92
|
resolve_method :is_public
|
@@ -87,6 +96,18 @@ class Post < GraphQL::Rails::ActiveResolver
|
|
87
96
|
resolve_where :created_at
|
88
97
|
resolve_where :updated_at
|
89
98
|
|
99
|
+
# Resolve :featured argument with default test: if argument `featured` is present
|
100
|
+
resolve_scope :featured
|
101
|
+
|
102
|
+
# Same resolution as the line above, but send the value to the scope function
|
103
|
+
resolve_scope :featured, :with_value => true
|
104
|
+
|
105
|
+
# Resolve :featured scope if it passes custom argument test
|
106
|
+
resolve_scope :featured, -> (value) { value == :today }
|
107
|
+
|
108
|
+
# Resolve :is_public argument with a different scope name
|
109
|
+
resolve_scope :is_public, -> (value) { value != true }, :scope_name => :is_private
|
110
|
+
|
90
111
|
def is_public(value)
|
91
112
|
@result.is_public if value
|
92
113
|
@result.is_private unless value
|
@@ -95,13 +116,15 @@ class Post < GraphQL::Rails::ActiveResolver
|
|
95
116
|
end
|
96
117
|
```
|
97
118
|
|
98
|
-
In the example above, there are
|
119
|
+
In the example above, there are three declarations:
|
99
120
|
|
100
121
|
`resolve_where` is a declarative approach using `ActiveRecord.where` to resolve arguments.
|
101
122
|
|
102
|
-
`
|
123
|
+
`resolve_scope` is an declarative way to call scopes on a model where a custom test for the argument can be specified with a closure.
|
124
|
+
- Use `with_value` to send the argument value to the scope closure.
|
125
|
+
- Use `scope_name` to map an argument to a scope by another name.
|
103
126
|
|
104
|
-
|
127
|
+
`resolve_method` is an imperative approach that allows completely custom resolution.
|
105
128
|
|
106
129
|
|
107
130
|
|
@@ -115,7 +138,7 @@ end
|
|
115
138
|
```
|
116
139
|
|
117
140
|
### Find Model by ID
|
118
|
-
|
141
|
+
`GraphQL::Rails::Resolver` includes the ability to resolve a model by ID. Using the following method, by default the resolver will find a model by **NodeIdentification.from_global_id(value)** or **Model.where(:id => value)**. This means a model can be resolved by Global ID or Integer ID.
|
119
142
|
```
|
120
143
|
def lookup_id(value)
|
121
144
|
...
|
@@ -123,8 +146,8 @@ end
|
|
123
146
|
```
|
124
147
|
|
125
148
|
|
126
|
-
### Override Default
|
127
|
-
The default behavior is to use `Model.all` to
|
149
|
+
### Override Default Scope
|
150
|
+
The default behavior is to use `Model.all` to scope the resolution. This scope can be changed by providing a block or lambda to the class instance:
|
128
151
|
```
|
129
152
|
Resolvers::Post.new(Proc.new {
|
130
153
|
::Post.where(:created_at => ...)
|
@@ -133,22 +156,8 @@ Resolvers::Post.new(Proc.new {
|
|
133
156
|
|
134
157
|
|
135
158
|
# Needs Help
|
136
|
-
I wanted to release this utility for the hopes of sparking interest in Rails integration with `graphql-ruby`.
|
137
|
-
|
138
|
-
If you wish to contribute to this project, any pull request is warmly welcomed. If time permits, I will continue to update this project to achieve the following:
|
139
|
-
|
140
|
-
### [Making Scopes Declarative](#making-scopes-declarative):
|
141
|
-
For first release, scopes can only be resolved using `resolve_method`. The goal for further development is to stop using `resolve_method` and adapt other methods to facilitate resolution.
|
142
|
-
|
143
|
-
The current syntax planned for scope resolution is as follows, where the argument is passed to the scope:
|
144
|
-
|
145
|
-
```
|
146
|
-
resolve_scope :is_public, -> (args) { args[:is_public] == true }
|
147
|
-
resolve_scope :is_private, -> (args) { args[:is_public] == false }
|
148
|
-
```
|
149
|
-
|
150
|
-
|
159
|
+
I wanted to release this utility for the hopes of sparking interest in Rails integration with `graphql-ruby`. If you wish to contribute to this project, any pull request is warmly welcomed.
|
151
160
|
|
152
161
|
# Credits
|
153
|
-
- Cole Turner ([@colepatrickturner](/colepatrickturner))
|
154
|
-
- Peter Salanki ([@salanki](/salanki))
|
162
|
+
- Cole Turner ([@colepatrickturner](https://github.com/colepatrickturner))
|
163
|
+
- Peter Salanki ([@salanki](https://github.com/salanki))
|
@@ -1,7 +1,9 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Rails
|
3
3
|
class Resolver
|
4
|
-
VERSION = '0.1.
|
4
|
+
VERSION = '0.1.3'
|
5
|
+
|
6
|
+
attr_accessor :resolvers
|
5
7
|
|
6
8
|
def initialize(callable=nil)
|
7
9
|
unless callable.nil?
|
@@ -12,7 +14,7 @@ module GraphQL
|
|
12
14
|
@obj = nil
|
13
15
|
@args = nil
|
14
16
|
@ctx = nil
|
15
|
-
@resolvers =
|
17
|
+
@resolvers = self.class.resolvers
|
16
18
|
end
|
17
19
|
|
18
20
|
def call(obj, args, ctx)
|
@@ -29,7 +31,7 @@ module GraphQL
|
|
29
31
|
|
30
32
|
@resolvers.each do |field,method|
|
31
33
|
if args.key? field
|
32
|
-
method
|
34
|
+
@result = method.call(@result, args[field])
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
@@ -75,28 +77,6 @@ module GraphQL
|
|
75
77
|
"::#{self.class.name.demodulize}".constantize
|
76
78
|
end
|
77
79
|
|
78
|
-
def self.resolve(field, method)
|
79
|
-
self.class_eval do
|
80
|
-
@resolvers[field] = method
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.resolve_where(field)
|
85
|
-
self.class_eval do
|
86
|
-
resolve(field, Proc.new {
|
87
|
-
@result = @result.where(field, @args[field])
|
88
|
-
})
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.resolve_method(field)
|
93
|
-
self.class_eval do
|
94
|
-
resolve(field, Proc.new {
|
95
|
-
send(field, @args[field])
|
96
|
-
})
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
80
|
def lookup_id(value)
|
101
81
|
if is_global_id(value)
|
102
82
|
type_name, id = NodeIdentification.from_global_id(value)
|
@@ -112,7 +92,49 @@ module GraphQL
|
|
112
92
|
end
|
113
93
|
end
|
114
94
|
|
95
|
+
class << self
|
96
|
+
|
97
|
+
def resolvers
|
98
|
+
@resolvers ||= {}
|
99
|
+
@resolvers
|
100
|
+
end
|
101
|
+
|
102
|
+
def resolve(field, method)
|
103
|
+
@resolvers ||= {}
|
104
|
+
@resolvers[field] = method
|
105
|
+
end
|
106
|
+
|
107
|
+
def resolve_where(field)
|
108
|
+
resolve(field, lambda { |obj, value|
|
109
|
+
where = {}
|
110
|
+
where[field] = value
|
111
|
+
|
112
|
+
obj.where(where)
|
113
|
+
})
|
114
|
+
end
|
115
|
+
|
116
|
+
def resolve_scope(field, test=nil, scope_name: nil, with_value: false)
|
117
|
+
test = lambda { |value| value.present? } if test.nil?
|
118
|
+
scope_name = field if scope_name.nil?
|
115
119
|
|
120
|
+
resolve(field, lambda { |obj, value|
|
121
|
+
args = []
|
122
|
+
args.push(value) if with_value
|
123
|
+
|
124
|
+
if test.call(value)
|
125
|
+
obj.send(scope_name, *args)
|
126
|
+
else
|
127
|
+
obj
|
128
|
+
end
|
129
|
+
})
|
130
|
+
end
|
131
|
+
|
132
|
+
def resolve_method(field)
|
133
|
+
resolve(field, lambda { |obj, value|
|
134
|
+
obj.send(field, value)
|
135
|
+
})
|
136
|
+
end
|
137
|
+
end
|
116
138
|
end
|
117
139
|
end
|
118
140
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-rails-resolver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cole Turner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-09-
|
11
|
+
date: 2016-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -44,6 +44,8 @@ executables: []
|
|
44
44
|
extensions: []
|
45
45
|
extra_rdoc_files: []
|
46
46
|
files:
|
47
|
+
- CHANGELOG.md
|
48
|
+
- LICENSE
|
47
49
|
- README.md
|
48
50
|
- lib/graphql/rails.rb
|
49
51
|
- lib/graphql/rails/resolver.rb
|