rest_framework 0.0.15 → 0.2.0
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 +17 -23
- data/app/views/layouts/rest_framework.html.erb +2 -2
- data/lib/rest_framework.rb +6 -0
- data/lib/rest_framework/VERSION_STAMP +1 -1
- data/lib/rest_framework/controller_mixins.rb +4 -0
- data/lib/rest_framework/controller_mixins/base.rb +166 -111
- data/lib/rest_framework/controller_mixins/models.rb +216 -178
- data/lib/rest_framework/errors.rb +26 -0
- data/lib/rest_framework/filters.rb +68 -0
- data/lib/rest_framework/generators.rb +5 -0
- data/lib/rest_framework/generators/controller_generator.rb +26 -0
- data/lib/rest_framework/paginators.rb +95 -0
- data/lib/rest_framework/routers.rb +88 -44
- data/lib/rest_framework/serializers.rb +132 -81
- data/lib/rest_framework/version.rb +11 -9
- metadata +10 -5
@@ -0,0 +1,26 @@
|
|
1
|
+
# Top-level class for all REST Framework errors.
|
2
|
+
class RESTFramework::Error < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
class RESTFramework::NilPassedToAPIResponseError < RESTFramework::Error
|
6
|
+
def message
|
7
|
+
return <<~MSG.split("\n").join(' ')
|
8
|
+
Payload of `nil` was passed to `api_response`; this is unsupported. If you want a blank
|
9
|
+
response, pass `''` (an empty string) as the payload. If this was the result of a `find_by`
|
10
|
+
(or similar Active Record method) not finding a record, you should use the bang version (e.g.,
|
11
|
+
`find_by!`) to raise `ActiveRecord::RecordNotFound`, which the REST controller will catch and
|
12
|
+
return an appropriate error response.
|
13
|
+
MSG
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class RESTFramework::UnserializableError < RESTFramework::Error
|
18
|
+
def initialize(object)
|
19
|
+
@object = object
|
20
|
+
return super
|
21
|
+
end
|
22
|
+
|
23
|
+
def message
|
24
|
+
return "Unable to serialize `#{@object.inspect}` (of type `#{@object.class}`)."
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class RESTFramework::BaseFilter
|
2
|
+
def initialize(controller:)
|
3
|
+
@controller = controller
|
4
|
+
end
|
5
|
+
|
6
|
+
def get_filtered_data(data)
|
7
|
+
raise NotImplementedError
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
# A simple filtering backend that supports filtering a recordset based on fields defined on the
|
13
|
+
# controller class.
|
14
|
+
class RESTFramework::ModelFilter < RESTFramework::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&.map(&:to_s) || @controller.send(:get_fields)
|
18
|
+
return @controller.request.query_parameters.select { |p|
|
19
|
+
fields.include?(p)
|
20
|
+
}.to_h.symbolize_keys # convert from HashWithIndifferentAccess to Hash w/keys
|
21
|
+
end
|
22
|
+
|
23
|
+
# Filter data according to the request query parameters.
|
24
|
+
def get_filtered_data(data)
|
25
|
+
filter_params = self._get_filter_params
|
26
|
+
unless filter_params.blank?
|
27
|
+
return data.where(**filter_params)
|
28
|
+
end
|
29
|
+
|
30
|
+
return data
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# A filter backend which handles ordering of the recordset.
|
36
|
+
class RESTFramework::ModelOrderingFilter < RESTFramework::BaseFilter
|
37
|
+
# Convert ordering string to an ordering configuration.
|
38
|
+
def _get_ordering
|
39
|
+
return nil unless @controller.class.ordering_query_param
|
40
|
+
|
41
|
+
order_string = @controller.params[@controller.class.ordering_query_param]
|
42
|
+
unless order_string.blank?
|
43
|
+
return order_string.split(',').map { |field|
|
44
|
+
if field[0] == '-'
|
45
|
+
[field[1..-1].to_sym, :desc]
|
46
|
+
else
|
47
|
+
[field.to_sym, :asc]
|
48
|
+
end
|
49
|
+
}.to_h
|
50
|
+
end
|
51
|
+
|
52
|
+
return nil
|
53
|
+
end
|
54
|
+
|
55
|
+
# Order data according to the request query parameters.
|
56
|
+
def get_filtered_data(data)
|
57
|
+
ordering = self._get_ordering
|
58
|
+
if ordering && !ordering.empty?
|
59
|
+
return data.order(_get_ordering)
|
60
|
+
end
|
61
|
+
return data
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# TODO: implement searching within fields rather than exact match filtering (ModelFilter)
|
67
|
+
# class RESTFramework::ModelSearchFilter < RESTFramework::BaseFilter
|
68
|
+
# end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
|
4
|
+
class RESTFramework::Generators::ControllerGenerator < Rails::Generators::Base
|
5
|
+
desc <<~END
|
6
|
+
Description:
|
7
|
+
Stubs out a active_scaffolded controller. Pass the model name,
|
8
|
+
either CamelCased or under_scored.
|
9
|
+
The controller name is retrieved as a pluralized version of the model
|
10
|
+
name.
|
11
|
+
To create a controller within a module, specify the model name as a
|
12
|
+
path like 'parent_module/controller_name'.
|
13
|
+
This generates a controller class in app/controllers and invokes helper,
|
14
|
+
template engine and test framework generators.
|
15
|
+
Example:
|
16
|
+
`rails generate rest_framework:controller CreditCard`
|
17
|
+
Credit card controller with URLs like /credit_card/debit.
|
18
|
+
Controller: app/controllers/credit_cards_controller.rb
|
19
|
+
Functional Test: test/functional/credit_cards_controller_test.rb
|
20
|
+
Helper: app/helpers/credit_cards_helper.rb
|
21
|
+
END
|
22
|
+
|
23
|
+
def create_controller_file
|
24
|
+
create_file "app/controllers/some_controller.rb", "# Add initialization content here"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
class RESTFramework::BasePaginator
|
2
|
+
def initialize(data:, controller:, **kwargs)
|
3
|
+
@data = data
|
4
|
+
@controller = controller
|
5
|
+
end
|
6
|
+
|
7
|
+
# Get the page and return it so the caller can serialize it.
|
8
|
+
def get_page
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
# Wrap the serialized page with appropriate metadata.
|
13
|
+
def get_paginated_response(serialized_page)
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
# A simple paginator based on page numbers.
|
20
|
+
#
|
21
|
+
# Example: http://example.com/api/users/?page=3&page_size=50
|
22
|
+
class RESTFramework::PageNumberPaginator < RESTFramework::BasePaginator
|
23
|
+
def initialize(**kwargs)
|
24
|
+
super
|
25
|
+
@count = @data.count
|
26
|
+
@page_size = self._page_size
|
27
|
+
|
28
|
+
@total_pages = @count / @page_size
|
29
|
+
@total_pages += 1 if (@count % @page_size != 0)
|
30
|
+
end
|
31
|
+
|
32
|
+
def _page_size
|
33
|
+
page_size = nil
|
34
|
+
|
35
|
+
# Get from context, if allowed.
|
36
|
+
if @controller.class.page_size_query_param
|
37
|
+
if page_size = @controller.params[@controller.class.page_size_query_param].presence
|
38
|
+
page_size = page_size.to_i
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Otherwise, get from config.
|
43
|
+
if !page_size && @controller.class.page_size
|
44
|
+
page_size = @controller.class.page_size
|
45
|
+
end
|
46
|
+
|
47
|
+
# Ensure we don't exceed the max page size.
|
48
|
+
if @controller.class.max_page_size && page_size > @controller.class.max_page_size
|
49
|
+
page_size = @controller.class.max_page_size
|
50
|
+
end
|
51
|
+
|
52
|
+
# Ensure we return at least 1.
|
53
|
+
return page_size.zero? ? 1 : page_size
|
54
|
+
end
|
55
|
+
|
56
|
+
def _page_query_param
|
57
|
+
return @controller.class.page_query_param&.to_sym
|
58
|
+
end
|
59
|
+
|
60
|
+
# Get the page and return it so the caller can serialize it.
|
61
|
+
def get_page(page_number=nil)
|
62
|
+
# If page number isn't provided, infer from the params or use 1 as a fallback value.
|
63
|
+
if !page_number
|
64
|
+
page_number = @controller&.params&.[](self._page_query_param)
|
65
|
+
if page_number.blank?
|
66
|
+
page_number = 1
|
67
|
+
else
|
68
|
+
page_number = page_number.to_i
|
69
|
+
if page_number.zero?
|
70
|
+
page_number = 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
@page_number = page_number
|
75
|
+
|
76
|
+
# Get the data page and return it so the caller can serialize the data in the proper format.
|
77
|
+
page_index = @page_number - 1
|
78
|
+
return @data.limit(@page_size).offset(page_index * @page_size)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Wrap the serialized page with appropriate metadata. TODO: include links.
|
82
|
+
def get_paginated_response(serialized_page)
|
83
|
+
return {
|
84
|
+
count: @count,
|
85
|
+
page: @page_number,
|
86
|
+
total_pages: @total_pages,
|
87
|
+
results: serialized_page,
|
88
|
+
}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
# TODO: implement this
|
94
|
+
# class RESTFramework::CountOffsetPaginator
|
95
|
+
# end
|
@@ -1,8 +1,44 @@
|
|
1
1
|
require 'action_dispatch/routing/mapper'
|
2
2
|
|
3
|
+
|
3
4
|
module ActionDispatch::Routing
|
4
5
|
class Mapper
|
5
|
-
#
|
6
|
+
# Internal helper to take extra_actions hash and convert to a consistent format.
|
7
|
+
protected def _parse_extra_actions(extra_actions)
|
8
|
+
return (extra_actions || {}).map do |k,v|
|
9
|
+
kwargs = {}
|
10
|
+
path = k
|
11
|
+
|
12
|
+
# Convert structure to path/methods/kwargs.
|
13
|
+
if v.is_a?(Hash) # allow kwargs
|
14
|
+
v = v.symbolize_keys
|
15
|
+
|
16
|
+
# Ensure methods is an array.
|
17
|
+
if v[:methods].is_a?(String) || v[:methods].is_a?(Symbol)
|
18
|
+
methods = [v.delete(:methods)]
|
19
|
+
else
|
20
|
+
methods = v.delete(:methods)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Override path if it's provided.
|
24
|
+
if v.key?(:path)
|
25
|
+
path = v.delete(:path)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Pass any further kwargs to the underlying Rails interface.
|
29
|
+
kwargs = kwargs.merge(v)
|
30
|
+
elsif v.is_a?(Symbol) || v.is_a?(String)
|
31
|
+
methods = [v]
|
32
|
+
else
|
33
|
+
methods = v
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return a hash with keys: :path, :methods, :kwargs.
|
37
|
+
{path: path, methods: methods, kwargs: kwargs}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Internal interface to get the controller class from the name and current scope.
|
6
42
|
protected def _get_controller_class(name, pluralize: true, fallback_reverse_pluralization: true)
|
7
43
|
# get class name
|
8
44
|
name = name.to_s.camelize # camelize to leave plural names plural
|
@@ -36,20 +72,20 @@ module ActionDispatch::Routing
|
|
36
72
|
return controller
|
37
73
|
end
|
38
74
|
|
39
|
-
#
|
75
|
+
# Internal core implementation of the `rest_resource(s)` router, both singular and plural.
|
40
76
|
# @param default_singular [Boolean] the default plurality of the resource if the plurality is
|
41
77
|
# not otherwise defined by the controller
|
42
78
|
# @param name [Symbol] the resource name, from which path and controller are deduced by default
|
43
|
-
# @param skip_undefined [Boolean] whether we should skip routing undefined actions
|
79
|
+
# @param skip_undefined [Boolean] whether we should skip routing undefined resourceful actions
|
44
80
|
protected def _rest_resources(default_singular, name, skip_undefined: true, **kwargs, &block)
|
45
|
-
controller = kwargs
|
81
|
+
controller = kwargs.delete(:controller) || name
|
46
82
|
if controller.is_a?(Class)
|
47
83
|
controller_class = controller
|
48
84
|
else
|
49
85
|
controller_class = _get_controller_class(controller, pluralize: !default_singular)
|
50
86
|
end
|
51
87
|
|
52
|
-
#
|
88
|
+
# Set controller if it's not explicitly set.
|
53
89
|
kwargs[:controller] = name unless kwargs[:controller]
|
54
90
|
|
55
91
|
# determine plural/singular resource
|
@@ -70,25 +106,20 @@ module ActionDispatch::Routing
|
|
70
106
|
public_send(resource_method, name, except: skip, **kwargs) do
|
71
107
|
if controller_class.respond_to?(:extra_member_actions)
|
72
108
|
member do
|
73
|
-
actions = controller_class.extra_member_actions
|
74
|
-
actions
|
75
|
-
|
76
|
-
|
77
|
-
methods.each do |m|
|
78
|
-
public_send(m, action)
|
109
|
+
actions = self._parse_extra_actions(controller_class.extra_member_actions)
|
110
|
+
actions.each do |action_config|
|
111
|
+
action_config[:methods].each do |m|
|
112
|
+
public_send(m, action_config[:path], **action_config[:kwargs])
|
79
113
|
end
|
80
114
|
end
|
81
115
|
end
|
82
116
|
end
|
83
117
|
|
84
118
|
collection do
|
85
|
-
actions = controller_class.extra_actions
|
86
|
-
actions
|
87
|
-
|
88
|
-
|
89
|
-
methods = [methods] if methods.is_a?(Symbol) || methods.is_a?(String)
|
90
|
-
methods.each do |m|
|
91
|
-
public_send(m, action)
|
119
|
+
actions = self._parse_extra_actions(controller_class.extra_actions)
|
120
|
+
actions.each do |action_config|
|
121
|
+
action_config[:methods].each do |m|
|
122
|
+
public_send(m, action_config[:path], **action_config[:kwargs])
|
92
123
|
end
|
93
124
|
end
|
94
125
|
end
|
@@ -112,42 +143,55 @@ module ActionDispatch::Routing
|
|
112
143
|
end
|
113
144
|
|
114
145
|
# Route a controller without the default resourceful paths.
|
115
|
-
def rest_route(
|
116
|
-
controller = kwargs.delete(:controller) ||
|
117
|
-
|
146
|
+
def rest_route(name=nil, **kwargs, &block)
|
147
|
+
controller = kwargs.delete(:controller) || name
|
148
|
+
if controller.is_a?(Class)
|
149
|
+
controller_class = controller
|
150
|
+
else
|
151
|
+
controller_class = self._get_controller_class(controller, pluralize: false)
|
152
|
+
end
|
118
153
|
|
119
|
-
#
|
120
|
-
|
121
|
-
|
122
|
-
actions
|
123
|
-
actions =
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
154
|
+
# Set controller if it's not explicitly set.
|
155
|
+
kwargs[:controller] = name unless kwargs[:controller]
|
156
|
+
|
157
|
+
# Route actions using the resourceful router, but skip all builtin actions.
|
158
|
+
actions = self._parse_extra_actions(controller_class.extra_actions)
|
159
|
+
public_send(:resource, name, only: [], **kwargs) do
|
160
|
+
actions.each do |action_config|
|
161
|
+
action_config[:methods].each do |m|
|
162
|
+
public_send(m, action_config[:path], **action_config[:kwargs])
|
163
|
+
end
|
164
|
+
yield if block_given?
|
129
165
|
end
|
130
|
-
yield if block_given?
|
131
166
|
end
|
132
167
|
end
|
133
168
|
|
134
169
|
# Route a controller's `#root` to '/' in the current scope/namespace, along with other actions.
|
135
|
-
# @param
|
136
|
-
def rest_root(
|
137
|
-
#
|
170
|
+
# @param name [Symbol] the snake_case name of the controller
|
171
|
+
def rest_root(name=nil, **kwargs, &block)
|
172
|
+
# By default, use RootController#root.
|
138
173
|
root_action = kwargs.delete(:action) || :root
|
139
|
-
controller = kwargs.delete(:controller) ||
|
140
|
-
path = path.to_s
|
174
|
+
controller = kwargs.delete(:controller) || name || :root
|
141
175
|
|
142
|
-
#
|
143
|
-
get
|
176
|
+
# Route the root.
|
177
|
+
get name.to_s, controller: controller, action: root_action
|
144
178
|
|
145
|
-
#
|
179
|
+
# Route any additional actions.
|
146
180
|
controller_class = self._get_controller_class(controller, pluralize: false)
|
147
|
-
(controller_class.extra_actions
|
148
|
-
|
149
|
-
|
150
|
-
|
181
|
+
actions = self._parse_extra_actions(controller_class.extra_actions)
|
182
|
+
actions.each do |action_config|
|
183
|
+
# Add :action unless kwargs defines it.
|
184
|
+
unless action_config[:kwargs].key?(:action)
|
185
|
+
action_config[:kwargs][:action] = action_config[:path]
|
186
|
+
end
|
187
|
+
|
188
|
+
action_config[:methods].each do |m|
|
189
|
+
public_send(
|
190
|
+
m,
|
191
|
+
File.join(name.to_s, action_config[:path].to_s),
|
192
|
+
controller: controller,
|
193
|
+
**action_config[:kwargs],
|
194
|
+
)
|
151
195
|
end
|
152
196
|
yield if block_given?
|
153
197
|
end
|
@@ -1,108 +1,159 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(object: nil, data: nil, controller: nil, **kwargs)
|
6
|
-
@object = object
|
7
|
-
@data = data
|
8
|
-
@controller = controller
|
9
|
-
end
|
1
|
+
class RESTFramework::BaseSerializer
|
2
|
+
def initialize(object: nil, controller: nil, **kwargs)
|
3
|
+
@object = object
|
4
|
+
@controller = controller
|
10
5
|
end
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
# This serializer uses `.serializable_hash` to convert objects to Ruby primitives (with the
|
10
|
+
# top-level being either an array or a hash).
|
11
|
+
class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
12
|
+
class_attribute :config
|
13
|
+
class_attribute :singular_config
|
14
|
+
class_attribute :plural_config
|
15
|
+
class_attribute :action_config
|
16
|
+
|
17
|
+
def initialize(many: nil, model: nil, **kwargs)
|
18
|
+
super(**kwargs)
|
11
19
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
class_attribute :config
|
17
|
-
class_attribute :singular_config
|
18
|
-
class_attribute :plural_config
|
19
|
-
class_attribute :action_config
|
20
|
-
|
21
|
-
def initialize(model: nil, many: nil, **kwargs)
|
22
|
-
super(**kwargs)
|
20
|
+
if many.nil?
|
21
|
+
# Determine if we are dealing with many objects or just one.
|
22
|
+
@many = @object.is_a?(Enumerable)
|
23
|
+
else
|
23
24
|
@many = many
|
24
|
-
@model = model || (@controller ? @controller.send(:get_model) : nil)
|
25
25
|
end
|
26
26
|
|
27
|
-
#
|
28
|
-
|
29
|
-
|
27
|
+
# Determine model either explicitly, or by inspecting @object or @controller.
|
28
|
+
@model = model
|
29
|
+
@model ||= @object.class if @object.is_a?(ActiveRecord::Base)
|
30
|
+
@model ||= @object[0].class if @many && @object[0].is_a?(ActiveRecord::Base)
|
31
|
+
@model ||= @controller.send(:get_model) if @controller
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get controller action, if possible.
|
35
|
+
def get_action
|
36
|
+
return @controller&.action_name&.to_sym
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get a locally defined native serializer configuration, if one is defined.
|
40
|
+
def get_local_native_serializer_config
|
41
|
+
action = self.get_action
|
42
|
+
|
43
|
+
if action && self.action_config
|
44
|
+
# Index action should use :list serializer config if :index is not provided.
|
45
|
+
action = :list if action == :index && !self.action_config.key?(:index)
|
46
|
+
|
47
|
+
return self.action_config[action] if self.action_config[action]
|
30
48
|
end
|
31
49
|
|
32
|
-
#
|
33
|
-
|
34
|
-
|
50
|
+
# No action_config, so try singular/plural config if explicitly instructed to via @many.
|
51
|
+
return self.plural_config if @many == true && self.plural_config
|
52
|
+
return self.singular_config if @many == false && self.singular_config
|
35
53
|
|
36
|
-
|
37
|
-
|
38
|
-
|
54
|
+
# Lastly, try returning the default config, or singular/plural config in that order.
|
55
|
+
return self.config || self.singular_config || self.plural_config
|
56
|
+
end
|
39
57
|
|
40
|
-
|
41
|
-
|
58
|
+
# Helper to get a native serializer configuration from the controller.
|
59
|
+
def get_controller_native_serializer_config
|
60
|
+
return nil unless @controller
|
42
61
|
|
43
|
-
|
44
|
-
|
45
|
-
|
62
|
+
if @many == true
|
63
|
+
controller_serializer = @controller.try(:native_serializer_plural_config)
|
64
|
+
elsif @many == false
|
65
|
+
controller_serializer = @controller.try(:native_serializer_singular_config)
|
66
|
+
end
|
67
|
+
|
68
|
+
return controller_serializer || @controller.try(:native_serializer_config)
|
69
|
+
end
|
46
70
|
|
47
|
-
|
48
|
-
|
71
|
+
# Get a configuration passable to `serializable_hash` for the object.
|
72
|
+
def get_serializer_config
|
73
|
+
# Return a locally defined serializer config if one is defined.
|
74
|
+
if local_config = self.get_local_native_serializer_config
|
75
|
+
return local_config
|
49
76
|
end
|
50
77
|
|
51
|
-
#
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
columns, methods = fields.partition { |f| f.to_s.in?(@model.column_names) }
|
65
|
-
return {only: columns, methods: methods}
|
78
|
+
# Return a serializer config if one is defined on the controller.
|
79
|
+
if serializer_config = get_controller_native_serializer_config
|
80
|
+
return serializer_config
|
81
|
+
end
|
82
|
+
|
83
|
+
# If the config wasn't determined, build a serializer config from model fields.
|
84
|
+
fields = @controller.send(:get_fields) if @controller
|
85
|
+
if fields
|
86
|
+
if @model
|
87
|
+
columns, methods = fields.partition { |f| f.in?(@model.column_names) }
|
88
|
+
else
|
89
|
+
columns = fields
|
90
|
+
methods = []
|
66
91
|
end
|
67
92
|
|
68
|
-
return {}
|
93
|
+
return {only: columns, methods: methods}
|
69
94
|
end
|
70
95
|
|
71
|
-
#
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
96
|
+
# By default, pass an empty configuration, allowing the serialization of all columns.
|
97
|
+
return {}
|
98
|
+
end
|
99
|
+
|
100
|
+
# Convert the object (record or recordset) to Ruby primitives.
|
101
|
+
def serialize
|
102
|
+
if @object
|
103
|
+
begin
|
104
|
+
if @object.is_a?(Enumerable)
|
105
|
+
return @object.map { |r| r.serializable_hash(self.get_serializer_config) }
|
106
|
+
end
|
107
|
+
return @object.serializable_hash(self.get_serializer_config)
|
108
|
+
rescue NoMethodError
|
76
109
|
end
|
77
|
-
return nil
|
78
110
|
end
|
79
111
|
|
80
|
-
#
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
112
|
+
# Raise an error if we cannot serialize the object.
|
113
|
+
raise RESTFramework::UnserializableError.new(@object)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Allow a serializer instance to be used as a hash directly in a nested serializer config.
|
117
|
+
def [](key)
|
118
|
+
unless instance_variable_defined?(:@_nested_config)
|
119
|
+
@_nested_config = self.get_serializer_config
|
86
120
|
end
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
121
|
+
return @_nested_config[key]
|
122
|
+
end
|
123
|
+
def []=(key, value)
|
124
|
+
unless instance_variable_defined?(:@_nested_config)
|
125
|
+
@_nested_config = self.get_serializer_config
|
92
126
|
end
|
127
|
+
return @_nested_config[key] = value
|
128
|
+
end
|
93
129
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
99
|
-
return @_nested_config[key]
|
130
|
+
# Allow a serializer class to be used as a hash directly in a nested serializer config.
|
131
|
+
def self.[](key)
|
132
|
+
unless instance_variable_defined?(:@_nested_config)
|
133
|
+
@_nested_config = self.new.get_serializer_config
|
100
134
|
end
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
135
|
+
return @_nested_config[key]
|
136
|
+
end
|
137
|
+
def self.[]=(key, value)
|
138
|
+
unless instance_variable_defined?(:@_nested_config)
|
139
|
+
@_nested_config = self.new.get_serializer_config
|
106
140
|
end
|
141
|
+
return @_nested_config[key] = value
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
# :nocov:
|
147
|
+
# Alias NativeModelSerializer -> NativeSerializer.
|
148
|
+
class RESTFramework::NativeModelSerializer < RESTFramework::NativeSerializer
|
149
|
+
def initialize(**kwargs)
|
150
|
+
super
|
151
|
+
ActiveSupport::Deprecation.warn(
|
152
|
+
<<~MSG.split("\n").join(' ')
|
153
|
+
RESTFramework::NativeModelSerializer is deprecated and will be removed in future versions of
|
154
|
+
REST Framework; you should use RESTFramework::NativeSerializer instead.
|
155
|
+
MSG
|
156
|
+
)
|
107
157
|
end
|
108
158
|
end
|
159
|
+
# :nocov:
|