jsonapi_compliable 0.6.13 → 0.7.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/.yardopts +1 -0
- data/docs/Jsonapi/ResourceGenerator.html +325 -0
- data/docs/Jsonapi.html +115 -0
- data/docs/JsonapiCompliable/Adapters/Abstract.html +1 -1
- data/docs/JsonapiCompliable/Adapters/ActiveRecord.html +59 -53
- data/docs/JsonapiCompliable/Adapters/ActiveRecordSideloading.html +6 -6
- data/docs/JsonapiCompliable/Adapters/Null.html +1 -1
- data/docs/JsonapiCompliable/Adapters.html +1 -1
- data/docs/JsonapiCompliable/Base.html +113 -117
- data/docs/JsonapiCompliable/Deserializer.html +87 -22
- data/docs/JsonapiCompliable/Errors/BadFilter.html +1 -1
- data/docs/JsonapiCompliable/Errors/StatNotFound.html +1 -1
- data/docs/JsonapiCompliable/Errors/UnsupportedPageSize.html +1 -1
- data/docs/JsonapiCompliable/Errors/ValidationError.html +1 -1
- data/docs/JsonapiCompliable/Errors.html +1 -1
- data/docs/JsonapiCompliable/Extensions/BooleanAttribute/ClassMethods.html +1 -1
- data/docs/JsonapiCompliable/Extensions/BooleanAttribute.html +1 -1
- data/docs/JsonapiCompliable/Extensions/ExtraAttribute/ClassMethods.html +1 -1
- data/docs/JsonapiCompliable/Extensions/ExtraAttribute.html +1 -1
- data/docs/JsonapiCompliable/Extensions.html +1 -1
- data/docs/JsonapiCompliable/Query.html +19 -17
- data/docs/JsonapiCompliable/Rails.html +1 -1
- data/docs/JsonapiCompliable/Resource.html +311 -220
- data/docs/JsonapiCompliable/Scope.html +1 -1
- data/docs/JsonapiCompliable/Scoping/Base.html +1 -1
- data/docs/JsonapiCompliable/Scoping/DefaultFilter.html +1 -1
- data/docs/JsonapiCompliable/Scoping/ExtraFields.html +1 -1
- data/docs/JsonapiCompliable/Scoping/Filter.html +1 -1
- data/docs/JsonapiCompliable/Scoping/Filterable.html +2 -2
- data/docs/JsonapiCompliable/Scoping/Paginate.html +1 -1
- data/docs/JsonapiCompliable/Scoping/Sort.html +1 -1
- data/docs/JsonapiCompliable/Scoping.html +1 -1
- data/docs/JsonapiCompliable/SerializableTempId.html +1 -1
- data/docs/JsonapiCompliable/Sideload.html +229 -78
- data/docs/JsonapiCompliable/Stats/DSL.html +1 -1
- data/docs/JsonapiCompliable/Stats/Payload.html +1 -1
- data/docs/JsonapiCompliable/Stats.html +1 -1
- data/docs/JsonapiCompliable/Util/FieldParams.html +1 -1
- data/docs/JsonapiCompliable/Util/Hash.html +1 -1
- data/docs/JsonapiCompliable/Util/IncludeParams.html +1 -1
- data/docs/JsonapiCompliable/Util/Persistence.html +1 -1
- data/docs/JsonapiCompliable/Util/RelationshipPayload.html +1 -1
- data/docs/JsonapiCompliable/Util/RenderOptions.html +1 -1
- data/docs/JsonapiCompliable/Util/ValidationResponse.html +1 -1
- data/docs/JsonapiCompliable/Util.html +1 -1
- data/docs/JsonapiCompliable.html +210 -3
- data/docs/_index.html +13 -1
- data/docs/class_list.html +1 -1
- data/docs/file.README.html +4 -2
- data/docs/index.html +4 -2
- data/docs/method_list.html +307 -243
- data/docs/top-level-namespace.html +2 -2
- data/lib/generators/jsonapi/resource_generator.rb +139 -58
- data/lib/generators/jsonapi/templates/application_resource.rb.erb +11 -2
- data/lib/generators/jsonapi/templates/controller.rb.erb +34 -0
- data/lib/generators/jsonapi/templates/destroy_request_spec.rb.erb +1 -1
- data/lib/generators/jsonapi/templates/payload.rb.erb +31 -0
- data/lib/generators/jsonapi/templates/resource.rb.erb +47 -0
- data/lib/generators/jsonapi/templates/serializer.rb.erb +17 -0
- data/lib/jsonapi_compliable/adapters/active_record_sideloading.rb +5 -5
- data/lib/jsonapi_compliable/resource.rb +11 -5
- data/lib/jsonapi_compliable/sideload.rb +40 -11
- data/lib/jsonapi_compliable/util/persistence.rb +9 -2
- data/lib/jsonapi_compliable/util/relationship_payload.rb +6 -0
- data/lib/jsonapi_compliable/util/validation_response.rb +2 -1
- data/lib/jsonapi_compliable/version.rb +1 -1
- metadata +4 -2
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
<p class="children">
|
|
83
83
|
|
|
84
84
|
|
|
85
|
-
<strong class="modules">Modules:</strong> <span class='object_link'><a href="JsonapiCompliable.html" title="JsonapiCompliable (module)">JsonapiCompliable</a></span>
|
|
85
|
+
<strong class="modules">Modules:</strong> <span class='object_link'><a href="Jsonapi.html" title="Jsonapi (module)">Jsonapi</a></span>, <span class='object_link'><a href="JsonapiCompliable.html" title="JsonapiCompliable (module)">JsonapiCompliable</a></span>
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
</div>
|
|
101
101
|
|
|
102
102
|
<div id="footer">
|
|
103
|
-
Generated on
|
|
103
|
+
Generated on Wed Jun 7 10:15:09 2017 by
|
|
104
104
|
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
105
105
|
0.9.9 (ruby-2.3.0).
|
|
106
106
|
</div>
|
|
@@ -2,85 +2,166 @@ module Jsonapi
|
|
|
2
2
|
class ResourceGenerator < ::Rails::Generators::NamedBase
|
|
3
3
|
source_root File.expand_path('../templates', __FILE__)
|
|
4
4
|
|
|
5
|
-
class_option :'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
class_option :'omit-comments',
|
|
6
|
+
type: :boolean,
|
|
7
|
+
default: false,
|
|
8
|
+
aliases: ['--omit-comments', '-c'],
|
|
9
|
+
desc: 'Generate without documentation comments'
|
|
10
|
+
class_option :'omit-controller',
|
|
11
|
+
type: :boolean,
|
|
12
|
+
default: false,
|
|
13
|
+
aliases: ['--omit-controller'],
|
|
14
|
+
desc: 'Generate without controller'
|
|
15
|
+
class_option :'omit-serializer',
|
|
16
|
+
type: :boolean,
|
|
17
|
+
default: false,
|
|
18
|
+
aliases: ['--omit-serializer', '-s'],
|
|
19
|
+
desc: 'Generate without serializer'
|
|
20
|
+
class_option :'omit-payload',
|
|
21
|
+
type: :boolean,
|
|
22
|
+
default: false,
|
|
23
|
+
aliases: ['--omit-payload', '-p'],
|
|
24
|
+
desc: 'Generate without spec payload'
|
|
25
|
+
class_option :'omit-strong-resource',
|
|
26
|
+
type: :boolean,
|
|
27
|
+
default: false,
|
|
28
|
+
aliases: ['--omit-strong-resource', '-r'],
|
|
29
|
+
desc: 'Generate without strong resource'
|
|
30
|
+
class_option :'omit-route',
|
|
31
|
+
type: :boolean,
|
|
32
|
+
default: false,
|
|
33
|
+
aliases: ['--omit-route'],
|
|
34
|
+
desc: 'Generate without specs'
|
|
35
|
+
class_option :'omit-tests',
|
|
36
|
+
type: :boolean,
|
|
37
|
+
default: false,
|
|
38
|
+
aliases: ['--omit-tests', '-t'],
|
|
39
|
+
desc: 'Generate without specs'
|
|
40
|
+
|
|
41
|
+
desc "This generator creates a resource file at app/resources, as well as corresponding controller/specs/route/etc"
|
|
12
42
|
def copy_resource_file
|
|
13
|
-
unless
|
|
14
|
-
|
|
15
|
-
template('controller.rb.erb', to)
|
|
43
|
+
unless model_klass
|
|
44
|
+
raise "You must define a #{class_name} model before generating the corresponding resource."
|
|
16
45
|
end
|
|
17
46
|
|
|
18
|
-
unless
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
47
|
+
generate_controller unless omit_controller?
|
|
48
|
+
generate_serializer unless omit_serializer?
|
|
49
|
+
generate_application_resource unless application_resource_defined?
|
|
50
|
+
generate_spec_payload unless omit_spec_payload?
|
|
51
|
+
generate_strong_resource unless omit_strong_resource?
|
|
52
|
+
generate_route unless omit_route?
|
|
53
|
+
generate_tests unless omit_tests?
|
|
54
|
+
generate_resource
|
|
55
|
+
end
|
|
22
56
|
|
|
23
|
-
|
|
24
|
-
to = File.join('app/resources', class_path, "application_resource.rb")
|
|
25
|
-
template('application_resource.rb.erb', to)
|
|
26
|
-
end
|
|
57
|
+
private
|
|
27
58
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
59
|
+
def omit_comments?
|
|
60
|
+
@options['omit-comments']
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def generate_controller
|
|
64
|
+
to = File.join('app/controllers', class_path, "#{file_name.pluralize}_controller.rb")
|
|
65
|
+
template('controller.rb.erb', to)
|
|
66
|
+
end
|
|
32
67
|
|
|
33
|
-
|
|
34
|
-
|
|
68
|
+
def omit_controller?
|
|
69
|
+
@options['omit-controller']
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def generate_serializer
|
|
73
|
+
to = File.join('app/serializers', class_path, "serializable_#{file_name}.rb")
|
|
74
|
+
template('serializer.rb.erb', to)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def omit_serializer?
|
|
78
|
+
@options['omit-serializer']
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def generate_application_resource
|
|
82
|
+
to = File.join('app/resources', class_path, "application_resource.rb")
|
|
83
|
+
template('application_resource.rb.erb', to)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def application_resource_defined?
|
|
87
|
+
'ApplicationResource'.safe_constantize.present?
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def generate_spec_payload
|
|
91
|
+
to = File.join('spec/payloads', class_path, "#{file_name}.rb")
|
|
92
|
+
template('payload.rb.erb', to)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def omit_spec_payload?
|
|
96
|
+
@options['no-payload']
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def generate_strong_resource
|
|
100
|
+
code = <<-STR
|
|
35
101
|
strong_resource :#{file_name} do
|
|
36
102
|
# Your attributes go here, e.g.
|
|
37
103
|
# attribute :name, :string
|
|
38
104
|
end
|
|
39
105
|
|
|
40
|
-
|
|
41
|
-
|
|
106
|
+
STR
|
|
107
|
+
inject_into_file 'config/initializers/strong_resources.rb', after: "StrongResources.configure do\n" do
|
|
108
|
+
code
|
|
42
109
|
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def omit_strong_resource?
|
|
113
|
+
@options['no-strong-resources']
|
|
114
|
+
end
|
|
43
115
|
|
|
44
|
-
|
|
45
|
-
|
|
116
|
+
def generate_route
|
|
117
|
+
code = <<-STR
|
|
46
118
|
resources :#{type}
|
|
47
|
-
|
|
48
|
-
|
|
119
|
+
STR
|
|
120
|
+
inject_into_file 'config/routes.rb', after: "scope path: '/api' do\n scope path: '/v1' do\n" do
|
|
121
|
+
code
|
|
49
122
|
end
|
|
123
|
+
end
|
|
50
124
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
125
|
+
def omit_route?
|
|
126
|
+
@options['no-route']
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def generate_tests
|
|
130
|
+
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
|
131
|
+
class_path,
|
|
132
|
+
"index_spec.rb"
|
|
133
|
+
template('index_request_spec.rb.erb', to)
|
|
134
|
+
|
|
135
|
+
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
|
136
|
+
class_path,
|
|
137
|
+
"show_spec.rb"
|
|
138
|
+
template('show_request_spec.rb.erb', to)
|
|
139
|
+
|
|
140
|
+
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
|
141
|
+
class_path,
|
|
142
|
+
"create_spec.rb"
|
|
143
|
+
template('create_request_spec.rb.erb', to)
|
|
144
|
+
|
|
145
|
+
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
|
146
|
+
class_path,
|
|
147
|
+
"update_spec.rb"
|
|
148
|
+
template('update_request_spec.rb.erb', to)
|
|
149
|
+
|
|
150
|
+
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
|
151
|
+
class_path,
|
|
152
|
+
"destroy_spec.rb"
|
|
153
|
+
template('destroy_request_spec.rb.erb', to)
|
|
154
|
+
end
|
|
77
155
|
|
|
156
|
+
def omit_tests?
|
|
157
|
+
@options['no-test']
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def generate_resource
|
|
78
161
|
to = File.join('app/resources', class_path, "#{file_name}_resource.rb")
|
|
79
162
|
template('resource.rb.erb', to)
|
|
80
163
|
end
|
|
81
164
|
|
|
82
|
-
private
|
|
83
|
-
|
|
84
165
|
def model_klass
|
|
85
166
|
class_name.safe_constantize
|
|
86
167
|
end
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
<%- unless omit_comments? -%>
|
|
2
|
+
# ApplicationResource is similar to ApplicationRecord - a base class that
|
|
3
|
+
# holds configuration/methods for subclasses.
|
|
4
|
+
# All Resources should inherit from ApplicationResource.
|
|
5
|
+
# Resource documentation: https://jsonapi-suite.github.io/jsonapi_compliable/JsonapiCompliable/Resource.html
|
|
6
|
+
<%- end -%>
|
|
3
7
|
class ApplicationResource < JsonapiCompliable::Resource
|
|
8
|
+
<%- unless omit_comments? -%>
|
|
9
|
+
# Use the ActiveRecord Adapter for all subclasses.
|
|
10
|
+
# Subclasses can still override this default.
|
|
11
|
+
# More on adapters: https://jsonapi-suite.github.io/jsonapi_compliable/JsonapiCompliable/Adapters/Abstract.html
|
|
12
|
+
<%- end -%>
|
|
4
13
|
use_adapter JsonapiCompliable::Adapters::ActiveRecord
|
|
5
14
|
end
|
|
@@ -1,21 +1,46 @@
|
|
|
1
1
|
<% module_namespacing do -%>
|
|
2
2
|
class <%= model_klass.name.pluralize %>Controller < ApplicationController
|
|
3
|
+
<%- unless omit_comments? -%>
|
|
4
|
+
# Mark this as a JSONAPI controller, associating with the given resource
|
|
5
|
+
<%- end -%>
|
|
3
6
|
jsonapi resource: <%= model_klass %>Resource
|
|
4
7
|
|
|
8
|
+
<%- unless omit_comments? -%>
|
|
9
|
+
# Reference a strong resource payload defined in
|
|
10
|
+
# config/initializers/strong_resources.rb
|
|
11
|
+
<%- end -%>
|
|
5
12
|
strong_resource :<%= file_name %>
|
|
6
13
|
|
|
14
|
+
<%- unless omit_comments? -%>
|
|
15
|
+
# Run strong parameter validation for these actions.
|
|
16
|
+
# Invalid keys will be dropped.
|
|
17
|
+
# Invalid value types will log or raise based on the configuration
|
|
18
|
+
# ActionController::Parameters.action_on_invalid_parameters
|
|
19
|
+
<%- end -%>
|
|
7
20
|
before_action :apply_strong_params, only: [:create, :update]
|
|
8
21
|
|
|
22
|
+
<%- unless omit_comments? -%>
|
|
23
|
+
# Start with a base scope and pass to render_jsonapi
|
|
24
|
+
<%- end -%>
|
|
9
25
|
def index
|
|
10
26
|
<%= file_name.pluralize %> = <%= model_klass %>.all
|
|
11
27
|
render_jsonapi(<%= file_name.pluralize %>)
|
|
12
28
|
end
|
|
13
29
|
|
|
30
|
+
<%- unless omit_comments? -%>
|
|
31
|
+
# Call jsonapi_scope directly here so we can get behavior like
|
|
32
|
+
# sparse fieldsets and statistics.
|
|
33
|
+
<%- end -%>
|
|
14
34
|
def show
|
|
15
35
|
scope = jsonapi_scope(<%= model_klass %>.where(id: params[:id]))
|
|
16
36
|
render_jsonapi(scope.resolve.first, scope: false)
|
|
17
37
|
end
|
|
18
38
|
|
|
39
|
+
<%- unless omit_comments? -%>
|
|
40
|
+
# jsonapi_create will use the configured Resource (and adapter) to persist.
|
|
41
|
+
# This will handle nested relationships as well.
|
|
42
|
+
# On validation errors, render correct error JSON.
|
|
43
|
+
<%- end -%>
|
|
19
44
|
def create
|
|
20
45
|
<%= file_name %>, success = jsonapi_create.to_a
|
|
21
46
|
|
|
@@ -26,6 +51,11 @@ class <%= model_klass.name.pluralize %>Controller < ApplicationController
|
|
|
26
51
|
end
|
|
27
52
|
end
|
|
28
53
|
|
|
54
|
+
<%- unless omit_comments? -%>
|
|
55
|
+
# jsonapi_update will use the configured Resource (and adapter) to persist.
|
|
56
|
+
# This will handle nested relationships as well.
|
|
57
|
+
# On validation errors, render correct error JSON.
|
|
58
|
+
<%- end -%>
|
|
29
59
|
def update
|
|
30
60
|
<%= file_name %>, success = jsonapi_update.to_a
|
|
31
61
|
|
|
@@ -36,6 +66,10 @@ class <%= model_klass.name.pluralize %>Controller < ApplicationController
|
|
|
36
66
|
end
|
|
37
67
|
end
|
|
38
68
|
|
|
69
|
+
<%- unless omit_comments? -%>
|
|
70
|
+
# No need for any special logic here as no_content is jsonapi_compliant.
|
|
71
|
+
# Customize this if you have a more complex use case.
|
|
72
|
+
<%- end -%>
|
|
39
73
|
def destroy
|
|
40
74
|
<%= file_name %> = <%= model_klass %>.find(params[:id])
|
|
41
75
|
<%= file_name %>.destroy
|
|
@@ -2,7 +2,7 @@ require 'rails_helper'
|
|
|
2
2
|
|
|
3
3
|
RSpec.describe "<%= type %>#destroy", type: :request do
|
|
4
4
|
context 'basic destroy' do
|
|
5
|
-
let!(:<%= file_name %>) { create(:<%= file_name %>) }
|
|
5
|
+
let!(:<%= file_name %>) { FactoryGirl.create(:<%= file_name %>) }
|
|
6
6
|
|
|
7
7
|
it 'updates the resource' do
|
|
8
8
|
expect {
|
|
@@ -1,2 +1,33 @@
|
|
|
1
|
+
<%- unless omit_comments? -%>
|
|
2
|
+
# Register a payload to validate against.
|
|
3
|
+
# Add expected attributes within this block, e.g.:
|
|
4
|
+
#
|
|
5
|
+
# key(:name)
|
|
6
|
+
#
|
|
7
|
+
# Optionally validate the type as well:
|
|
8
|
+
#
|
|
9
|
+
# key(:name, String)
|
|
10
|
+
#
|
|
11
|
+
# This will:
|
|
12
|
+
#
|
|
13
|
+
# * Compare record.name == json['name']
|
|
14
|
+
# * Ensure no extra keys are in the json payload
|
|
15
|
+
# * Ensure no values are nil (unless allow_nil: true is passed)
|
|
16
|
+
# * Ensures json['name'] is a string
|
|
17
|
+
#
|
|
18
|
+
# If you have custom serialization logic and want to compare against
|
|
19
|
+
# something other than "record.name", pass a block:
|
|
20
|
+
#
|
|
21
|
+
# key(:name) { |record| record.name.upcase }
|
|
22
|
+
#
|
|
23
|
+
# Or, if this is a one-off for a particular spec, do that customization at
|
|
24
|
+
# runtime:
|
|
25
|
+
#
|
|
26
|
+
# assert_payload(:person, person_record, json_item) do
|
|
27
|
+
# key(:name) { 'Homer Simpson' }
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# For more information, see https://jsonapi-suite.github.io/jsonapi_spec_helpers/
|
|
31
|
+
<%- end -%>
|
|
1
32
|
JsonapiSpecHelpers::Payload.register(:<%= file_name %>) do
|
|
2
33
|
end
|
|
@@ -1,6 +1,53 @@
|
|
|
1
|
+
<%- unless omit_comments? -%>
|
|
2
|
+
# Define how to query and persist a given model.
|
|
3
|
+
# Further Resource documentation: https://jsonapi-suite.github.io/jsonapi_compliable/JsonapiCompliable/Resource.html
|
|
4
|
+
<%- end -%>
|
|
1
5
|
<% module_namespacing do -%>
|
|
2
6
|
class <%= class_name %>Resource < ApplicationResource
|
|
7
|
+
<%- unless omit_comments? -%>
|
|
8
|
+
# Used for associating this resource with a given input.
|
|
9
|
+
# This should match the 'type' in the corresponding serializer.
|
|
10
|
+
<%- end -%>
|
|
3
11
|
type :<%= type %>
|
|
12
|
+
<%- unless omit_comments? -%>
|
|
13
|
+
# Associate to a Model object so we know how to persist.
|
|
14
|
+
<%- end -%>
|
|
4
15
|
model <%= model_klass %>
|
|
16
|
+
<%- unless omit_comments? -%>
|
|
17
|
+
# Customize your resource here. Some common examples:
|
|
18
|
+
#
|
|
19
|
+
# === Allow ?filter[name] query parameter ===
|
|
20
|
+
# allow_filter :name
|
|
21
|
+
#
|
|
22
|
+
# === Enable total count, when requested ===
|
|
23
|
+
# allow_stat total: [:count]
|
|
24
|
+
#
|
|
25
|
+
# === Allow sideloading/sideposting of relationships ===
|
|
26
|
+
# belongs_to :foo,
|
|
27
|
+
# foreign_key: :foo_id,
|
|
28
|
+
# resource: FooResource,
|
|
29
|
+
# scope: -> { Foo.all }
|
|
30
|
+
#
|
|
31
|
+
# === Custom sorting logic ===
|
|
32
|
+
# sort do |scope, att, dir|
|
|
33
|
+
# ... code ...
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# === Change default sort ===
|
|
37
|
+
# default_sort([{ title: :asc }])
|
|
38
|
+
#
|
|
39
|
+
# === Custom pagination logic ===
|
|
40
|
+
# paginate do |scope, current_page, per_page|
|
|
41
|
+
# ... code ...
|
|
42
|
+
# end
|
|
43
|
+
#
|
|
44
|
+
# === Change default page size ===
|
|
45
|
+
# default_page_size(10)
|
|
46
|
+
#
|
|
47
|
+
# === Change how we resolve the scope ===
|
|
48
|
+
# def resolve(scope)
|
|
49
|
+
# ... code ...
|
|
50
|
+
# end
|
|
51
|
+
<%- end -%>
|
|
5
52
|
end
|
|
6
53
|
<% end -%>
|
|
@@ -1,5 +1,22 @@
|
|
|
1
|
+
<%- unless omit_comments? -%>
|
|
2
|
+
# Serializers define the rendered JSON for a model instance.
|
|
3
|
+
# We use jsonapi-rb, which is similar to active_model_serializers.
|
|
4
|
+
<%- end -%>
|
|
1
5
|
<% module_namespacing do -%>
|
|
2
6
|
class Serializable<%= class_name %> < JSONAPI::Serializable::Resource
|
|
3
7
|
type :<%= type %>
|
|
8
|
+
|
|
9
|
+
<%- unless omit_comments? -%>
|
|
10
|
+
# Add attributes here to ensure they get rendered, .e.g.
|
|
11
|
+
#
|
|
12
|
+
# attribute :name
|
|
13
|
+
#
|
|
14
|
+
# To customize, pass a block and reference the underlying @object
|
|
15
|
+
# being serialized:
|
|
16
|
+
#
|
|
17
|
+
# attribute :name do
|
|
18
|
+
# @object.name.upcase
|
|
19
|
+
# end
|
|
20
|
+
<%- end -%>
|
|
4
21
|
end
|
|
5
22
|
<% end -%>
|
|
@@ -47,7 +47,7 @@ module JsonapiCompliable
|
|
|
47
47
|
def has_one(association_name, scope: nil, resource:, foreign_key:, primary_key: :id, &blk)
|
|
48
48
|
_scope = scope
|
|
49
49
|
|
|
50
|
-
allow_sideload association_name, resource: resource do
|
|
50
|
+
allow_sideload association_name, type: :has_one, foreign_key: foreign_key, primary_key: primary_key, resource: resource do
|
|
51
51
|
scope do |parents|
|
|
52
52
|
parent_ids = parents.map { |p| p.send(primary_key) }
|
|
53
53
|
_scope.call.where(foreign_key => parent_ids.uniq.compact)
|
|
@@ -71,7 +71,7 @@ module JsonapiCompliable
|
|
|
71
71
|
fk = foreign_key.values.first
|
|
72
72
|
_scope = scope
|
|
73
73
|
|
|
74
|
-
allow_sideload association_name, resource: resource do
|
|
74
|
+
allow_sideload association_name, type: :habtm, foreign_key: foreign_key, primary_key: primary_key, resource: resource do
|
|
75
75
|
scope do |parents|
|
|
76
76
|
parent_ids = parents.map { |p| p.send(primary_key) }
|
|
77
77
|
parent_ids.uniq!
|
|
@@ -94,14 +94,14 @@ module JsonapiCompliable
|
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
def polymorphic_belongs_to(association_name, group_by:, groups:, &blk)
|
|
97
|
-
allow_sideload association_name, polymorphic: true do
|
|
98
|
-
group_by(
|
|
97
|
+
allow_sideload association_name, type: :polymorphic_belongs_to, polymorphic: true do
|
|
98
|
+
group_by(group_by)
|
|
99
99
|
|
|
100
100
|
groups.each_pair do |type, config|
|
|
101
101
|
primary_key = config[:primary_key] || :id
|
|
102
102
|
foreign_key = config[:foreign_key]
|
|
103
103
|
|
|
104
|
-
allow_sideload type, resource: config[:resource] do
|
|
104
|
+
allow_sideload type, parent: self, primary_key: primary_key, foreign_key: foreign_key, type: :belongs_to, resource: config[:resource] do
|
|
105
105
|
scope do |parents|
|
|
106
106
|
parent_ids = parents.map { |p| p.send(foreign_key) }
|
|
107
107
|
parent_ids.compact!
|
|
@@ -87,8 +87,6 @@ module JsonapiCompliable
|
|
|
87
87
|
# scope: -> { Comment.all },
|
|
88
88
|
# resource: CommentResource,
|
|
89
89
|
# foreign_key: :post_id
|
|
90
|
-
#
|
|
91
|
-
# @attr_reader [Hash] context A hash of +object+ and +namespace+ - Example object is a Rails controller, example namespace would be +:index+ or +:show+
|
|
92
90
|
class Resource
|
|
93
91
|
extend Forwardable
|
|
94
92
|
attr_reader :context
|
|
@@ -451,16 +449,24 @@ module JsonapiCompliable
|
|
|
451
449
|
end
|
|
452
450
|
end
|
|
453
451
|
|
|
454
|
-
# The current context set by +#with_context
|
|
452
|
+
# The current context **object** set by +#with_context+. If you are
|
|
453
|
+
# using Rails, this is a controller instance.
|
|
455
454
|
#
|
|
456
|
-
#
|
|
455
|
+
# This method is equivalent to +JsonapiCompliable.context[:object]+
|
|
457
456
|
#
|
|
458
457
|
# @see #with_context
|
|
459
|
-
# @return
|
|
458
|
+
# @return the context object
|
|
460
459
|
def context
|
|
461
460
|
JsonapiCompliable.context[:object]
|
|
462
461
|
end
|
|
463
462
|
|
|
463
|
+
# The current context **namespace** set by +#with_context+. If you
|
|
464
|
+
# are using Rails, this is the controller method name (e.g. +:index+)
|
|
465
|
+
#
|
|
466
|
+
# This method is equivalent to +JsonapiCompliable.context[:namespace]+
|
|
467
|
+
#
|
|
468
|
+
# @see #with_context
|
|
469
|
+
# @return [Symbol] the context namespace
|
|
464
470
|
def context_namespace
|
|
465
471
|
JsonapiCompliable.context[:namespace]
|
|
466
472
|
end
|
|
@@ -6,7 +6,7 @@ module JsonapiCompliable
|
|
|
6
6
|
# @attr_reader [Hash] sideloads The associated sibling sideloads
|
|
7
7
|
# @attr_reader [Proc] scope_proc The configured 'scope' block
|
|
8
8
|
# @attr_reader [Proc] assign_proc The configured 'assign' block
|
|
9
|
-
# @attr_reader [
|
|
9
|
+
# @attr_reader [Symbol] grouping_field The configured 'group_by' symbol
|
|
10
10
|
# @attr_reader [Symbol] foreign_key The attribute used to match objects - need not be a true database foreign key.
|
|
11
11
|
# @attr_reader [Symbol] primary_key The attribute used to match objects - need not be a true database primary key.
|
|
12
12
|
# @attr_reader [Symbol] type One of :has_many, :belongs_to, etc
|
|
@@ -15,10 +15,11 @@ module JsonapiCompliable
|
|
|
15
15
|
:resource_class,
|
|
16
16
|
:polymorphic,
|
|
17
17
|
:polymorphic_groups,
|
|
18
|
+
:parent,
|
|
18
19
|
:sideloads,
|
|
19
20
|
:scope_proc,
|
|
20
21
|
:assign_proc,
|
|
21
|
-
:
|
|
22
|
+
:grouping_field,
|
|
22
23
|
:foreign_key,
|
|
23
24
|
:primary_key,
|
|
24
25
|
:type
|
|
@@ -28,12 +29,13 @@ module JsonapiCompliable
|
|
|
28
29
|
# An anonymous Resource will be assigned when none provided.
|
|
29
30
|
#
|
|
30
31
|
# @see Adapters::Abstract#sideloading_module
|
|
31
|
-
def initialize(name, type: nil, resource: nil, polymorphic: false, primary_key: :id, foreign_key: nil)
|
|
32
|
+
def initialize(name, type: nil, resource: nil, polymorphic: false, primary_key: :id, foreign_key: nil, parent: nil)
|
|
32
33
|
@name = name
|
|
33
34
|
@resource_class = (resource || Class.new(Resource))
|
|
34
35
|
@sideloads = {}
|
|
35
36
|
@polymorphic = !!polymorphic
|
|
36
37
|
@polymorphic_groups = {} if polymorphic?
|
|
38
|
+
@parent = parent
|
|
37
39
|
@primary_key = primary_key
|
|
38
40
|
@foreign_key = foreign_key
|
|
39
41
|
@type = type
|
|
@@ -55,7 +57,7 @@ module JsonapiCompliable
|
|
|
55
57
|
# +Business+ or +Government+:
|
|
56
58
|
#
|
|
57
59
|
# allow_sideload :organization, :polymorphic: true do
|
|
58
|
-
# group_by
|
|
60
|
+
# group_by :organization_type
|
|
59
61
|
#
|
|
60
62
|
# allow_sideload 'Business', resource: BusinessResource do
|
|
61
63
|
# # ... code ...
|
|
@@ -70,7 +72,7 @@ module JsonapiCompliable
|
|
|
70
72
|
# with ActiveRecord:
|
|
71
73
|
#
|
|
72
74
|
# polymorphic_belongs_to :organization,
|
|
73
|
-
# group_by:
|
|
75
|
+
# group_by: :organization_type,
|
|
74
76
|
# groups: {
|
|
75
77
|
# 'Business' => {
|
|
76
78
|
# scope: -> { Business.all },
|
|
@@ -181,21 +183,25 @@ module JsonapiCompliable
|
|
|
181
183
|
# @see #name
|
|
182
184
|
# @see #type
|
|
183
185
|
def associate(parent, child)
|
|
184
|
-
|
|
186
|
+
association_name = @parent ? @parent.name : name
|
|
187
|
+
resource_class.config[:adapter].associate parent,
|
|
188
|
+
child,
|
|
189
|
+
association_name,
|
|
190
|
+
type
|
|
185
191
|
end
|
|
186
192
|
|
|
187
|
-
# Define
|
|
193
|
+
# Define an attribute that groups the parent records. For instance, with
|
|
188
194
|
# an ActiveRecord polymorphic belongs_to there will be a +parent_id+
|
|
189
195
|
# and +parent_type+. We would want to group on +parent_type+:
|
|
190
196
|
#
|
|
191
197
|
# allow_sideload :organization, polymorphic: true do
|
|
192
198
|
# # group parent_type, parent here is 'organization'
|
|
193
|
-
# group_by
|
|
199
|
+
# group_by :organization_type
|
|
194
200
|
# end
|
|
195
201
|
#
|
|
196
202
|
# @see #polymorphic?
|
|
197
|
-
def group_by(
|
|
198
|
-
@
|
|
203
|
+
def group_by(grouping_field)
|
|
204
|
+
@grouping_field = grouping_field
|
|
199
205
|
end
|
|
200
206
|
|
|
201
207
|
# Resolve the sideload.
|
|
@@ -323,6 +329,13 @@ module JsonapiCompliable
|
|
|
323
329
|
result
|
|
324
330
|
end
|
|
325
331
|
|
|
332
|
+
# @api private
|
|
333
|
+
def polymorphic_child_for_type(type)
|
|
334
|
+
polymorphic_groups.values.find do |v|
|
|
335
|
+
v.resource_class.config[:type] == type.to_sym
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
326
339
|
private
|
|
327
340
|
|
|
328
341
|
def nested_sideload_hash(sideload, processed)
|
|
@@ -333,8 +346,24 @@ module JsonapiCompliable
|
|
|
333
346
|
end
|
|
334
347
|
end
|
|
335
348
|
|
|
349
|
+
def polymorphic_grouper(grouping_field)
|
|
350
|
+
lambda do |record|
|
|
351
|
+
if record.is_a?(Hash)
|
|
352
|
+
if record.keys[0].is_a?(Symbol)
|
|
353
|
+
record[grouping_field]
|
|
354
|
+
else
|
|
355
|
+
record[grouping_field.to_s]
|
|
356
|
+
end
|
|
357
|
+
else
|
|
358
|
+
record.send(grouping_field)
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
336
363
|
def resolve_polymorphic(parents, query)
|
|
337
|
-
|
|
364
|
+
grouper = polymorphic_grouper(@grouping_field)
|
|
365
|
+
|
|
366
|
+
parents.group_by(&grouper).each_pair do |group_type, group_members|
|
|
338
367
|
sideload_for_group = @polymorphic_groups[group_type]
|
|
339
368
|
if sideload_for_group
|
|
340
369
|
sideload_for_group.resolve(group_members, query, name)
|