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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphiti/api_test_generator.rb +71 -0
  3. data/lib/generators/graphiti/generator_mixin.rb +45 -0
  4. data/lib/generators/graphiti/resource_generator.rb +99 -0
  5. data/lib/generators/graphiti/resource_test_generator.rb +57 -0
  6. data/lib/generators/{jsonapi → graphiti}/templates/application_resource.rb.erb +0 -0
  7. data/lib/generators/{jsonapi → graphiti}/templates/controller.rb.erb +0 -0
  8. data/lib/generators/{jsonapi → graphiti}/templates/create_request_spec.rb.erb +3 -3
  9. data/lib/generators/{jsonapi → graphiti}/templates/destroy_request_spec.rb.erb +5 -5
  10. data/lib/generators/graphiti/templates/index_request_spec.rb.erb +22 -0
  11. data/lib/generators/{jsonapi → graphiti}/templates/resource.rb.erb +0 -0
  12. data/lib/generators/{jsonapi → graphiti}/templates/resource_reads_spec.rb.erb +12 -12
  13. data/lib/generators/{jsonapi → graphiti}/templates/resource_writes_spec.rb.erb +12 -12
  14. data/lib/generators/graphiti/templates/show_request_spec.rb.erb +21 -0
  15. data/lib/generators/{jsonapi → graphiti}/templates/update_request_spec.rb.erb +5 -5
  16. data/lib/graphiti.rb +0 -6
  17. data/lib/graphiti/adapters/abstract.rb +0 -1
  18. data/lib/graphiti/adapters/active_record/inferrence.rb +8 -1
  19. data/lib/graphiti/base.rb +1 -1
  20. data/lib/graphiti/errors.rb +70 -5
  21. data/lib/graphiti/jsonapi_serializable_ext.rb +18 -6
  22. data/lib/graphiti/query.rb +28 -17
  23. data/lib/graphiti/resource.rb +14 -0
  24. data/lib/graphiti/resource/configuration.rb +22 -3
  25. data/lib/graphiti/resource/dsl.rb +17 -2
  26. data/lib/graphiti/resource/interface.rb +10 -8
  27. data/lib/graphiti/resource/links.rb +6 -3
  28. data/lib/graphiti/runner.rb +2 -1
  29. data/lib/graphiti/schema.rb +33 -5
  30. data/lib/graphiti/schema_diff.rb +71 -1
  31. data/lib/graphiti/scope.rb +4 -9
  32. data/lib/graphiti/scoping/base.rb +2 -2
  33. data/lib/graphiti/scoping/filter.rb +5 -0
  34. data/lib/graphiti/scoping/filterable.rb +21 -6
  35. data/lib/graphiti/scoping/paginate.rb +4 -4
  36. data/lib/graphiti/scoping/sort.rb +26 -5
  37. data/lib/graphiti/sideload.rb +13 -22
  38. data/lib/graphiti/sideload/belongs_to.rb +11 -2
  39. data/lib/graphiti/sideload/has_many.rb +1 -1
  40. data/lib/graphiti/sideload/polymorphic_belongs_to.rb +2 -3
  41. data/lib/graphiti/types.rb +1 -1
  42. data/lib/graphiti/util/class.rb +5 -2
  43. data/lib/graphiti/util/persistence.rb +1 -1
  44. data/lib/graphiti/version.rb +1 -1
  45. metadata +16 -13
  46. data/lib/generators/jsonapi/resource_generator.rb +0 -169
  47. data/lib/generators/jsonapi/templates/index_request_spec.rb.erb +0 -22
  48. data/lib/generators/jsonapi/templates/show_request_spec.rb.erb +0 -21
@@ -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 check!
71
- return if scope_proc
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.to_hash.tap do |hash|
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
- { primary_key => ids_for_parents(parents).join(',') }
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)
@@ -4,7 +4,7 @@ class Graphiti::Sideload::HasMany < Graphiti::Sideload
4
4
  end
5
5
 
6
6
  def load_params(parents, query)
7
- query.to_hash.tap do |hash|
7
+ query.hash.tap do |hash|
8
8
  hash[:filter] ||= {}
9
9
  hash[:filter].merge!(base_filter(parents))
10
10
  end
@@ -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 = ->(name, sl) { sl.group_name == group_name.to_sym }
86
- if child = children.find(&match)
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
@@ -40,7 +40,7 @@ module Graphiti
40
40
 
41
41
  Bool = create(nil) do |input|
42
42
  input = Dry::Types['params.bool'][input]
43
- Dry::Types['strict.bool'][input] if input
43
+ Dry::Types['strict.bool'][input] unless input.nil?
44
44
  end
45
45
 
46
46
  PresentBool = create(nil) do |input|
@@ -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
- klass = "#{namespace}::#{inferred_name}".safe_constantize
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
- persisted.instance_variable_set(:@__serializer_klass, @resource.serializer)
44
+ @resource.decorate_record(persisted)
45
45
  assign_temp_id(persisted, @meta[:temp_id])
46
46
 
47
47
  associate_parents(persisted, parents)
@@ -1,3 +1,3 @@
1
1
  module Graphiti
2
- VERSION = "1.0.alpha.5"
2
+ VERSION = "1.0.alpha.6"
3
3
  end
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.5
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-08 00:00:00.000000000 Z
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/jsonapi/resource_generator.rb
222
- - lib/generators/jsonapi/templates/application_resource.rb.erb
223
- - lib/generators/jsonapi/templates/controller.rb.erb
224
- - lib/generators/jsonapi/templates/create_request_spec.rb.erb
225
- - lib/generators/jsonapi/templates/destroy_request_spec.rb.erb
226
- - lib/generators/jsonapi/templates/index_request_spec.rb.erb
227
- - lib/generators/jsonapi/templates/resource.rb.erb
228
- - lib/generators/jsonapi/templates/resource_reads_spec.rb.erb
229
- - lib/generators/jsonapi/templates/resource_writes_spec.rb.erb
230
- - lib/generators/jsonapi/templates/show_request_spec.rb.erb
231
- - lib/generators/jsonapi/templates/update_request_spec.rb.erb
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