rest_framework 0.0.15 → 0.0.16
Sign up to get free protection for your applications and to get access to all the features.
- 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
|