graphql-decorate 0.2.1 → 1.0.2
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/.github/workflows/ci.yml +38 -0
- data/.github/workflows/gem-push-on-release.yml +4 -3
- data/.rubocop.yml +14 -0
- data/Gemfile +4 -2
- data/README.md +62 -31
- data/Rakefile +5 -3
- data/bin/console +4 -3
- data/graphql-decorate.gemspec +33 -18
- data/lib/graphql/decorate/configuration.rb +4 -2
- data/lib/graphql/decorate/decoration.rb +34 -0
- data/lib/graphql/decorate/extract_type.rb +14 -0
- data/lib/graphql/decorate/field_extension.rb +27 -13
- data/lib/graphql/decorate/metadata.rb +29 -0
- data/lib/graphql/decorate/object_integration.rb +15 -19
- data/lib/graphql/decorate/type_attributes.rb +11 -14
- data/lib/graphql/decorate/undecorated_field.rb +114 -0
- data/lib/graphql/decorate/version.rb +3 -1
- data/lib/graphql/decorate.rb +22 -4
- metadata +72 -14
- data/.github/workflows/rspec.yml +0 -32
- data/lib/graphql/decorate/connection.rb +0 -52
- data/lib/graphql/decorate/field_context.rb +0 -18
- data/lib/graphql/decorate/field_integration.rb +0 -38
- data/lib/graphql/decorate/object.rb +0 -71
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ac0949a542979b4218036dae55013d1b148cda15cfafdab0d741aa23d882d289
|
|
4
|
+
data.tar.gz: ac03a2c2c2d554fadd4a7b1ad489d572954c84d7efb77f8afe301c3e8f5909ab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4646f9ea8f791e8ae56163ee8f1ff67849a697365eed6fc5ac9d3ef4327933d6d650cb2aef6cf8160ebea76826de0152d13c9c0b5719da5bdb02e012d9371587
|
|
7
|
+
data.tar.gz: 02e3384f30868f20d490db4dc3481c77bc94f1ce1e3ad446c15a9bb63a3825fd98d7712341452ed532e4a794379503a0e9d4db518080c521d980579337373fd3
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ master ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ master ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
ci:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
ruby-version: ['2.6', '2.7', '3.0']
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v2
|
|
18
|
+
|
|
19
|
+
- name: Set up Ruby
|
|
20
|
+
uses: ruby/setup-ruby@v1
|
|
21
|
+
with:
|
|
22
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
23
|
+
bundler-cache: true
|
|
24
|
+
|
|
25
|
+
- name: Run tests
|
|
26
|
+
run: bundle exec rake
|
|
27
|
+
|
|
28
|
+
- name: Run RuboCop
|
|
29
|
+
run: bundle exec rubocop
|
|
30
|
+
|
|
31
|
+
- name: Check documentation completion
|
|
32
|
+
run: |
|
|
33
|
+
completion_percentage=$(bundle exec yard | tee /dev/stdout | tail -1 | cut -d'%' -f1 | xargs)
|
|
34
|
+
echo $completion_percentage
|
|
35
|
+
if [[ $completion_percentage != "100.00" ]]; then
|
|
36
|
+
echo "YARD documentation must be at 100%"
|
|
37
|
+
exit 2;
|
|
38
|
+
fi
|
|
@@ -11,10 +11,11 @@ jobs:
|
|
|
11
11
|
|
|
12
12
|
steps:
|
|
13
13
|
- uses: actions/checkout@v2
|
|
14
|
-
- name: Set up Ruby
|
|
15
|
-
uses:
|
|
14
|
+
- name: Set up Ruby
|
|
15
|
+
uses: ruby/setup-ruby@v1
|
|
16
16
|
with:
|
|
17
|
-
ruby-version: 2.6
|
|
17
|
+
ruby-version: 2.6
|
|
18
|
+
bundler-cache: true
|
|
18
19
|
|
|
19
20
|
- name: Verify version number matches
|
|
20
21
|
run: |
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require:
|
|
2
|
+
rubocop-rspec
|
|
3
|
+
AllCops:
|
|
4
|
+
NewCops: enable
|
|
5
|
+
TargetRubyVersion: 2.6
|
|
6
|
+
SuggestExtensions: false
|
|
7
|
+
RSpec/FilePath:
|
|
8
|
+
CustomTransform:
|
|
9
|
+
GraphQL: graphql
|
|
10
|
+
Metrics/BlockLength:
|
|
11
|
+
Exclude:
|
|
12
|
+
- 'spec/**/*.rb'
|
|
13
|
+
RSpec/MessageSpies:
|
|
14
|
+
EnforcedStyle: receive
|
data/Gemfile
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
4
6
|
|
|
5
7
|
# Specify your gem's dependencies in graphql-decorate.gemspec
|
|
6
8
|
gemspec
|
data/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
[](https://badge.fury.io/rb/graphql-decorate)
|
|
2
|
+

|
|
3
|
+
|
|
1
4
|
# GraphQL Decorate
|
|
2
5
|
|
|
3
|
-
`graphql-decorate` adds an easy-to-use interface for decorating types in `graphql-ruby
|
|
6
|
+
`graphql-decorate` adds an easy-to-use interface for decorating types in [`graphql-ruby`](https://github.com/rmosolgo/graphql-ruby). It lets
|
|
4
7
|
you move logic out of your type files and keep them declarative.
|
|
5
8
|
|
|
6
9
|
## Installation
|
|
@@ -19,16 +22,19 @@ Or install it yourself as:
|
|
|
19
22
|
|
|
20
23
|
$ gem install graphql-decorate
|
|
21
24
|
|
|
22
|
-
Once the gem is installed, you need to add the
|
|
25
|
+
Once the gem is installed, you need to add the plugin to your schema and the integration into
|
|
26
|
+
your base object class.
|
|
23
27
|
```ruby
|
|
24
|
-
class
|
|
25
|
-
|
|
28
|
+
class Schema < GraphQL::Schema
|
|
29
|
+
use GraphQL::Decorate
|
|
26
30
|
end
|
|
27
31
|
|
|
28
|
-
class
|
|
29
|
-
include GraphQL::Decorate::
|
|
32
|
+
class BaseObject < GraphQL::Schema::Object
|
|
33
|
+
include GraphQL::Decorate::ObjectIntegration
|
|
30
34
|
end
|
|
31
35
|
```
|
|
36
|
+
Note that `use GraphQL::Decorate` must be included in the schema _after_ `query` and `mutation`
|
|
37
|
+
so that the fields to be extended are initialized first.
|
|
32
38
|
|
|
33
39
|
## Usage
|
|
34
40
|
|
|
@@ -48,7 +54,7 @@ class RectangleDecorator < BaseDecorator
|
|
|
48
54
|
end
|
|
49
55
|
end
|
|
50
56
|
|
|
51
|
-
class
|
|
57
|
+
class RectangleType < BaseObject
|
|
52
58
|
decorate_with RectangleDecorator
|
|
53
59
|
|
|
54
60
|
field :area, Int, null: false
|
|
@@ -59,24 +65,27 @@ In this example, the `Rectangle` type is being decorated with a `RectangleDecora
|
|
|
59
65
|
`RectangleDecorator`. All of the methods on the decorator are accessible on the type.
|
|
60
66
|
|
|
61
67
|
### Decorators
|
|
62
|
-
By default, `graphql-decorate` is set up to work with
|
|
68
|
+
By default, `graphql-decorate` is set up to work with [`draper`](https://github.com/drapergem/draper) style decorators. These decorators
|
|
63
69
|
provide a `decorate` method that wraps the original object and returns an instance of the
|
|
64
|
-
decorator. They can also take in
|
|
70
|
+
decorator. They can also take in additional metadata.
|
|
65
71
|
```ruby
|
|
66
|
-
RectangleDecorator.decorate(rectangle, context)
|
|
72
|
+
RectangleDecorator.decorate(rectangle, context: metadata)
|
|
67
73
|
```
|
|
68
74
|
If you are using a different decorator pattern then you can override this default behavior in
|
|
69
75
|
the configuration.
|
|
70
76
|
```ruby
|
|
71
77
|
GraphQL::Decorate.configure do |config|
|
|
72
|
-
config.decorate do |decorator_class, object,
|
|
78
|
+
config.decorate do |decorator_class, object, _metadata|
|
|
73
79
|
decorator_class.decorate_differently(object)
|
|
74
80
|
end
|
|
75
81
|
end
|
|
76
82
|
```
|
|
77
83
|
|
|
78
84
|
### Types
|
|
79
|
-
|
|
85
|
+
Two methods are made available on your type classes: `decorate_with` and `decorate_metadata`.
|
|
86
|
+
Every method that yields the underlying object will also yield the current GraphQL `context`.
|
|
87
|
+
If decoration depends on some context in the current query then you can access it when the field is resolved.
|
|
88
|
+
|
|
80
89
|
#### decorate_with
|
|
81
90
|
`decorate_with` accepts a decorator class that will decorate every instance of your type.
|
|
82
91
|
```ruby
|
|
@@ -85,12 +94,11 @@ class Rectangle < GraphQL::Schema::Object
|
|
|
85
94
|
end
|
|
86
95
|
```
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
`decorate_when` accepts a block which yields the underlying object. If you have multiple
|
|
97
|
+
`decorate_with` optionally accepts a block which yields the underlying object. If you have multiple
|
|
90
98
|
possible decorator classes you can return the one intended for the underling object.
|
|
91
99
|
```ruby
|
|
92
100
|
class Rectangle < GraphQL::Schema::Object
|
|
93
|
-
|
|
101
|
+
decorate_with do |object, _graphql_context|
|
|
94
102
|
if object.length == object.width
|
|
95
103
|
SquareDecorator
|
|
96
104
|
else
|
|
@@ -100,30 +108,50 @@ class Rectangle < GraphQL::Schema::Object
|
|
|
100
108
|
end
|
|
101
109
|
```
|
|
102
110
|
|
|
103
|
-
####
|
|
104
|
-
|
|
105
|
-
|
|
111
|
+
#### decorate_metadata
|
|
112
|
+
If your decorator pattern allows additional metadata to be passed into the decorators, you can
|
|
113
|
+
define it here. By default every metadata hash will contain `{ graphql: true }`. This is
|
|
114
|
+
useful if your decorator logic needs to diverge when used in a GraphQL context. Ideally your
|
|
115
|
+
decorators are agnostic to where they are being used, but it is available if needed.
|
|
116
|
+
|
|
117
|
+
`decorate_metadata` yields a `GraphQL::Decorate::Metadata` metadata instance. It responds to two
|
|
118
|
+
methods: `unscoped` and `scoped`. `unscoped` sets metadata for a resolved field. `scoped` sets
|
|
119
|
+
metadata for a resolved field and all of its child fields. `unscoped` and `scoped` are expected
|
|
120
|
+
to return `Hash`s.
|
|
121
|
+
|
|
106
122
|
```ruby
|
|
107
123
|
class Rectangle < GraphQL::Schema::Object
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
124
|
+
decorate_metadata do |metadata|
|
|
125
|
+
metadata.unscoped do |object, _graphql_context|
|
|
126
|
+
{
|
|
127
|
+
name: object.name
|
|
128
|
+
}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
metadata.scoped do |object, _graphql_context|
|
|
132
|
+
{
|
|
133
|
+
inside_rectangle: true
|
|
134
|
+
}
|
|
135
|
+
end
|
|
112
136
|
end
|
|
113
137
|
end
|
|
114
138
|
```
|
|
115
|
-
`RectangleDecorator` will be initialized with
|
|
139
|
+
`RectangleDecorator` will be initialized with metadata `{ name: <object_name>,
|
|
140
|
+
inside_rectangle: true, graphql: true }`. All child fields of `Rectangle` will be initialized
|
|
141
|
+
with metadata `{ inside_rectangle: true, graphql: true }`.
|
|
116
142
|
|
|
117
143
|
#### Combinations
|
|
118
|
-
You can mix and match these methods to suit your needs. Note that if `
|
|
119
|
-
`
|
|
144
|
+
You can mix and match these methods to suit your needs. Note that if `unscoped` and
|
|
145
|
+
`scoped` are both provided for metadata that `scoped` will override any shared keys.
|
|
120
146
|
```ruby
|
|
121
147
|
class Rectangle < GraphQL::Schema::Object
|
|
122
148
|
decorate_with RectangleDecorator
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
149
|
+
decorate_metadata do |metadata|
|
|
150
|
+
metadata.scoped do |object, _graphql_context|
|
|
151
|
+
{
|
|
152
|
+
name: object.name
|
|
153
|
+
}
|
|
154
|
+
end
|
|
127
155
|
end
|
|
128
156
|
end
|
|
129
157
|
```
|
|
@@ -141,8 +169,11 @@ end
|
|
|
141
169
|
|
|
142
170
|
## Development
|
|
143
171
|
|
|
144
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to
|
|
172
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to
|
|
173
|
+
run the tests. You can also run `bin/console` for an interactive prompt that will allow you to
|
|
174
|
+
experiment.
|
|
145
175
|
|
|
146
176
|
## License
|
|
147
177
|
|
|
148
|
-
The gem is available as open source under the terms of the
|
|
178
|
+
The gem is available as open source under the terms of the
|
|
179
|
+
[MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
require 'graphql/decorate'
|
|
5
6
|
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
|
@@ -10,5 +11,5 @@ require "graphql/decorate"
|
|
|
10
11
|
# require "pry"
|
|
11
12
|
# Pry.start
|
|
12
13
|
|
|
13
|
-
require
|
|
14
|
+
require 'irb'
|
|
14
15
|
IRB.start(__FILE__)
|
data/graphql-decorate.gemspec
CHANGED
|
@@ -1,30 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
|
|
2
|
-
lib = File.expand_path(
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
-
require
|
|
5
|
+
require 'graphql/decorate/version'
|
|
5
6
|
|
|
7
|
+
# rubocop:disable Metrics/BlockLength
|
|
6
8
|
Gem::Specification.new do |spec|
|
|
7
|
-
spec.name
|
|
8
|
-
spec.version
|
|
9
|
-
spec.authors
|
|
10
|
-
spec.email
|
|
9
|
+
spec.name = 'graphql-decorate'
|
|
10
|
+
spec.version = GraphQL::Decorate::VERSION
|
|
11
|
+
spec.authors = ['Ben Brook']
|
|
12
|
+
spec.email = ['bbrook154@gmail.com']
|
|
11
13
|
|
|
12
|
-
spec.summary
|
|
13
|
-
spec.homepage
|
|
14
|
-
spec.license
|
|
14
|
+
spec.summary = 'A decorator integration for the GraphQL gem'
|
|
15
|
+
spec.homepage = 'https://www.github.com/TrueCar/graphql-decorate'
|
|
16
|
+
spec.license = 'MIT'
|
|
17
|
+
spec.metadata = {
|
|
18
|
+
'rubygems_mfa_required' => 'true'
|
|
19
|
+
}
|
|
15
20
|
|
|
16
21
|
# Specify which files should be added to the gem when it is released.
|
|
17
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added
|
|
23
|
+
# into git.
|
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
25
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
26
|
+
f.match(%r{^(test|spec|features)/})
|
|
27
|
+
end
|
|
20
28
|
end
|
|
21
|
-
spec.bindir =
|
|
29
|
+
spec.bindir = 'exe'
|
|
22
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
23
|
-
spec.require_paths = [
|
|
31
|
+
spec.require_paths = ['lib']
|
|
24
32
|
|
|
25
|
-
spec.
|
|
33
|
+
spec.required_ruby_version = '>= 2.6.0'
|
|
26
34
|
|
|
27
|
-
spec.
|
|
28
|
-
|
|
29
|
-
spec.add_development_dependency
|
|
35
|
+
spec.add_runtime_dependency 'graphql', '>= 1.3', '< 2'
|
|
36
|
+
|
|
37
|
+
spec.add_development_dependency 'bundler', '>= 2'
|
|
38
|
+
spec.add_development_dependency 'rake', '>= 12.3.3'
|
|
39
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
40
|
+
spec.add_development_dependency 'rubocop', ' >= 1.11.0 '
|
|
41
|
+
spec.add_development_dependency 'rubocop-rspec', '2.2.0'
|
|
42
|
+
spec.add_development_dependency 'simplecov', '~> 0.21.2'
|
|
43
|
+
spec.add_development_dependency 'yard', '~> 0.9.26'
|
|
30
44
|
end
|
|
45
|
+
# rubocop:enable Metrics/BlockLength
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module GraphQL
|
|
2
4
|
module Decorate
|
|
3
5
|
# Allows overriding default decoration and custom collection class behavior.
|
|
@@ -9,8 +11,8 @@ module GraphQL
|
|
|
9
11
|
attr_accessor :custom_collection_classes
|
|
10
12
|
|
|
11
13
|
def initialize
|
|
12
|
-
@evaluate_decorator = lambda do |decorator_class, object,
|
|
13
|
-
decorator_class.decorate(object, context:
|
|
14
|
+
@evaluate_decorator = lambda do |decorator_class, object, metadata|
|
|
15
|
+
decorator_class.decorate(object, context: metadata)
|
|
14
16
|
end
|
|
15
17
|
@custom_collection_classes = []
|
|
16
18
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GraphQL
|
|
4
|
+
module Decorate
|
|
5
|
+
# Handles decorating an value at runtime given its current field.
|
|
6
|
+
class Decoration
|
|
7
|
+
# Resolve the undecorated_field.value with decoration.
|
|
8
|
+
# @param undecorated_field [GraphQL::Decorate::UndecoratedField]
|
|
9
|
+
# @return [Object] Decorated undecorated_field.value if possible, otherwise the original undecorated_field.value.
|
|
10
|
+
def self.decorate(undecorated_field)
|
|
11
|
+
new(undecorated_field).decorate
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @param undecorated_field [GraphQL::Decorate::UndecoratedField]
|
|
15
|
+
def initialize(undecorated_field)
|
|
16
|
+
@undecorated_field = undecorated_field
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @return [Object] Decorated undecorated_field.value if possible, otherwise the original undecorated_field.value.
|
|
20
|
+
def decorate
|
|
21
|
+
if undecorated_field.decorator_class
|
|
22
|
+
GraphQL::Decorate.configuration.evaluate_decorator.call(undecorated_field.decorator_class,
|
|
23
|
+
undecorated_field.value, undecorated_field.metadata)
|
|
24
|
+
else
|
|
25
|
+
undecorated_field.value
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
attr_reader :undecorated_field
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Allows extraction of a type class from a particular field.
|
|
4
|
+
module ExtractType
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def extract_type(field)
|
|
8
|
+
if field.respond_to?(:of_type)
|
|
9
|
+
extract_type(field.of_type)
|
|
10
|
+
else
|
|
11
|
+
field
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -1,36 +1,50 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
module GraphQL
|
|
3
4
|
module Decorate
|
|
4
5
|
# Extension run after fields are resolved to decorate their value.
|
|
5
6
|
class FieldExtension < GraphQL::Schema::FieldExtension
|
|
7
|
+
include ExtractType
|
|
8
|
+
|
|
6
9
|
# Extension to be called after lazy loading.
|
|
7
10
|
# @param context [GraphQL::Query::Context] The current GraphQL query context.
|
|
8
|
-
# @param value [Object, GraphQL::Schema::Object] The object being decorated. Can
|
|
9
|
-
#
|
|
11
|
+
# @param value [Object, Array, GraphQL::Schema::Object] The object being decorated. Can
|
|
12
|
+
# be a schema object if the field hasn't been resolved yet or a connection.
|
|
13
|
+
# @return [Object] Decorated object.
|
|
10
14
|
def after_resolve(context:, value:, **_rest)
|
|
11
15
|
return if value.nil?
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
resolve_decorated_value(value, context)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def resolve_decorated_value(value, context)
|
|
23
|
+
type = extract_type(context.to_h[:current_field].type)
|
|
24
|
+
|
|
25
|
+
if collection?(value)
|
|
26
|
+
value.each_with_index.map do |item, index|
|
|
27
|
+
decorate(item, type, context, index)
|
|
28
|
+
end
|
|
18
29
|
else
|
|
19
|
-
decorate(value,
|
|
30
|
+
decorate(value, type, context)
|
|
20
31
|
end
|
|
21
32
|
end
|
|
22
33
|
|
|
23
|
-
|
|
34
|
+
def decorate(value, type, context, index = nil)
|
|
35
|
+
undecorated_field = GraphQL::Decorate::UndecoratedField.new(value, type, context, index)
|
|
36
|
+
GraphQL::Decorate::Decoration.decorate(undecorated_field)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def collection?(value)
|
|
40
|
+
collection_classes.any? { |c| value.is_a?(c) }
|
|
41
|
+
end
|
|
24
42
|
|
|
25
43
|
def collection_classes
|
|
26
44
|
klasses = [Array] + GraphQL::Decorate.configuration.custom_collection_classes
|
|
27
45
|
klasses << ::ActiveRecord::Relation if defined?(ActiveRecord::Relation)
|
|
28
46
|
klasses
|
|
29
47
|
end
|
|
30
|
-
|
|
31
|
-
def decorate(object, field_context)
|
|
32
|
-
GraphQL::Decorate::Object.new(object, field_context).decorate
|
|
33
|
-
end
|
|
34
48
|
end
|
|
35
49
|
end
|
|
36
50
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GraphQL
|
|
4
|
+
module Decorate
|
|
5
|
+
# Contains methods to evaluate different types of metadata
|
|
6
|
+
class Metadata
|
|
7
|
+
# @return [Proc]
|
|
8
|
+
attr_reader :unscoped_proc
|
|
9
|
+
|
|
10
|
+
# @return [Proc]
|
|
11
|
+
attr_reader :scoped_proc
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@unscoped_proc = nil
|
|
15
|
+
@scoped_proc = nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @yield [object, graphql_context] Evaluate metadata for a single resolved field
|
|
19
|
+
def unscoped(&block)
|
|
20
|
+
@unscoped_proc = block
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @yield [object, graphql_context] Evaluate metadata for a resolved field and all child fields
|
|
24
|
+
def scoped(&block)
|
|
25
|
+
@scoped_proc = block
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -1,41 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module GraphQL
|
|
2
4
|
module Decorate
|
|
3
5
|
# Extends GraphQL::Schema::Object classes with methods to set the desired decorator class and context.
|
|
4
6
|
module ObjectIntegration
|
|
7
|
+
# @param base [Class] Base class the module is being included in
|
|
8
|
+
# @return [nil]
|
|
9
|
+
def self.included(base)
|
|
10
|
+
base.extend(self)
|
|
11
|
+
end
|
|
12
|
+
|
|
5
13
|
# Decorate the type with a decorator class.
|
|
6
14
|
# @param klass [Class] Class the object should be decorated with.
|
|
7
|
-
def decorate_with(klass)
|
|
15
|
+
def decorate_with(klass = nil, &block)
|
|
8
16
|
@decorator_class = klass
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
# Dynamically choose the decorator class based on the underlying object.
|
|
12
|
-
# @yield [object] Gives the underlying object to the block.
|
|
13
|
-
# @return [Proc] Proc to evaluate decorator class. Proc should return a decorator class.
|
|
14
|
-
def decorate_when(&block)
|
|
15
17
|
@decorator_evaluator = block
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
# Pass additional data to the decorator context (if supported).
|
|
19
21
|
# @yield [object] Gives the underlying object to the block.
|
|
20
22
|
# @return [Proc] Proc to evaluate decorator context. Proc should return Hash.
|
|
21
|
-
def
|
|
22
|
-
@
|
|
23
|
+
def decorate_metadata
|
|
24
|
+
@decorator_metadata ||= GraphQL::Decorate::Metadata.new
|
|
25
|
+
yield(@decorator_metadata)
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
# @return [Class, nil] Gets the currently set decorator class.
|
|
26
|
-
|
|
27
|
-
@decorator_class
|
|
28
|
-
end
|
|
29
|
+
attr_reader :decorator_class
|
|
29
30
|
|
|
30
31
|
# @return [Proc, nil] Gets the currently set decorator evaluator.
|
|
31
|
-
|
|
32
|
-
@decorator_evaluator
|
|
33
|
-
end
|
|
32
|
+
attr_reader :decorator_evaluator
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
def decorator_context_evaluator
|
|
37
|
-
@decorator_context_evaluator
|
|
38
|
-
end
|
|
34
|
+
attr_reader :decorator_metadata
|
|
39
35
|
end
|
|
40
36
|
end
|
|
41
37
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
module GraphQL
|
|
3
4
|
module Decorate
|
|
4
5
|
# Extracts configured decorator attributes from a GraphQL::Schema::Object type.
|
|
@@ -11,6 +12,11 @@ module GraphQL
|
|
|
11
12
|
@type = type
|
|
12
13
|
end
|
|
13
14
|
|
|
15
|
+
# @return [Boolean] True if the type can be decorated, false otherwise
|
|
16
|
+
def decoratable?
|
|
17
|
+
!!(decorator_class || decorator_evaluator || unresolved_type?)
|
|
18
|
+
end
|
|
19
|
+
|
|
14
20
|
# @return [Class, nil] Decorator class for the type if available
|
|
15
21
|
def decorator_class
|
|
16
22
|
get_attribute(:decorator_class)
|
|
@@ -21,9 +27,9 @@ module GraphQL
|
|
|
21
27
|
get_attribute(:decorator_evaluator)
|
|
22
28
|
end
|
|
23
29
|
|
|
24
|
-
# @return [Proc, nil] Decorator
|
|
25
|
-
def
|
|
26
|
-
get_attribute(:
|
|
30
|
+
# @return [Proc, nil] Decorator metadata evaluator for the type if available
|
|
31
|
+
def decorator_metadata
|
|
32
|
+
get_attribute(:decorator_metadata)
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
# @return [GraphQL::Schema::Object, nil] Decorator evaluator for the type if available
|
|
@@ -33,7 +39,7 @@ module GraphQL
|
|
|
33
39
|
|
|
34
40
|
# @return [Boolean] True if type is not yet resolved, false if it is resolved
|
|
35
41
|
def unresolved_type?
|
|
36
|
-
type.respond_to?(:
|
|
42
|
+
type.respond_to?(:kind) && [GraphQL::TypeKinds::INTERFACE, GraphQL::TypeKinds::UNION].include?(type.kind)
|
|
37
43
|
end
|
|
38
44
|
|
|
39
45
|
# @return [Boolean] True if type is resolved, false if it is not resolved
|
|
@@ -41,19 +47,10 @@ module GraphQL
|
|
|
41
47
|
!unresolved_type?
|
|
42
48
|
end
|
|
43
49
|
|
|
44
|
-
# @return [Boolean] True if type is a connection, false if it is resolved
|
|
45
|
-
def connection?
|
|
46
|
-
resolved_type? && type.respond_to?(:node_type)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
50
|
private
|
|
50
51
|
|
|
51
52
|
def get_attribute(name)
|
|
52
|
-
|
|
53
|
-
type.node_type.respond_to?(name) && type.node_type.public_send(name)
|
|
54
|
-
elsif resolved_type?
|
|
55
|
-
type.respond_to?(name) ? type.public_send(name) : nil
|
|
56
|
-
end
|
|
53
|
+
type.respond_to?(name) ? type.public_send(name) : nil
|
|
57
54
|
end
|
|
58
55
|
end
|
|
59
56
|
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GraphQL
|
|
4
|
+
module Decorate
|
|
5
|
+
# Wraps current value, parents, and graphql_context and extracts relevant decoration data to resolve the field.
|
|
6
|
+
class UndecoratedField
|
|
7
|
+
# @return [Object] Value to be decorated
|
|
8
|
+
attr_reader :value
|
|
9
|
+
|
|
10
|
+
# @param value [Object] Value to be decorated
|
|
11
|
+
# @param type [GraphQL::Schema::Object] Type class of value to be decorated
|
|
12
|
+
# @param graphql_context [GraphQL::Query::Context] Current query graphql_context
|
|
13
|
+
def initialize(value, type, graphql_context, index = nil)
|
|
14
|
+
@value = value
|
|
15
|
+
@type = type
|
|
16
|
+
@type_attributes = GraphQL::Decorate::TypeAttributes.new(type)
|
|
17
|
+
@graphql_context = graphql_context
|
|
18
|
+
@default_metadata = { graphql: true }
|
|
19
|
+
@path = graphql_context[:current_path].dup
|
|
20
|
+
@path << index if index
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [Class] Decorator class for the current field
|
|
24
|
+
def decorator_class
|
|
25
|
+
resolved_class = type_attributes.decorator_class || resolve_decorator_class
|
|
26
|
+
return resolved_class if resolved_class
|
|
27
|
+
|
|
28
|
+
class_evaluator = type_attributes.decorator_evaluator || resolve_decorator_evaluator
|
|
29
|
+
class_evaluator&.call(value, graphql_context)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [Hash] Metadata to be provided to a decorator for the current field
|
|
33
|
+
def metadata
|
|
34
|
+
default_metadata.merge(unscoped_metadata, scoped_metadata)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
attr_reader :type, :type_attributes, :graphql_context, :default_metadata, :path
|
|
40
|
+
|
|
41
|
+
def unscoped_metadata
|
|
42
|
+
unscoped_metadata_proc&.call(value, graphql_context) || {}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def scoped_metadata
|
|
46
|
+
insert_scoped_metadata(new_scoped_metadata)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def new_scoped_metadata
|
|
50
|
+
scoped_metadata_proc&.call(value, graphql_context) || {}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# rubocop:disable Metrics/AbcSize
|
|
54
|
+
def insert_scoped_metadata(metadata)
|
|
55
|
+
# Save metadata at each level in the path of the current execution.
|
|
56
|
+
# If a field's direct parent does not have metadata then it will
|
|
57
|
+
# use the next highest metadata in the tree that matches its path.
|
|
58
|
+
scoped_metadata = graphql_context[:scoped_decorator_metadata] ||= {}
|
|
59
|
+
prev_value = {}
|
|
60
|
+
|
|
61
|
+
path[0...-1].each do |step|
|
|
62
|
+
# Write the parent's metadata to the child if it doesn't already exist
|
|
63
|
+
scoped_metadata[step] = { value: prev_value, children: {} } unless scoped_metadata[step]
|
|
64
|
+
# Update the next parent's metadata to include anything at the current level
|
|
65
|
+
prev_value = prev_value.merge(scoped_metadata[step][:value])
|
|
66
|
+
# Move to the child fields and repeat
|
|
67
|
+
scoped_metadata = scoped_metadata[step][:children]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# The last step in the path is the current field, merge in new metadata from
|
|
71
|
+
# the field itself and return it.
|
|
72
|
+
merged_metadata = { value: prev_value.merge(metadata), children: {} }
|
|
73
|
+
scoped_metadata[path[-1]] = merged_metadata
|
|
74
|
+
merged_metadata[:value]
|
|
75
|
+
end
|
|
76
|
+
# rubocop:enable Metrics/AbcSize
|
|
77
|
+
|
|
78
|
+
def unscoped_metadata_proc
|
|
79
|
+
type_attributes.decorator_metadata&.unscoped_proc || resolve_unscoped_proc
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def scoped_metadata_proc
|
|
83
|
+
type_attributes.decorator_metadata&.scoped_proc || resolve_scoped_proc
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def resolve_decorator_class
|
|
87
|
+
resolved_type_attributes&.decorator_class
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def resolve_decorator_evaluator
|
|
91
|
+
resolved_type_attributes&.decorator_evaluator
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def resolve_unscoped_proc
|
|
95
|
+
resolved_type_attributes&.decorator_metadata&.unscoped_proc
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def resolve_scoped_proc
|
|
99
|
+
resolved_type_attributes&.decorator_metadata&.scoped_proc
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def resolved_type_attributes
|
|
103
|
+
@resolved_type_attributes ||= if type_attributes.unresolved_type?
|
|
104
|
+
if type.respond_to?(:resolve_type)
|
|
105
|
+
GraphQL::Decorate::TypeAttributes.new(type.resolve_type(value,
|
|
106
|
+
graphql_context))
|
|
107
|
+
else
|
|
108
|
+
graphql_context.schema.resolve_type(type, value, graphql_context)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
data/lib/graphql/decorate.rb
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'graphql'
|
|
2
4
|
require_relative 'decorate/version'
|
|
3
5
|
require_relative 'decorate/configuration'
|
|
6
|
+
require_relative 'decorate/extract_type'
|
|
4
7
|
require_relative 'decorate/object_integration'
|
|
5
|
-
require_relative 'decorate/field_integration'
|
|
6
8
|
require_relative 'decorate/field_extension'
|
|
7
|
-
require_relative 'decorate/
|
|
9
|
+
require_relative 'decorate/decoration'
|
|
8
10
|
require_relative 'decorate/type_attributes'
|
|
9
|
-
require_relative 'decorate/
|
|
10
|
-
require_relative 'decorate/
|
|
11
|
+
require_relative 'decorate/undecorated_field'
|
|
12
|
+
require_relative 'decorate/metadata'
|
|
11
13
|
|
|
12
14
|
# Matching the graphql-ruby namespace
|
|
13
15
|
module GraphQL
|
|
14
16
|
# Entry point for graphql-decorate. Handles configuration.
|
|
15
17
|
module Decorate
|
|
18
|
+
extend ExtractType
|
|
19
|
+
|
|
16
20
|
# @return [Configuration] Returns a new instance of GraphQL::Decorate::Configuration.
|
|
17
21
|
def self.configuration
|
|
18
22
|
@configuration ||= Configuration.new
|
|
@@ -27,5 +31,19 @@ module GraphQL
|
|
|
27
31
|
def self.reset_configuration!
|
|
28
32
|
@configuration = Configuration.new
|
|
29
33
|
end
|
|
34
|
+
|
|
35
|
+
# @param schema_defn [GraphQL::Schema] Current schema class
|
|
36
|
+
# @return [nil]
|
|
37
|
+
def self.use(schema_defn)
|
|
38
|
+
schema_defn.types.each do |_name, type|
|
|
39
|
+
next unless type.respond_to?(:fields)
|
|
40
|
+
|
|
41
|
+
type.fields.each do |_name, field|
|
|
42
|
+
field_type = extract_type(field.type_class.type)
|
|
43
|
+
type_attributes = GraphQL::Decorate::TypeAttributes.new(field_type)
|
|
44
|
+
field.type_class.extension(GraphQL::Decorate::FieldExtension) if type_attributes.decoratable?
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
30
48
|
end
|
|
31
49
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: graphql-decorate
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2
|
|
4
|
+
version: 1.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ben Brook
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-04-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: graphql
|
|
@@ -48,16 +48,16 @@ dependencies:
|
|
|
48
48
|
name: rake
|
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
|
-
- - "
|
|
51
|
+
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
53
|
+
version: 12.3.3
|
|
54
54
|
type: :development
|
|
55
55
|
prerelease: false
|
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
|
-
- - "
|
|
58
|
+
- - ">="
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version:
|
|
60
|
+
version: 12.3.3
|
|
61
61
|
- !ruby/object:Gem::Dependency
|
|
62
62
|
name: rspec
|
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -72,6 +72,62 @@ dependencies:
|
|
|
72
72
|
- - "~>"
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
74
|
version: '3.0'
|
|
75
|
+
- !ruby/object:Gem::Dependency
|
|
76
|
+
name: rubocop
|
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 1.11.0
|
|
82
|
+
type: :development
|
|
83
|
+
prerelease: false
|
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: 1.11.0
|
|
89
|
+
- !ruby/object:Gem::Dependency
|
|
90
|
+
name: rubocop-rspec
|
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - '='
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: 2.2.0
|
|
96
|
+
type: :development
|
|
97
|
+
prerelease: false
|
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - '='
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 2.2.0
|
|
103
|
+
- !ruby/object:Gem::Dependency
|
|
104
|
+
name: simplecov
|
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: 0.21.2
|
|
110
|
+
type: :development
|
|
111
|
+
prerelease: false
|
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: 0.21.2
|
|
117
|
+
- !ruby/object:Gem::Dependency
|
|
118
|
+
name: yard
|
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - "~>"
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: 0.9.26
|
|
124
|
+
type: :development
|
|
125
|
+
prerelease: false
|
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: 0.9.26
|
|
75
131
|
description:
|
|
76
132
|
email:
|
|
77
133
|
- bbrook154@gmail.com
|
|
@@ -79,9 +135,10 @@ executables: []
|
|
|
79
135
|
extensions: []
|
|
80
136
|
extra_rdoc_files: []
|
|
81
137
|
files:
|
|
138
|
+
- ".github/workflows/ci.yml"
|
|
82
139
|
- ".github/workflows/gem-push-on-release.yml"
|
|
83
|
-
- ".github/workflows/rspec.yml"
|
|
84
140
|
- ".gitignore"
|
|
141
|
+
- ".rubocop.yml"
|
|
85
142
|
- Gemfile
|
|
86
143
|
- LICENSE.txt
|
|
87
144
|
- README.md
|
|
@@ -91,18 +148,19 @@ files:
|
|
|
91
148
|
- graphql-decorate.gemspec
|
|
92
149
|
- lib/graphql/decorate.rb
|
|
93
150
|
- lib/graphql/decorate/configuration.rb
|
|
94
|
-
- lib/graphql/decorate/
|
|
95
|
-
- lib/graphql/decorate/
|
|
151
|
+
- lib/graphql/decorate/decoration.rb
|
|
152
|
+
- lib/graphql/decorate/extract_type.rb
|
|
96
153
|
- lib/graphql/decorate/field_extension.rb
|
|
97
|
-
- lib/graphql/decorate/
|
|
98
|
-
- lib/graphql/decorate/object.rb
|
|
154
|
+
- lib/graphql/decorate/metadata.rb
|
|
99
155
|
- lib/graphql/decorate/object_integration.rb
|
|
100
156
|
- lib/graphql/decorate/type_attributes.rb
|
|
157
|
+
- lib/graphql/decorate/undecorated_field.rb
|
|
101
158
|
- lib/graphql/decorate/version.rb
|
|
102
159
|
homepage: https://www.github.com/TrueCar/graphql-decorate
|
|
103
160
|
licenses:
|
|
104
161
|
- MIT
|
|
105
|
-
metadata:
|
|
162
|
+
metadata:
|
|
163
|
+
rubygems_mfa_required: 'true'
|
|
106
164
|
post_install_message:
|
|
107
165
|
rdoc_options: []
|
|
108
166
|
require_paths:
|
|
@@ -111,14 +169,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
111
169
|
requirements:
|
|
112
170
|
- - ">="
|
|
113
171
|
- !ruby/object:Gem::Version
|
|
114
|
-
version:
|
|
172
|
+
version: 2.6.0
|
|
115
173
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
174
|
requirements:
|
|
117
175
|
- - ">="
|
|
118
176
|
- !ruby/object:Gem::Version
|
|
119
177
|
version: '0'
|
|
120
178
|
requirements: []
|
|
121
|
-
rubygems_version: 3.0.3
|
|
179
|
+
rubygems_version: 3.0.3.1
|
|
122
180
|
signing_key:
|
|
123
181
|
specification_version: 4
|
|
124
182
|
summary: A decorator integration for the GraphQL gem
|
data/.github/workflows/rspec.yml
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# This workflow uses actions that are not certified by GitHub.
|
|
2
|
-
# They are provided by a third-party and are governed by
|
|
3
|
-
# separate terms of service, privacy policy, and support
|
|
4
|
-
# documentation.
|
|
5
|
-
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
|
6
|
-
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
|
7
|
-
|
|
8
|
-
name: Ruby
|
|
9
|
-
|
|
10
|
-
on:
|
|
11
|
-
push:
|
|
12
|
-
branches: [ master ]
|
|
13
|
-
pull_request:
|
|
14
|
-
branches: [ master ]
|
|
15
|
-
|
|
16
|
-
jobs:
|
|
17
|
-
test:
|
|
18
|
-
|
|
19
|
-
runs-on: ubuntu-latest
|
|
20
|
-
strategy:
|
|
21
|
-
matrix:
|
|
22
|
-
ruby-version: ['2.6', '2.7', '3.0']
|
|
23
|
-
|
|
24
|
-
steps:
|
|
25
|
-
- uses: actions/checkout@v2
|
|
26
|
-
- name: Set up Ruby
|
|
27
|
-
uses: ruby/setup-ruby@v1
|
|
28
|
-
with:
|
|
29
|
-
ruby-version: ${{ matrix.ruby-version }}
|
|
30
|
-
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
31
|
-
- name: Run tests
|
|
32
|
-
run: bundle exec rake
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
module GraphQL
|
|
3
|
-
module Decorate
|
|
4
|
-
# Wraps a GraphQL::Pagination::Connection object to decorate values after pagination is applied.
|
|
5
|
-
class Connection
|
|
6
|
-
# @return [GraphQL::Pagination::Connection] Connection being decorated
|
|
7
|
-
attr_reader :connection
|
|
8
|
-
|
|
9
|
-
# @return [GraphQL::Decorate::FieldContext] Current field context
|
|
10
|
-
attr_reader :field_context
|
|
11
|
-
|
|
12
|
-
def initialize(connection, field_context)
|
|
13
|
-
@connection = connection
|
|
14
|
-
@field_context = field_context
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# @return [Array] Decorated nodes after pagination is applied
|
|
18
|
-
def nodes
|
|
19
|
-
nodes = @connection.nodes
|
|
20
|
-
nodes.map { |node| GraphQL::Decorate::Object.new(node, field_context).decorate }
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# @see nodes
|
|
24
|
-
# @return [Array] Decorated nodes after pagination is applied
|
|
25
|
-
def edge_nodes
|
|
26
|
-
nodes
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
class << self
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def method_missing(symbol, *args, &block)
|
|
33
|
-
@connection.class.send(symbol, *args, &block)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def respond_to_missing?(method, include_private = false)
|
|
37
|
-
@connection.class.respond_to_missing(method, include_private)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
private
|
|
42
|
-
|
|
43
|
-
def method_missing(symbol, *args, &block)
|
|
44
|
-
@connection.send(symbol, *args, &block)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def respond_to_missing?(method, include_private = false)
|
|
48
|
-
@connection.respond_to_missing(method, include_private)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
module GraphQL
|
|
3
|
-
module Decorate
|
|
4
|
-
# Wraps current GraphQL::Query::Context and options provided to a field for portability.
|
|
5
|
-
class FieldContext
|
|
6
|
-
# @return [GraphQL::Query::Context] Current GraphQL query context
|
|
7
|
-
attr_reader :context
|
|
8
|
-
|
|
9
|
-
# @return [Hash] Options provided to the field being decorated
|
|
10
|
-
attr_reader :options
|
|
11
|
-
|
|
12
|
-
def initialize(context, options)
|
|
13
|
-
@context = context
|
|
14
|
-
@options = options
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
module GraphQL
|
|
2
|
-
module Decorate
|
|
3
|
-
# Extends default field behavior and adds extension to the field if it should be decorated.
|
|
4
|
-
module FieldIntegration
|
|
5
|
-
# Overridden field initializer
|
|
6
|
-
# @param type [GraphQL::Schema::Object] The type to add the extension to.
|
|
7
|
-
# @return [Void]
|
|
8
|
-
def initialize(type:, **rest, &block)
|
|
9
|
-
super
|
|
10
|
-
field_type = [type].flatten(1).first
|
|
11
|
-
extension_options = get_extension_options(field_type)
|
|
12
|
-
extend_with_decorator(extension_options) if extension_options
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
private
|
|
16
|
-
|
|
17
|
-
def get_extension_options(type)
|
|
18
|
-
type_attributes = GraphQL::Decorate::TypeAttributes.new(type)
|
|
19
|
-
return unless type_attributes.decorator_class
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
decorator_class: type_attributes.decorator_class,
|
|
23
|
-
decorator_evaluator: type_attributes.decorator_evaluator,
|
|
24
|
-
decorator_context_evaluator: type_attributes.decorator_context_evaluator,
|
|
25
|
-
unresolved_type: type_attributes.unresolved_type
|
|
26
|
-
}
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def extend_with_decorator(options)
|
|
30
|
-
extension(GraphQL::Decorate::FieldExtension, options)
|
|
31
|
-
# ext = GraphQL::Decorate::FieldExtension.new(field: self, options: options)
|
|
32
|
-
# @extensions = @extensions.dup
|
|
33
|
-
# @extensions.unshift(ext)
|
|
34
|
-
# @extensions.freeze
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
module GraphQL
|
|
3
|
-
module Decorate
|
|
4
|
-
# Handles decorating an object given its current field context.
|
|
5
|
-
class Object
|
|
6
|
-
# @param object [Object] Object being decorated.
|
|
7
|
-
# @param field_context [GraphQL::Decorate::FieldContext] Current GraphQL field context and options.
|
|
8
|
-
def initialize(object, field_context)
|
|
9
|
-
@object = object
|
|
10
|
-
@field_context = field_context
|
|
11
|
-
@default_decorator_context = { graphql: true }
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# Resolve the object with decoration.
|
|
15
|
-
# @return [Object] Decorated object if possible, otherwise the original object.
|
|
16
|
-
def decorate
|
|
17
|
-
if decorator_class
|
|
18
|
-
GraphQL::Decorate.configuration.evaluate_decorator.call(decorator_class, object, decorator_context)
|
|
19
|
-
else
|
|
20
|
-
object
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
private
|
|
25
|
-
|
|
26
|
-
attr_reader :object, :field_context, :default_decorator_context
|
|
27
|
-
|
|
28
|
-
def decorator_class
|
|
29
|
-
if field_context.options[:decorator_class]
|
|
30
|
-
field_context.options[:decorator_class]
|
|
31
|
-
elsif field_context.options[:decorator_evaluator]
|
|
32
|
-
field_context.options[:decorator_evaluator].call(object)
|
|
33
|
-
else
|
|
34
|
-
resolve_decorator_class
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def decorator_context_evaluator
|
|
39
|
-
field_context.options[:decorator_context_evaluator] || resolve_decorator_context_evaluator
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def evaluate_decoration_context
|
|
45
|
-
decorator_context_evaluator ? decorator_context_evaluator.call(object) : {}
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def decorator_context
|
|
49
|
-
evaluate_decoration_context.merge(default_decorator_context)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def resolve_decorator_class
|
|
53
|
-
type = resolve_type
|
|
54
|
-
if type.respond_to?(:decorator_class) && type.decorator_class
|
|
55
|
-
type.decorator_class
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def resolve_decorator_context_evaluator
|
|
60
|
-
type = resolve_type
|
|
61
|
-
if type.respond_to?(:decorator_context_evaluator) && type.decorator_context_evaluator
|
|
62
|
-
type.decorator_context_evaluator
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def resolve_type
|
|
67
|
-
field_context.options[:unresolved_type]&.resolve_type(object, field_context.context)
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|