rest_framework 0.0.15 → 0.0.16
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/README.md +2 -15
- data/app/views/layouts/rest_framework.html.erb +2 -2
- data/lib/rest_framework.rb +3 -0
- data/lib/rest_framework/VERSION_STAMP +1 -1
- data/lib/rest_framework/controller_mixins/base.rb +74 -7
- data/lib/rest_framework/controller_mixins/models.rb +70 -78
- data/lib/rest_framework/filters.rb +65 -0
- data/lib/rest_framework/paginators.rb +84 -0
- data/lib/rest_framework/serializers.rb +39 -22
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 825e7e7ac0e9c8ae57250288a54dc3ef6510665ce3d4fee23245c559aa8e8233
|
4
|
+
data.tar.gz: 2211c303d14708ef94a4cb35d6e3b25bf78b3b08645f2990ca5448354d8dd64e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ee2cf406f2b8d77d9ea63650b080d037a759143e0a3e19846585d493a386a4647db0fe59188aa0fd887257a18087aaca82b80d99dee962af1dfdb7eca66a07c
|
7
|
+
data.tar.gz: 6248caea5ad7f5a37a55f7da6aae55c70dc284b9f04d046fc6149a13dff4ae34410106c97f54274f59a8353c79fe97073b7dcba51d65d9c94eb0f9cff1a205ac
|
data/README.md
CHANGED
@@ -116,18 +116,5 @@ To run the full test suite:
|
|
116
116
|
$ rake test
|
117
117
|
```
|
118
118
|
|
119
|
-
To
|
120
|
-
|
121
|
-
```shell
|
122
|
-
$ rake test:unit
|
123
|
-
```
|
124
|
-
|
125
|
-
To run integration tests:
|
126
|
-
|
127
|
-
```shell
|
128
|
-
$ rake test:integration
|
129
|
-
```
|
130
|
-
|
131
|
-
To interact with the integration app, you can `cd test/integration` and operate it via the normal
|
132
|
-
Rails interfaces. Ensure you run `rake db:schema:load` before running `rails server` or
|
133
|
-
`rails console`.
|
119
|
+
To interact with the test app, `cd test` and operate it via the normal Rails interfaces. Ensure you
|
120
|
+
run `rake db:schema:load` before running `rails server` or `rails console`.
|
@@ -30,14 +30,14 @@
|
|
30
30
|
<% if @json_payload %>
|
31
31
|
<li class="nav-item">
|
32
32
|
<a class="nav-link active" href="#tab-json" data-toggle="tab" role="tab">
|
33
|
-
|
33
|
+
.json
|
34
34
|
</a>
|
35
35
|
</li>
|
36
36
|
<% end %>
|
37
37
|
<% if @xml_payload %>
|
38
38
|
<li class="nav-item">
|
39
39
|
<a class="nav-link" href="#tab-xml" data-toggle="tab" role="tab">
|
40
|
-
|
40
|
+
.xml
|
41
41
|
</a>
|
42
42
|
</li>
|
43
43
|
<% end %>
|
data/lib/rest_framework.rb
CHANGED
@@ -3,5 +3,8 @@ end
|
|
3
3
|
|
4
4
|
require_relative "rest_framework/controller_mixins"
|
5
5
|
require_relative "rest_framework/engine"
|
6
|
+
require_relative "rest_framework/filters"
|
7
|
+
require_relative "rest_framework/paginators"
|
6
8
|
require_relative "rest_framework/routers"
|
9
|
+
require_relative "rest_framework/serializers"
|
7
10
|
require_relative "rest_framework/version"
|
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.16
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative '../serializers'
|
2
|
+
|
1
3
|
module RESTFramework
|
2
4
|
|
3
5
|
# This module provides the common functionality for any controller mixins, a `root` action, and
|
@@ -28,12 +30,37 @@ module RESTFramework
|
|
28
30
|
def self.included(base)
|
29
31
|
if base.is_a? Class
|
30
32
|
base.extend ClassMethods
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
:
|
35
|
-
:
|
36
|
-
|
33
|
+
|
34
|
+
# Add class attributes (with defaults) unless they already exist.
|
35
|
+
{
|
36
|
+
extra_actions: nil,
|
37
|
+
extra_member_actions: nil,
|
38
|
+
filter_backends: nil,
|
39
|
+
native_serializer_config: nil,
|
40
|
+
native_serializer_action_config: nil,
|
41
|
+
paginator_class: nil,
|
42
|
+
page_size: nil,
|
43
|
+
page_query_param: 'page',
|
44
|
+
page_size_query_param: 'page_size',
|
45
|
+
max_page_size: nil,
|
46
|
+
serializer_class: nil,
|
47
|
+
singleton_controller: nil,
|
48
|
+
skip_actions: nil,
|
49
|
+
}.each do |a, default|
|
50
|
+
unless base.respond_to?(a)
|
51
|
+
base.class_attribute(a)
|
52
|
+
|
53
|
+
# Set default manually so we can still support Rails 4. Maybe later we can use the
|
54
|
+
# default parameter on `class_attribute`.
|
55
|
+
base.send(:"#{a}=", default)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Alias `extra_actions` to `extra_collection_actions`.
|
60
|
+
unless base.respond_to?(:extra_collection_actions)
|
61
|
+
base.alias_method(:extra_collection_actions, :extra_actions)
|
62
|
+
base.alias_method(:extra_collection_actions=, :extra_actions=)
|
63
|
+
end
|
37
64
|
|
38
65
|
# skip csrf since this is an API
|
39
66
|
base.skip_before_action(:verify_authenticity_token) rescue nil
|
@@ -48,6 +75,45 @@ module RESTFramework
|
|
48
75
|
|
49
76
|
protected
|
50
77
|
|
78
|
+
# Helper to get filtering backends with a sane default.
|
79
|
+
def get_filter_backends
|
80
|
+
if self.class.filter_backends
|
81
|
+
return self.class.filter_backends
|
82
|
+
end
|
83
|
+
|
84
|
+
# By default, return nil.
|
85
|
+
return nil
|
86
|
+
end
|
87
|
+
|
88
|
+
# Filter the recordset over all configured filter backends.
|
89
|
+
def get_filtered_data(data)
|
90
|
+
(self.get_filter_backends || []).each do |filter_class|
|
91
|
+
filter = filter_class.new(controller: self)
|
92
|
+
data = filter.get_filtered_data(data)
|
93
|
+
end
|
94
|
+
|
95
|
+
return data
|
96
|
+
end
|
97
|
+
|
98
|
+
# Helper to get the configured serializer class, or `NativeModelSerializer` as a default.
|
99
|
+
def get_serializer_class
|
100
|
+
return self.class.serializer_class || NativeModelSerializer
|
101
|
+
end
|
102
|
+
|
103
|
+
# Get a native serializer config for the current action.
|
104
|
+
def get_native_serializer_config
|
105
|
+
action_serializer_config = self.class.native_serializer_action_config || {}
|
106
|
+
action = self.action_name.to_sym
|
107
|
+
|
108
|
+
# Handle case where :index action is not defined.
|
109
|
+
if action == :index && !action_serializer_config.key?(:index)
|
110
|
+
# Default is :show if `singleton_controller`, otherwise :list.
|
111
|
+
action = self.class.singleton_controller ? :show : :list
|
112
|
+
end
|
113
|
+
|
114
|
+
return (action_serializer_config[action] if action) || self.class.native_serializer_config
|
115
|
+
end
|
116
|
+
|
51
117
|
def record_invalid(e)
|
52
118
|
return api_response(
|
53
119
|
{message: "Record invalid.", exception: e, errors: e.record.errors}, status: 400
|
@@ -66,6 +132,7 @@ module RESTFramework
|
|
66
132
|
return api_response({message: "Record not destroyed.", exception: e}, status: 406)
|
67
133
|
end
|
68
134
|
|
135
|
+
# Helper for showing routes under a controller action, used for the browsable API.
|
69
136
|
def _get_routes
|
70
137
|
begin
|
71
138
|
formatter = ActionDispatch::Routing::ConsoleFormatter::Sheet
|
@@ -124,8 +191,8 @@ module RESTFramework
|
|
124
191
|
rescue ActionView::MissingTemplate # fallback to rest_framework layout/view
|
125
192
|
kwargs[:layout] = "rest_framework"
|
126
193
|
kwargs[:template] = "rest_framework/default"
|
194
|
+
render(**kwargs)
|
127
195
|
end
|
128
|
-
render(**kwargs)
|
129
196
|
}
|
130
197
|
end
|
131
198
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require_relative 'base'
|
2
|
-
require_relative '../
|
2
|
+
require_relative '../filters'
|
3
3
|
|
4
4
|
module RESTFramework
|
5
5
|
|
@@ -8,61 +8,71 @@ module RESTFramework
|
|
8
8
|
def self.included(base)
|
9
9
|
if base.is_a? Class
|
10
10
|
BaseControllerMixin.included(base)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
:
|
16
|
-
:
|
17
|
-
|
18
|
-
|
19
|
-
:
|
20
|
-
:
|
21
|
-
|
22
|
-
|
23
|
-
:
|
24
|
-
|
25
|
-
|
11
|
+
|
12
|
+
# Add class attributes (with defaults) unless they already exist.
|
13
|
+
{
|
14
|
+
# Core attributes related to models.
|
15
|
+
model: nil,
|
16
|
+
recordset: nil,
|
17
|
+
|
18
|
+
# Attributes for create/update parameters.
|
19
|
+
allowed_parameters: nil,
|
20
|
+
allowed_action_parameters: nil,
|
21
|
+
|
22
|
+
# Attributes for configuring record fields.
|
23
|
+
fields: nil,
|
24
|
+
action_fields: nil,
|
25
|
+
|
26
|
+
# Attributes for default model filtering (and ordering).
|
27
|
+
filterset_fields: nil,
|
28
|
+
ordering_fields: nil,
|
29
|
+
ordering_query_param: 'ordering',
|
30
|
+
|
31
|
+
# Other misc attributes.
|
32
|
+
disable_creation_from_recordset: nil, # Option to disable `recordset.create` behavior.
|
33
|
+
}.each do |a, default|
|
34
|
+
unless base.respond_to?(a)
|
35
|
+
base.class_attribute(a)
|
36
|
+
|
37
|
+
# Set default manually so we can still support Rails 4. Maybe later we can use the
|
38
|
+
# default parameter on `class_attribute`.
|
39
|
+
base.send(:"#{a}=", default)
|
40
|
+
end
|
41
|
+
end
|
26
42
|
end
|
27
43
|
end
|
28
44
|
|
29
45
|
protected
|
30
46
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# Get a list of fields for the current action.
|
36
|
-
def get_fields
|
37
|
-
action_fields = self.class.action_fields || {}
|
47
|
+
# Get a list of parameters allowed for the current action.
|
48
|
+
def get_allowed_parameters
|
49
|
+
allowed_action_parameters = self.class.allowed_action_parameters || {}
|
38
50
|
action = self.action_name.to_sym
|
39
51
|
|
40
|
-
# index action should use :list
|
41
|
-
action = :list if action == :index && !
|
52
|
+
# index action should use :list allowed parameters if :index is not provided
|
53
|
+
action = :list if action == :index && !allowed_action_parameters.key?(:index)
|
42
54
|
|
43
|
-
return (
|
55
|
+
return (allowed_action_parameters[action] if action) || self.class.allowed_parameters
|
44
56
|
end
|
45
57
|
|
46
|
-
# Get
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
# index action should use :list serializer config if :index is not provided
|
52
|
-
action = :list if action == :index && !action_serializer_config.key?(:index)
|
58
|
+
# Get the list of filtering backends to use.
|
59
|
+
def get_filter_backends
|
60
|
+
backends = super
|
61
|
+
return backends if backends
|
53
62
|
|
54
|
-
return
|
63
|
+
# By default, return the standard model filter backend.
|
64
|
+
return [RESTFramework::ModelFilter, RESTFramework::ModelOrderingFilter]
|
55
65
|
end
|
56
66
|
|
57
|
-
# Get a list of
|
58
|
-
def
|
59
|
-
|
67
|
+
# Get a list of fields for the current action.
|
68
|
+
def get_fields
|
69
|
+
action_fields = self.class.action_fields || {}
|
60
70
|
action = self.action_name.to_sym
|
61
71
|
|
62
|
-
# index action should use :list
|
63
|
-
action = :list if action == :index && !
|
72
|
+
# index action should use :list fields if :index is not provided
|
73
|
+
action = :list if action == :index && !action_fields.key?(:index)
|
64
74
|
|
65
|
-
return (
|
75
|
+
return (action_fields[action] if action) || self.class.fields || []
|
66
76
|
end
|
67
77
|
|
68
78
|
# Filter the request body for keys in current action's allowed_parameters/fields config.
|
@@ -75,29 +85,10 @@ module RESTFramework
|
|
75
85
|
alias :get_create_params :_get_parameter_values_from_request_body
|
76
86
|
alias :get_update_params :_get_parameter_values_from_request_body
|
77
87
|
|
78
|
-
# Filter params for keys allowed by the current action's filterset_fields/fields config.
|
79
|
-
def _get_filterset_values_from_params
|
80
|
-
fields = self.filterset_fields || self.get_fields
|
81
|
-
return @_get_filterset_values_from_params ||= request.query_parameters.select { |p|
|
82
|
-
fields.include?(p.to_sym) || fields.include?(p.to_s)
|
83
|
-
}
|
84
|
-
end
|
85
|
-
alias :get_lookup_params :_get_filterset_values_from_params
|
86
|
-
alias :get_filter_params :_get_filterset_values_from_params
|
87
|
-
|
88
|
-
# Get the recordset, filtered by the filter params.
|
89
|
-
def get_filtered_recordset
|
90
|
-
filter_params = self.get_filter_params
|
91
|
-
unless filter_params.blank?
|
92
|
-
return self.get_recordset.where(**self.get_filter_params.to_hash.symbolize_keys)
|
93
|
-
end
|
94
|
-
return self.get_recordset
|
95
|
-
end
|
96
|
-
|
97
88
|
# Get a record by `id` or return a single record if recordset is filtered down to a single
|
98
89
|
# record.
|
99
90
|
def get_record
|
100
|
-
records = self.
|
91
|
+
records = self.get_filtered_data(self.get_recordset)
|
101
92
|
if params['id'] # direct lookup
|
102
93
|
return records.find(params['id'])
|
103
94
|
elsif records.length == 1
|
@@ -144,23 +135,28 @@ module RESTFramework
|
|
144
135
|
end
|
145
136
|
|
146
137
|
module ListModelMixin
|
147
|
-
# TODO: pagination classes like Django
|
148
138
|
def index
|
149
|
-
@records = self.
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
139
|
+
@records = self.get_filtered_data(self.get_recordset)
|
140
|
+
|
141
|
+
# Handle pagination, if enabled.
|
142
|
+
if self.class.paginator_class
|
143
|
+
paginator = self.class.paginator_class.new(data: @records, controller: self)
|
144
|
+
page = paginator.get_page
|
145
|
+
serialized_page = self.get_serializer_class.new(object: page, controller: self).serialize
|
146
|
+
data = paginator.get_paginated_response(serialized_page)
|
147
|
+
else
|
148
|
+
data = self.get_serializer_class.new(object: @records, controller: self).serialize
|
149
|
+
end
|
150
|
+
|
151
|
+
return api_response(data)
|
154
152
|
end
|
155
153
|
end
|
156
154
|
|
157
155
|
module ShowModelMixin
|
158
156
|
def show
|
159
157
|
@record = self.get_record
|
160
|
-
|
161
|
-
|
162
|
-
).serialize
|
163
|
-
return api_response(@serialized_record)
|
158
|
+
serialized_record = self.get_serializer_class.new(object: @record, controller: self).serialize
|
159
|
+
return api_response(serialized_record)
|
164
160
|
end
|
165
161
|
end
|
166
162
|
|
@@ -173,10 +169,8 @@ module RESTFramework
|
|
173
169
|
# Otherwise, perform a "bare" create.
|
174
170
|
@record = self.get_model.create!(self.get_create_params)
|
175
171
|
end
|
176
|
-
|
177
|
-
|
178
|
-
).serialize
|
179
|
-
return api_response(@serialized_record)
|
172
|
+
serialized_record = self.get_serializer_class.new(object: @record, controller: self).serialize
|
173
|
+
return api_response(serialized_record)
|
180
174
|
end
|
181
175
|
end
|
182
176
|
|
@@ -184,10 +178,8 @@ module RESTFramework
|
|
184
178
|
def update
|
185
179
|
@record = self.get_record
|
186
180
|
@record.update!(self.get_update_params)
|
187
|
-
|
188
|
-
|
189
|
-
).serialize
|
190
|
-
return api_response(@serialized_record)
|
181
|
+
serialized_record = self.get_serializer_class.new(object: @record, controller: self).serialize
|
182
|
+
return api_response(serialized_record)
|
191
183
|
end
|
192
184
|
end
|
193
185
|
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module RESTFramework
|
2
|
+
class BaseFilter
|
3
|
+
def initialize(controller:)
|
4
|
+
@controller = controller
|
5
|
+
end
|
6
|
+
|
7
|
+
def get_filtered_data(data)
|
8
|
+
raise NotImplementedError
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# A simple filtering backend that supports filtering a recordset based on fields defined on the
|
13
|
+
# controller class.
|
14
|
+
class ModelFilter < BaseFilter
|
15
|
+
# Filter params for keys allowed by the current action's filterset_fields/fields config.
|
16
|
+
def _get_filter_params
|
17
|
+
fields = @controller.class.filterset_fields || @controller.send(:get_fields)
|
18
|
+
return @controller.request.query_parameters.select { |p|
|
19
|
+
fields.include?(p.to_sym) || fields.include?(p.to_s)
|
20
|
+
}.to_hash.symbolize_keys
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_filtered_data(data)
|
24
|
+
filter_params = self._get_filter_params
|
25
|
+
unless filter_params.blank?
|
26
|
+
return data.where(**filter_params)
|
27
|
+
end
|
28
|
+
|
29
|
+
return data
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# A filter backend which handles ordering of the recordset.
|
34
|
+
class ModelOrderingFilter < BaseFilter
|
35
|
+
# Convert ordering string to an ordering configuration.
|
36
|
+
def _get_ordering
|
37
|
+
return nil unless @controller.class.ordering_query_param
|
38
|
+
|
39
|
+
order_string = @controller.params[@controller.class.ordering_query_param]
|
40
|
+
unless order_string.blank?
|
41
|
+
return order_string.split(',').map { |field|
|
42
|
+
if field[0] == '-'
|
43
|
+
[field[1..-1].to_sym, :desc]
|
44
|
+
else
|
45
|
+
[field.to_sym, :asc]
|
46
|
+
end
|
47
|
+
}.to_h
|
48
|
+
end
|
49
|
+
|
50
|
+
return nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_filtered_data(data)
|
54
|
+
ordering = self._get_ordering
|
55
|
+
if ordering && !ordering.empty?
|
56
|
+
return data.order(_get_ordering)
|
57
|
+
end
|
58
|
+
return data
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# TODO: implement searching within fields rather than exact match filtering (ModelFilter)
|
63
|
+
# class ModelSearchFilter < BaseFilter
|
64
|
+
# end
|
65
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module RESTFramework
|
2
|
+
|
3
|
+
# A simple paginator based on page numbers.
|
4
|
+
#
|
5
|
+
# Example: http://example.com/api/users/?page=3&page_size=50
|
6
|
+
class PageNumberPaginator
|
7
|
+
def initialize(data:, controller:, **kwargs)
|
8
|
+
@data = data
|
9
|
+
@controller = controller
|
10
|
+
@count = data.count
|
11
|
+
@page_size = self._page_size
|
12
|
+
|
13
|
+
@total_pages = @count / @page_size
|
14
|
+
@total_pages += 1 if @count % @page_size
|
15
|
+
end
|
16
|
+
|
17
|
+
def _page_size
|
18
|
+
page_size = nil
|
19
|
+
|
20
|
+
# Get from context, if allowed.
|
21
|
+
if @controller.class.page_size_query_param
|
22
|
+
page_size = @controller.params[@controller.class.page_size_query_param].presence
|
23
|
+
if page_size
|
24
|
+
page_size = page_size.to_i
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Otherwise, get from config.
|
29
|
+
if !page_size && @controller.class.page_size
|
30
|
+
page_size = @controller.class.page_size
|
31
|
+
end
|
32
|
+
|
33
|
+
# Fallback to a page size of 15.
|
34
|
+
page_size = 15 unless page_size
|
35
|
+
|
36
|
+
# Ensure we don't exceed the max page size.
|
37
|
+
if @controller.class.max_page_size && page_size > @controller.class.max_page_size
|
38
|
+
page_size = @controller.class.max_page_size
|
39
|
+
end
|
40
|
+
|
41
|
+
# Ensure we return at least 1.
|
42
|
+
return page_size.zero? ? 1 : page_size
|
43
|
+
end
|
44
|
+
|
45
|
+
def _page_query_param
|
46
|
+
return @controller.class.page_query_param&.to_sym
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_page(page_number=nil)
|
50
|
+
# If page number isn't provided, infer from the params or use 1 as a fallback value.
|
51
|
+
if !page_number
|
52
|
+
page_number = @controller&.params&.[](self._page_query_param)
|
53
|
+
if page_number.blank?
|
54
|
+
page_number = 1
|
55
|
+
else
|
56
|
+
page_number = page_number.to_i
|
57
|
+
if page_number.zero?
|
58
|
+
page_number = 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
@page_number = page_number
|
63
|
+
|
64
|
+
# Get the data page and return it so the caller can serialize the data in the proper format.
|
65
|
+
page_index = @page_number - 1
|
66
|
+
return @data.limit(@page_size).offset(page_index * @page_size)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Wrap the serialized page with appripriate metadata.
|
70
|
+
def get_paginated_response(serialized_page)
|
71
|
+
return {
|
72
|
+
count: @count,
|
73
|
+
page: @page_number,
|
74
|
+
total_pages: @total_pages,
|
75
|
+
results: serialized_page,
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# TODO: implement this
|
81
|
+
# class CountOffsetPaginator
|
82
|
+
# end
|
83
|
+
|
84
|
+
end
|
@@ -2,9 +2,8 @@ module RESTFramework
|
|
2
2
|
class BaseSerializer
|
3
3
|
attr_reader :errors
|
4
4
|
|
5
|
-
def initialize(object: nil,
|
5
|
+
def initialize(object: nil, controller: nil, **kwargs)
|
6
6
|
@object = object
|
7
|
-
@data = data
|
8
7
|
@controller = controller
|
9
8
|
end
|
10
9
|
end
|
@@ -12,16 +11,15 @@ module RESTFramework
|
|
12
11
|
# This serializer uses `.as_json` to serialize objects. Despite the name, `.as_json` is a Rails
|
13
12
|
# method which converts objects to Ruby primitives (with the top-level being either an array or a
|
14
13
|
# hash).
|
15
|
-
class
|
14
|
+
class NativeSerializer < BaseSerializer
|
16
15
|
class_attribute :config
|
17
16
|
class_attribute :singular_config
|
18
17
|
class_attribute :plural_config
|
19
18
|
class_attribute :action_config
|
20
19
|
|
21
|
-
def initialize(
|
20
|
+
def initialize(many: nil, **kwargs)
|
22
21
|
super(**kwargs)
|
23
22
|
@many = many
|
24
|
-
@model = model || (@controller ? @controller.send(:get_model) : nil)
|
25
23
|
end
|
26
24
|
|
27
25
|
# Get controller action, if possible.
|
@@ -34,41 +32,36 @@ module RESTFramework
|
|
34
32
|
action = self.get_action
|
35
33
|
|
36
34
|
if action && self.action_config
|
37
|
-
#
|
35
|
+
# Index action should use :list serializer config if :index is not provided.
|
38
36
|
action = :list if action == :index && !self.action_config.key?(:index)
|
39
37
|
|
40
38
|
return self.action_config[action] if self.action_config[action]
|
41
39
|
end
|
42
40
|
|
43
|
-
#
|
41
|
+
# No action_config, so try singular/plural config.
|
44
42
|
return self.plural_config if @many && self.plural_config
|
45
43
|
return self.singular_config if !@many && self.singular_config
|
46
44
|
|
47
|
-
#
|
45
|
+
# Lastly, try returning the default config.
|
48
46
|
return self.config
|
49
47
|
end
|
50
48
|
|
51
|
-
# Get a configuration passable to `as_json` for the
|
49
|
+
# Get a configuration passable to `as_json` for the object.
|
52
50
|
def get_serializer_config
|
53
|
-
#
|
54
|
-
local_config = self.get_local_serializer_config
|
55
|
-
|
56
|
-
|
57
|
-
# return a serializer config if one is defined
|
58
|
-
serializer_config = @controller.send(:get_native_serializer_config)
|
59
|
-
return serializer_config if serializer_config
|
51
|
+
# Return a locally defined serializer config if one is defined.
|
52
|
+
if local_config = self.get_local_serializer_config
|
53
|
+
return local_config
|
54
|
+
end
|
60
55
|
|
61
|
-
#
|
62
|
-
|
63
|
-
|
64
|
-
columns, methods = fields.partition { |f| f.to_s.in?(@model.column_names) }
|
65
|
-
return {only: columns, methods: methods}
|
56
|
+
# Return a serializer config if one is defined.
|
57
|
+
if serializer_config = @controller.send(:get_native_serializer_config)
|
58
|
+
return serializer_config
|
66
59
|
end
|
67
60
|
|
68
61
|
return {}
|
69
62
|
end
|
70
63
|
|
71
|
-
# Convert the object(
|
64
|
+
# Convert the object (record or recordset) to Ruby primitives.
|
72
65
|
def serialize
|
73
66
|
if @object
|
74
67
|
@many = @object.respond_to?(:each) if @many.nil?
|
@@ -105,4 +98,28 @@ module RESTFramework
|
|
105
98
|
return @_nested_config[key] = value
|
106
99
|
end
|
107
100
|
end
|
101
|
+
|
102
|
+
# `NativeModelSerializer` is similar to `NativeSerializer` but with some customizations to work
|
103
|
+
# with `ActiveModel`.
|
104
|
+
class NativeModelSerializer < NativeSerializer
|
105
|
+
def initialize(model: nil, **kwargs)
|
106
|
+
super(**kwargs)
|
107
|
+
@model = model || (@controller ? @controller.send(:get_model) : nil)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Get a configuration passable to `as_json` for the object.
|
111
|
+
def get_serializer_config
|
112
|
+
config = super
|
113
|
+
return config unless config.blank?
|
114
|
+
|
115
|
+
# If the config wasn't determined, build a serializer config from model fields.
|
116
|
+
fields = @controller.try(:get_fields) if @controller
|
117
|
+
unless fields.blank?
|
118
|
+
columns, methods = fields.partition { |f| f.to_s.in?(@model.column_names) }
|
119
|
+
return {only: columns, methods: methods}
|
120
|
+
end
|
121
|
+
|
122
|
+
return {}
|
123
|
+
end
|
124
|
+
end
|
108
125
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest_framework
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gregory N. Schmit
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -43,6 +43,8 @@ files:
|
|
43
43
|
- lib/rest_framework/controller_mixins/base.rb
|
44
44
|
- lib/rest_framework/controller_mixins/models.rb
|
45
45
|
- lib/rest_framework/engine.rb
|
46
|
+
- lib/rest_framework/filters.rb
|
47
|
+
- lib/rest_framework/paginators.rb
|
46
48
|
- lib/rest_framework/routers.rb
|
47
49
|
- lib/rest_framework/serializers.rb
|
48
50
|
- lib/rest_framework/version.rb
|