graphiti 1.0.alpha.5 → 1.0.alpha.6

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.
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