graphiti 1.0.alpha.5 → 1.0.alpha.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphiti/api_test_generator.rb +71 -0
- data/lib/generators/graphiti/generator_mixin.rb +45 -0
- data/lib/generators/graphiti/resource_generator.rb +99 -0
- data/lib/generators/graphiti/resource_test_generator.rb +57 -0
- data/lib/generators/{jsonapi → graphiti}/templates/application_resource.rb.erb +0 -0
- data/lib/generators/{jsonapi → graphiti}/templates/controller.rb.erb +0 -0
- data/lib/generators/{jsonapi → graphiti}/templates/create_request_spec.rb.erb +3 -3
- data/lib/generators/{jsonapi → graphiti}/templates/destroy_request_spec.rb.erb +5 -5
- data/lib/generators/graphiti/templates/index_request_spec.rb.erb +22 -0
- data/lib/generators/{jsonapi → graphiti}/templates/resource.rb.erb +0 -0
- data/lib/generators/{jsonapi → graphiti}/templates/resource_reads_spec.rb.erb +12 -12
- data/lib/generators/{jsonapi → graphiti}/templates/resource_writes_spec.rb.erb +12 -12
- data/lib/generators/graphiti/templates/show_request_spec.rb.erb +21 -0
- data/lib/generators/{jsonapi → graphiti}/templates/update_request_spec.rb.erb +5 -5
- data/lib/graphiti.rb +0 -6
- data/lib/graphiti/adapters/abstract.rb +0 -1
- data/lib/graphiti/adapters/active_record/inferrence.rb +8 -1
- data/lib/graphiti/base.rb +1 -1
- data/lib/graphiti/errors.rb +70 -5
- data/lib/graphiti/jsonapi_serializable_ext.rb +18 -6
- data/lib/graphiti/query.rb +28 -17
- data/lib/graphiti/resource.rb +14 -0
- data/lib/graphiti/resource/configuration.rb +22 -3
- data/lib/graphiti/resource/dsl.rb +17 -2
- data/lib/graphiti/resource/interface.rb +10 -8
- data/lib/graphiti/resource/links.rb +6 -3
- data/lib/graphiti/runner.rb +2 -1
- data/lib/graphiti/schema.rb +33 -5
- data/lib/graphiti/schema_diff.rb +71 -1
- data/lib/graphiti/scope.rb +4 -9
- data/lib/graphiti/scoping/base.rb +2 -2
- data/lib/graphiti/scoping/filter.rb +5 -0
- data/lib/graphiti/scoping/filterable.rb +21 -6
- data/lib/graphiti/scoping/paginate.rb +4 -4
- data/lib/graphiti/scoping/sort.rb +26 -5
- data/lib/graphiti/sideload.rb +13 -22
- data/lib/graphiti/sideload/belongs_to.rb +11 -2
- data/lib/graphiti/sideload/has_many.rb +1 -1
- data/lib/graphiti/sideload/polymorphic_belongs_to.rb +2 -3
- data/lib/graphiti/types.rb +1 -1
- data/lib/graphiti/util/class.rb +5 -2
- data/lib/graphiti/util/persistence.rb +1 -1
- data/lib/graphiti/version.rb +1 -1
- metadata +16 -13
- data/lib/generators/jsonapi/resource_generator.rb +0 -169
- data/lib/generators/jsonapi/templates/index_request_spec.rb.erb +0 -22
- data/lib/generators/jsonapi/templates/show_request_spec.rb.erb +0 -21
data/lib/graphiti/sideload.rb
CHANGED
@@ -31,6 +31,7 @@ module Graphiti
|
|
31
31
|
@writable = opts[:writable]
|
32
32
|
@as = opts[:as]
|
33
33
|
@link = opts[:link]
|
34
|
+
@single = opts[:single]
|
34
35
|
|
35
36
|
# polymorphic-specific
|
36
37
|
@group_name = opts[:group_name]
|
@@ -39,8 +40,6 @@ module Graphiti
|
|
39
40
|
if polymorphic_child?
|
40
41
|
parent.resource.polymorphic << resource_class
|
41
42
|
end
|
42
|
-
|
43
|
-
check! if defined?(::Rails)
|
44
43
|
end
|
45
44
|
|
46
45
|
def self.scope(&blk)
|
@@ -67,25 +66,8 @@ module Graphiti
|
|
67
66
|
self.link_proc = blk
|
68
67
|
end
|
69
68
|
|
70
|
-
def
|
71
|
-
|
72
|
-
case type
|
73
|
-
when :has_many, :has_one
|
74
|
-
unless resource.filters[foreign_key]
|
75
|
-
raise Errors::MissingSideloadFilter.new parent_resource_class,
|
76
|
-
self, foreign_key
|
77
|
-
end
|
78
|
-
when :belongs_to
|
79
|
-
unless resource.filters[primary_key]
|
80
|
-
raise Errors::MissingSideloadFilter.new parent_resource_class,
|
81
|
-
self, primary_key
|
82
|
-
end
|
83
|
-
when :many_to_many
|
84
|
-
unless resource.filters[true_foreign_key]
|
85
|
-
raise Errors::MissingSideloadFilter.new parent_resource_class,
|
86
|
-
self, true_foreign_key
|
87
|
-
end
|
88
|
-
end
|
69
|
+
def errors
|
70
|
+
@errors ||= []
|
89
71
|
end
|
90
72
|
|
91
73
|
def readable?
|
@@ -96,6 +78,10 @@ module Graphiti
|
|
96
78
|
!!@writable
|
97
79
|
end
|
98
80
|
|
81
|
+
def single?
|
82
|
+
!!@single
|
83
|
+
end
|
84
|
+
|
99
85
|
def link?
|
100
86
|
return true if link_proc
|
101
87
|
|
@@ -159,7 +145,7 @@ module Graphiti
|
|
159
145
|
params_proc.call(params, parents) if params_proc
|
160
146
|
opts = load_options(parents, query)
|
161
147
|
proxy = resource.class._all(params, opts, base_scope)
|
162
|
-
pre_load_proc.call(proxy) if pre_load_proc
|
148
|
+
pre_load_proc.call(proxy, parents) if pre_load_proc
|
163
149
|
proxy.to_a
|
164
150
|
end
|
165
151
|
|
@@ -197,6 +183,10 @@ module Graphiti
|
|
197
183
|
end
|
198
184
|
|
199
185
|
def resolve(parents, query)
|
186
|
+
if single? && parents.length > 1
|
187
|
+
raise Errors::SingularSideload.new(self, parents.length)
|
188
|
+
end
|
189
|
+
|
200
190
|
if self.class.scope_proc
|
201
191
|
sideload_scope = fire_scope(parents)
|
202
192
|
sideload_scope = Scope.new sideload_scope,
|
@@ -264,6 +254,7 @@ module Graphiti
|
|
264
254
|
{}.tap do |opts|
|
265
255
|
opts[:default_paginate] = false
|
266
256
|
opts[:sideload_parent_length] = parents.length
|
257
|
+
opts[:query] = query
|
267
258
|
opts[:after_resolve] = ->(results) {
|
268
259
|
fire_assign(parents, results)
|
269
260
|
}
|
@@ -4,14 +4,23 @@ class Graphiti::Sideload::BelongsTo < Graphiti::Sideload
|
|
4
4
|
end
|
5
5
|
|
6
6
|
def load_params(parents, query)
|
7
|
-
query.
|
7
|
+
query.hash.tap do |hash|
|
8
8
|
hash[:filter] ||= {}
|
9
9
|
hash[:filter].merge!(base_filter(parents))
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
def load(parents, query)
|
14
|
+
if ids_for_parents(parents).empty?
|
15
|
+
[]
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
13
21
|
def base_filter(parents)
|
14
|
-
|
22
|
+
parent_ids = ids_for_parents(parents)
|
23
|
+
{ primary_key => parent_ids.join(',') }
|
15
24
|
end
|
16
25
|
|
17
26
|
def assign_each(parent, children)
|
@@ -82,9 +82,8 @@ class Graphiti::Sideload::PolymorphicBelongsTo < Graphiti::Sideload::BelongsTo
|
|
82
82
|
parents.group_by(&grouper.field_name).each_pair do |group_name, group|
|
83
83
|
next if group_name.nil?
|
84
84
|
|
85
|
-
match = ->(
|
86
|
-
if
|
87
|
-
sideload = child[1]
|
85
|
+
match = ->(c) { c.group_name == group_name.to_sym }
|
86
|
+
if sideload = children.values.find(&match)
|
88
87
|
query = remove_invalid_sideloads(sideload.resource, query)
|
89
88
|
sideload.resolve(group, query)
|
90
89
|
else
|
data/lib/graphiti/types.rb
CHANGED
data/lib/graphiti/util/class.rb
CHANGED
@@ -4,16 +4,19 @@ module Graphiti
|
|
4
4
|
def self.infer_resource_class(parent_resource_class, sideload_name)
|
5
5
|
namespace = namespace_for(parent_resource_class)
|
6
6
|
inferred_name = "#{sideload_name.to_s.singularize.classify}Resource"
|
7
|
-
|
7
|
+
klass_name = "#{namespace}::#{inferred_name}"
|
8
|
+
tried = [klass_name, inferred_name]
|
9
|
+
klass = klass_name.safe_constantize
|
8
10
|
klass ||= inferred_name.safe_constantize
|
9
11
|
unless klass
|
10
|
-
raise Errors::ResourceNotFound.new(parent_resource_class, sideload_name)
|
12
|
+
raise Errors::ResourceNotFound.new(parent_resource_class, sideload_name, tried)
|
11
13
|
end
|
12
14
|
klass
|
13
15
|
end
|
14
16
|
|
15
17
|
def self.namespace_for(klass)
|
16
18
|
namespace = klass.name
|
19
|
+
return '' unless namespace
|
17
20
|
split = namespace.split('::')
|
18
21
|
split[0,split.length-1].join('::')
|
19
22
|
end
|
@@ -41,7 +41,7 @@ class Graphiti::Util::Persistence
|
|
41
41
|
update_foreign_key_for_parents(parents)
|
42
42
|
|
43
43
|
persisted = persist_object(@meta[:method], @attributes)
|
44
|
-
|
44
|
+
@resource.decorate_record(persisted)
|
45
45
|
assign_temp_id(persisted, @meta[:temp_id])
|
46
46
|
|
47
47
|
associate_parents(persisted, parents)
|
data/lib/graphiti/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphiti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.alpha.
|
4
|
+
version: 1.0.alpha.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lee Richmond
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08-
|
11
|
+
date: 2018-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jsonapi-serializable
|
@@ -218,17 +218,20 @@ files:
|
|
218
218
|
- gemfiles/rails_4.gemfile
|
219
219
|
- gemfiles/rails_5.gemfile
|
220
220
|
- graphiti.gemspec
|
221
|
-
- lib/generators/
|
222
|
-
- lib/generators/
|
223
|
-
- lib/generators/
|
224
|
-
- lib/generators/
|
225
|
-
- lib/generators/
|
226
|
-
- lib/generators/
|
227
|
-
- lib/generators/
|
228
|
-
- lib/generators/
|
229
|
-
- lib/generators/
|
230
|
-
- lib/generators/
|
231
|
-
- lib/generators/
|
221
|
+
- lib/generators/graphiti/api_test_generator.rb
|
222
|
+
- lib/generators/graphiti/generator_mixin.rb
|
223
|
+
- lib/generators/graphiti/resource_generator.rb
|
224
|
+
- lib/generators/graphiti/resource_test_generator.rb
|
225
|
+
- lib/generators/graphiti/templates/application_resource.rb.erb
|
226
|
+
- lib/generators/graphiti/templates/controller.rb.erb
|
227
|
+
- lib/generators/graphiti/templates/create_request_spec.rb.erb
|
228
|
+
- lib/generators/graphiti/templates/destroy_request_spec.rb.erb
|
229
|
+
- lib/generators/graphiti/templates/index_request_spec.rb.erb
|
230
|
+
- lib/generators/graphiti/templates/resource.rb.erb
|
231
|
+
- lib/generators/graphiti/templates/resource_reads_spec.rb.erb
|
232
|
+
- lib/generators/graphiti/templates/resource_writes_spec.rb.erb
|
233
|
+
- lib/generators/graphiti/templates/show_request_spec.rb.erb
|
234
|
+
- lib/generators/graphiti/templates/update_request_spec.rb.erb
|
232
235
|
- lib/graphiti.rb
|
233
236
|
- lib/graphiti/adapters/abstract.rb
|
234
237
|
- lib/graphiti/adapters/active_record.rb
|
@@ -1,169 +0,0 @@
|
|
1
|
-
module Jsonapi
|
2
|
-
class ResourceGenerator < ::Rails::Generators::NamedBase
|
3
|
-
source_root File.expand_path('../templates', __FILE__)
|
4
|
-
|
5
|
-
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
|
6
|
-
|
7
|
-
class_option :'omit-comments',
|
8
|
-
type: :boolean,
|
9
|
-
default: false,
|
10
|
-
aliases: ['--omit-comments', '-c'],
|
11
|
-
desc: 'Generate without documentation comments'
|
12
|
-
class_option :'actions',
|
13
|
-
type: :array,
|
14
|
-
default: nil,
|
15
|
-
aliases: ['--actions', '-a'],
|
16
|
-
desc: 'Array of controller actions to support, e.g. "index show destroy"'
|
17
|
-
|
18
|
-
desc "This generator creates a resource file at app/resources, as well as corresponding controller/specs/route/etc"
|
19
|
-
def copy_resource_file
|
20
|
-
unless model_klass
|
21
|
-
raise "You must define a #{class_name} model before generating the corresponding resource."
|
22
|
-
end
|
23
|
-
|
24
|
-
generate_controller
|
25
|
-
generate_application_resource unless application_resource_defined?
|
26
|
-
generate_route
|
27
|
-
generate_resource_specs
|
28
|
-
generate_api_specs
|
29
|
-
generate_resource
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def actions
|
35
|
-
@options['actions'] || %w(index show create update destroy)
|
36
|
-
end
|
37
|
-
|
38
|
-
def actions?(*methods)
|
39
|
-
methods.any? { |m| actions.include?(m) }
|
40
|
-
end
|
41
|
-
|
42
|
-
def omit_comments?
|
43
|
-
@options['omit-comments']
|
44
|
-
end
|
45
|
-
|
46
|
-
def responders?
|
47
|
-
defined?(Responders)
|
48
|
-
end
|
49
|
-
|
50
|
-
def generate_controller
|
51
|
-
to = File.join('app/controllers', class_path, "#{file_name.pluralize}_controller.rb")
|
52
|
-
template('controller.rb.erb', to)
|
53
|
-
end
|
54
|
-
|
55
|
-
def generate_application_resource
|
56
|
-
to = File.join('app/resources', class_path, "application_resource.rb")
|
57
|
-
template('application_resource.rb.erb', to)
|
58
|
-
end
|
59
|
-
|
60
|
-
def application_resource_defined?
|
61
|
-
'ApplicationResource'.safe_constantize.present?
|
62
|
-
end
|
63
|
-
|
64
|
-
def generate_route
|
65
|
-
code = " resources :#{type}"
|
66
|
-
code << ", only: [#{actions.map { |a| ":#{a}" }.join(', ')}]" if actions.length < 5
|
67
|
-
code << "\n"
|
68
|
-
inject_into_file 'config/routes.rb', after: "scope path: '/v1' do\n" do
|
69
|
-
code
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def generate_resource_specs
|
74
|
-
to = File.join("spec/resources/#{file_name}", class_path, "reads_spec.rb")
|
75
|
-
template('resource_reads_spec.rb.erb', to)
|
76
|
-
|
77
|
-
to = File.join("spec/resources/#{file_name}", class_path, "writes_spec.rb")
|
78
|
-
template('resource_writes_spec.rb.erb', to)
|
79
|
-
end
|
80
|
-
|
81
|
-
def generate_api_specs
|
82
|
-
if actions?('index')
|
83
|
-
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
84
|
-
class_path,
|
85
|
-
"index_spec.rb"
|
86
|
-
template('index_request_spec.rb.erb', to)
|
87
|
-
end
|
88
|
-
|
89
|
-
if actions?('show')
|
90
|
-
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
91
|
-
class_path,
|
92
|
-
"show_spec.rb"
|
93
|
-
template('show_request_spec.rb.erb', to)
|
94
|
-
end
|
95
|
-
|
96
|
-
if actions?('create')
|
97
|
-
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
98
|
-
class_path,
|
99
|
-
"create_spec.rb"
|
100
|
-
template('create_request_spec.rb.erb', to)
|
101
|
-
end
|
102
|
-
|
103
|
-
if actions?('update')
|
104
|
-
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
105
|
-
class_path,
|
106
|
-
"update_spec.rb"
|
107
|
-
template('update_request_spec.rb.erb', to)
|
108
|
-
end
|
109
|
-
|
110
|
-
if actions?('destroy')
|
111
|
-
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
112
|
-
class_path,
|
113
|
-
"destroy_spec.rb"
|
114
|
-
template('destroy_request_spec.rb.erb', to)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def generate_resource
|
119
|
-
to = File.join('app/resources', class_path, "#{file_name}_resource.rb")
|
120
|
-
template('resource.rb.erb', to)
|
121
|
-
end
|
122
|
-
|
123
|
-
def jsonapi_config
|
124
|
-
File.exists?('.jsonapicfg.yml') ? YAML.load_file('.jsonapicfg.yml') : {}
|
125
|
-
end
|
126
|
-
|
127
|
-
def update_config!(attrs)
|
128
|
-
config = jsonapi_config.merge(attrs)
|
129
|
-
File.open('.jsonapicfg.yml', 'w') { |f| f.write(config.to_yaml) }
|
130
|
-
end
|
131
|
-
|
132
|
-
def prompt(header: nil, description: nil, default: nil)
|
133
|
-
say(set_color("\n#{header}", :magenta, :bold)) if header
|
134
|
-
say("\n#{description}") if description
|
135
|
-
answer = ask(set_color("\n(default: #{default}):", :magenta, :bold))
|
136
|
-
answer = default if answer.blank? && default != 'nil'
|
137
|
-
say(set_color("\nGot it!\n", :white, :bold))
|
138
|
-
answer
|
139
|
-
end
|
140
|
-
|
141
|
-
def api_namespace
|
142
|
-
@api_namespace ||= begin
|
143
|
-
ns = jsonapi_config['namespace']
|
144
|
-
|
145
|
-
if ns.blank?
|
146
|
-
ns = prompt \
|
147
|
-
header: "What is your API namespace?",
|
148
|
-
description: "This will be used as a route prefix, e.g. if you want the route '/books_api/v1/authors' your namespace would be 'books_api'",
|
149
|
-
default: 'api'
|
150
|
-
update_config!('namespace' => ns)
|
151
|
-
end
|
152
|
-
|
153
|
-
ns
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def model_klass
|
158
|
-
class_name.safe_constantize
|
159
|
-
end
|
160
|
-
|
161
|
-
def resource_klass
|
162
|
-
"#{model_klass}Resource"
|
163
|
-
end
|
164
|
-
|
165
|
-
def type
|
166
|
-
model_klass.name.underscore.pluralize
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'rails_helper'
|
2
|
-
|
3
|
-
RSpec.describe "<%= file_name.pluralize %>#index", type: :request do
|
4
|
-
let(:params) { {} }
|
5
|
-
|
6
|
-
subject(:make_request) do
|
7
|
-
jsonapi_get "/<%= api_namespace %>/v1/<%= file_name.pluralize %>", params: params
|
8
|
-
end
|
9
|
-
|
10
|
-
describe 'basic fetch' do
|
11
|
-
let!(:<%= file_name %>1) { create(:<%= file_name %>) }
|
12
|
-
let!(:<%= file_name %>2) { create(:<%= file_name %>) }
|
13
|
-
|
14
|
-
it 'works' do
|
15
|
-
expect(<%= resource_klass %>).to receive(:all).and_call_original
|
16
|
-
make_request
|
17
|
-
expect(response.status).to eq(200)
|
18
|
-
expect(d.map(&:jsonapi_type).uniq).to eq(['<%= file_name.pluralize %>'])
|
19
|
-
expect(d.map(&:id)).to eq([<%= file_name %>1.id, <%= file_name %>2.id])
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'rails_helper'
|
2
|
-
|
3
|
-
RSpec.describe "<%= file_name.pluralize %>#show", type: :request do
|
4
|
-
let(:params) { {} }
|
5
|
-
|
6
|
-
subject(:make_request) do
|
7
|
-
jsonapi_get "/<%= api_namespace %>/v1/<%= file_name.pluralize %>/#{<%= file_name %>.id}", params: params
|
8
|
-
end
|
9
|
-
|
10
|
-
describe 'basic fetch' do
|
11
|
-
let!(:<%= file_name %>) { create(:<%= file_name %>) }
|
12
|
-
|
13
|
-
it 'works' do
|
14
|
-
expect(<%= resource_klass %>).to receive(:find).and_call_original
|
15
|
-
make_request
|
16
|
-
expect(response.status).to eq(200)
|
17
|
-
expect(d.jsonapi_type).to eq('<%= file_name.pluralize %>')
|
18
|
-
expect(d.id).to eq(<%= file_name %>.id)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|