rest_framework 0.1.3 → 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 +3 -2
- data/lib/rest_framework.rb +2 -0
- data/lib/rest_framework/VERSION_STAMP +1 -1
- data/lib/rest_framework/controller_mixins/base.rb +39 -30
- data/lib/rest_framework/controller_mixins/models.rb +78 -68
- data/lib/rest_framework/errors.rb +26 -0
- data/lib/rest_framework/filters.rb +3 -3
- data/lib/rest_framework/generators.rb +5 -0
- data/lib/rest_framework/generators/controller_generator.rb +26 -0
- data/lib/rest_framework/routers.rb +1 -0
- data/lib/rest_framework/serializers.rb +65 -24
- data/lib/rest_framework/version.rb +11 -9
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ef7499f4d1e10af1f2520d3b9847dc7f711bd1c7f4377efe21bd5b60c0dd9a7
|
4
|
+
data.tar.gz: b15a9b0abee58e315fdc3a1157ca04d7051630821d3ed2ca691564966b5fefca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb586a083989a67f206d5b5d6c14eb546949569e384d3b44c870de5f49f610b5848d53da87e01234542705304dd7d49bcf6f0037f0b89331c7fa2a1b084422ce
|
7
|
+
data.tar.gz: fe4c960cda0d2a9ba43cb7d5b1cfa84963802800829323c26b350841bbd34290e68cbbc4f14a3138a1cb95a28aafff76b2d9a78a41772950e00ad80159c8f2f3
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/rest_framework)
|
4
4
|
[](https://travis-ci.org/gregschmit/rails-rest-framework)
|
5
|
+
[](https://coveralls.io/github/gregschmit/rails-rest-framework?branch=master)
|
5
6
|
|
6
7
|
A framework for DRY RESTful APIs in Ruby on Rails.
|
7
8
|
|
@@ -78,8 +79,8 @@ class Api::ReadOnlyMoviesController < ApiController
|
|
78
79
|
end
|
79
80
|
```
|
80
81
|
|
81
|
-
Note that you can also override
|
82
|
-
|
82
|
+
Note that you can also override the `get_recordset` instance method to override the API behavior
|
83
|
+
dynamically per-request.
|
83
84
|
|
84
85
|
### Routing
|
85
86
|
|
data/lib/rest_framework.rb
CHANGED
@@ -4,8 +4,10 @@ end
|
|
4
4
|
|
5
5
|
require_relative "rest_framework/controller_mixins"
|
6
6
|
require_relative "rest_framework/engine"
|
7
|
+
require_relative "rest_framework/errors"
|
7
8
|
require_relative "rest_framework/filters"
|
8
9
|
require_relative "rest_framework/paginators"
|
9
10
|
require_relative "rest_framework/routers"
|
10
11
|
require_relative "rest_framework/serializers"
|
11
12
|
require_relative "rest_framework/version"
|
13
|
+
require_relative "rest_framework/generators"
|
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require_relative '../errors'
|
1
2
|
require_relative '../serializers'
|
2
3
|
|
3
4
|
|
@@ -33,6 +34,9 @@ module RESTFramework::BaseControllerMixin
|
|
33
34
|
|
34
35
|
# Add class attributes (with defaults) unless they already exist.
|
35
36
|
{
|
37
|
+
filter_pk_from_request_body: true,
|
38
|
+
exclude_body_fields: [:created_at, :created_by, :updated_at, :updated_by],
|
39
|
+
accept_generic_params_as_body_params: true,
|
36
40
|
extra_actions: nil,
|
37
41
|
extra_member_actions: nil,
|
38
42
|
filter_backends: nil,
|
@@ -42,14 +46,16 @@ module RESTFramework::BaseControllerMixin
|
|
42
46
|
page_size_query_param: 'page_size',
|
43
47
|
max_page_size: nil,
|
44
48
|
serializer_class: nil,
|
49
|
+
serialize_to_json: true,
|
50
|
+
serialize_to_xml: true,
|
45
51
|
singleton_controller: nil,
|
46
52
|
skip_actions: nil,
|
47
53
|
}.each do |a, default|
|
48
54
|
unless base.respond_to?(a)
|
49
55
|
base.class_attribute(a)
|
50
56
|
|
51
|
-
# Set default manually so we can still support Rails 4. Maybe later we can use the
|
52
|
-
#
|
57
|
+
# Set default manually so we can still support Rails 4. Maybe later we can use the default
|
58
|
+
# parameter on `class_attribute`.
|
53
59
|
base.send(:"#{a}=", default)
|
54
60
|
end
|
55
61
|
end
|
@@ -60,10 +66,10 @@ module RESTFramework::BaseControllerMixin
|
|
60
66
|
base.alias_method(:extra_collection_actions=, :extra_actions=)
|
61
67
|
end
|
62
68
|
|
63
|
-
#
|
69
|
+
# Skip csrf since this is an API.
|
64
70
|
base.skip_before_action(:verify_authenticity_token) rescue nil
|
65
71
|
|
66
|
-
#
|
72
|
+
# Handle some common exceptions.
|
67
73
|
base.rescue_from(ActiveRecord::RecordNotFound, with: :record_not_found)
|
68
74
|
base.rescue_from(ActiveRecord::RecordInvalid, with: :record_invalid)
|
69
75
|
base.rescue_from(ActiveRecord::RecordNotSaved, with: :record_not_saved)
|
@@ -129,39 +135,42 @@ module RESTFramework::BaseControllerMixin
|
|
129
135
|
|
130
136
|
# Helper to render a browsable API for `html` format, along with basic `json`/`xml` formats, and
|
131
137
|
# with support or passing custom `kwargs` to the underlying `render` calls.
|
132
|
-
def api_response(payload, html_kwargs: nil,
|
138
|
+
def api_response(payload, html_kwargs: nil, **kwargs)
|
133
139
|
html_kwargs ||= {}
|
134
|
-
json_kwargs
|
135
|
-
xml_kwargs
|
136
|
-
|
137
|
-
#
|
138
|
-
|
140
|
+
json_kwargs = kwargs.delete(:json_kwargs) || {}
|
141
|
+
xml_kwargs = kwargs.delete(:xml_kwargs) || {}
|
142
|
+
|
143
|
+
# Raise helpful error if payload is nil. Usually this happens when a record is not found (e.g.,
|
144
|
+
# when passing something like `User.find_by(id: some_id)` to `api_response`). The caller should
|
145
|
+
# actually be calling `find_by!` to raise ActiveRecord::RecordNotFound and allowing the REST
|
146
|
+
# framework to catch this error and return an appropriate error response.
|
147
|
+
if payload.nil?
|
148
|
+
raise RESTFramework::NilPassedToAPIResponseError
|
149
|
+
end
|
139
150
|
|
140
151
|
respond_to do |format|
|
141
|
-
if
|
142
|
-
format.json {head :no_content}
|
143
|
-
format.xml {head :no_content}
|
152
|
+
if payload == ''
|
153
|
+
format.json {head :no_content} if self.serialize_to_json
|
154
|
+
format.xml {head :no_content} if self.serialize_to_xml
|
144
155
|
else
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
render(xml: payload, layout: false, **xkwargs)
|
155
|
-
}
|
156
|
-
end
|
156
|
+
format.json {
|
157
|
+
jkwargs = kwargs.merge(json_kwargs)
|
158
|
+
render(json: payload, layout: false, **jkwargs)
|
159
|
+
} if self.serialize_to_json
|
160
|
+
format.xml {
|
161
|
+
xkwargs = kwargs.merge(xml_kwargs)
|
162
|
+
render(xml: payload, layout: false, **xkwargs)
|
163
|
+
} if self.serialize_to_xml
|
164
|
+
# TODO: possibly support more formats here if supported?
|
157
165
|
end
|
158
166
|
format.html {
|
159
167
|
@payload = payload
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
@
|
168
|
+
if payload == ''
|
169
|
+
@json_payload = '' if self.serialize_to_json
|
170
|
+
@xml_payload = '' if self.serialize_to_xml
|
171
|
+
else
|
172
|
+
@json_payload = payload.to_json if self.serialize_to_json
|
173
|
+
@xml_payload = payload.to_xml if self.serialize_to_xml
|
165
174
|
end
|
166
175
|
@template_logo_text ||= "Rails REST Framework"
|
167
176
|
@title ||= self.controller_name.camelize
|
@@ -16,17 +16,18 @@ module RESTFramework::BaseModelControllerMixin
|
|
16
16
|
model: nil,
|
17
17
|
recordset: nil,
|
18
18
|
|
19
|
-
# Attributes for create/update parameters.
|
20
|
-
allowed_parameters: nil,
|
21
|
-
allowed_action_parameters: nil,
|
22
|
-
|
23
19
|
# Attributes for configuring record fields.
|
24
20
|
fields: nil,
|
25
21
|
action_fields: nil,
|
26
22
|
|
23
|
+
# Attributes for create/update parameters.
|
24
|
+
allowed_parameters: nil,
|
25
|
+
allowed_action_parameters: nil,
|
26
|
+
|
27
27
|
# Attributes for the default native serializer.
|
28
28
|
native_serializer_config: nil,
|
29
|
-
|
29
|
+
native_serializer_singular_config: nil,
|
30
|
+
native_serializer_plural_config: nil,
|
30
31
|
|
31
32
|
# Attributes for default model filtering (and ordering).
|
32
33
|
filterset_fields: nil,
|
@@ -34,13 +35,13 @@ module RESTFramework::BaseModelControllerMixin
|
|
34
35
|
ordering_query_param: 'ordering',
|
35
36
|
|
36
37
|
# Other misc attributes.
|
37
|
-
disable_creation_from_recordset:
|
38
|
+
disable_creation_from_recordset: false, # Option to disable `recordset.create` behavior.
|
38
39
|
}.each do |a, default|
|
39
40
|
unless base.respond_to?(a)
|
40
41
|
base.class_attribute(a)
|
41
42
|
|
42
|
-
# Set default manually so we can still support Rails 4. Maybe later we can use the
|
43
|
-
#
|
43
|
+
# Set default manually so we can still support Rails 4. Maybe later we can use the default
|
44
|
+
# parameter on `class_attribute`.
|
44
45
|
base.send(:"#{a}=", default)
|
45
46
|
end
|
46
47
|
end
|
@@ -49,36 +50,34 @@ module RESTFramework::BaseModelControllerMixin
|
|
49
50
|
|
50
51
|
protected
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
action_serializer_config = self.class.native_serializer_action_config || {}
|
56
|
-
action = self.action_name.to_sym
|
57
|
-
|
58
|
-
# Handle case where :index action is not defined.
|
59
|
-
if action == :index && !action_serializer_config.key?(:index)
|
60
|
-
# Default is :show if `singleton_controller`, otherwise :list.
|
61
|
-
action = self.class.singleton_controller ? :show : :list
|
62
|
-
end
|
53
|
+
def _get_specific_action_config(action_config_key, generic_config_key)
|
54
|
+
action_config = self.class.send(action_config_key) || {}
|
55
|
+
action = self.action_name&.to_sym
|
63
56
|
|
64
|
-
|
65
|
-
|
57
|
+
# Index action should use :list serializer if :index is not provided.
|
58
|
+
action = :list if action == :index && !action_config.key?(:index)
|
66
59
|
|
67
|
-
|
68
|
-
# @return [RESTFramework::BaseSerializer]
|
69
|
-
def get_serializer_class
|
70
|
-
return self.class.serializer_class || RESTFramework::NativeSerializer
|
60
|
+
return (action_config[action] if action) || self.class.send(generic_config_key)
|
71
61
|
end
|
72
62
|
|
73
63
|
# Get a list of parameters allowed for the current action.
|
74
64
|
def get_allowed_parameters
|
75
|
-
|
76
|
-
|
65
|
+
return _get_specific_action_config(:allowed_action_parameters, :allowed_parameters)&.map(&:to_s)
|
66
|
+
end
|
77
67
|
|
78
|
-
|
79
|
-
|
68
|
+
# Get a list of fields for the current action.
|
69
|
+
def get_fields
|
70
|
+
return (
|
71
|
+
_get_specific_action_config(:action_fields, :fields)&.map(&:to_s) ||
|
72
|
+
self.get_model&.column_names ||
|
73
|
+
[]
|
74
|
+
)
|
75
|
+
end
|
80
76
|
|
81
|
-
|
77
|
+
# Helper to get the configured serializer class, or `NativeSerializer` as a default.
|
78
|
+
# @return [RESTFramework::BaseSerializer]
|
79
|
+
def get_serializer_class
|
80
|
+
return self.class.serializer_class || RESTFramework::NativeSerializer
|
82
81
|
end
|
83
82
|
|
84
83
|
# Get the list of filtering backends to use.
|
@@ -89,70 +88,81 @@ module RESTFramework::BaseModelControllerMixin
|
|
89
88
|
]
|
90
89
|
end
|
91
90
|
|
92
|
-
#
|
93
|
-
def
|
94
|
-
|
95
|
-
|
91
|
+
# Filter the request body for keys in current action's allowed_parameters/fields config.
|
92
|
+
def get_body_params
|
93
|
+
return @_get_body_params ||= begin
|
94
|
+
fields = self.get_allowed_parameters || self.get_fields
|
95
|
+
|
96
|
+
# Filter the request body.
|
97
|
+
body_params = request.request_parameters.select { |p|
|
98
|
+
fields.include?(p)
|
99
|
+
}
|
100
|
+
|
101
|
+
# Add query params in place of missing body params, if configured.
|
102
|
+
if self.class.accept_generic_params_as_body_params
|
103
|
+
(fields - body_params.keys).each do |k|
|
104
|
+
if (value = params[k])
|
105
|
+
body_params[k] = value
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
96
109
|
|
97
|
-
|
98
|
-
|
110
|
+
# Filter primary key if configured.
|
111
|
+
if self.class.filter_pk_from_request_body
|
112
|
+
body_params.delete(self.get_model&.primary_key)
|
113
|
+
end
|
99
114
|
|
100
|
-
|
101
|
-
|
115
|
+
# Filter fields in exclude_body_fields.
|
116
|
+
(self.class.exclude_body_fields || []).each do |f|
|
117
|
+
body_params.delete(f.to_s)
|
118
|
+
end
|
102
119
|
|
103
|
-
|
104
|
-
|
105
|
-
fields = self.get_allowed_parameters || self.get_fields
|
106
|
-
return @get_body_params ||= (request.request_parameters.select { |p|
|
107
|
-
fields.include?(p.to_sym) || fields.include?(p.to_s)
|
108
|
-
})
|
120
|
+
body_params
|
121
|
+
end
|
109
122
|
end
|
110
123
|
alias :get_create_params :get_body_params
|
111
124
|
alias :get_update_params :get_body_params
|
112
125
|
|
113
|
-
# Get a record by the primary key from the filtered recordset.
|
126
|
+
# Get a record by the primary key from the (non-filtered) recordset.
|
114
127
|
def get_record
|
115
|
-
records = self.get_filtered_data(self.get_recordset)
|
116
128
|
if pk = params[self.get_model.primary_key]
|
117
|
-
return
|
129
|
+
return self.get_recordset.find(pk)
|
118
130
|
end
|
119
131
|
return nil
|
120
132
|
end
|
121
133
|
|
122
|
-
#
|
123
|
-
def
|
134
|
+
# Get the model for this controller.
|
135
|
+
def get_model(from_get_recordset: false)
|
124
136
|
return @model if instance_variable_defined?(:@model) && @model
|
125
137
|
return (@model = self.class.model) if self.class.model
|
126
|
-
|
127
|
-
|
128
|
-
|
138
|
+
|
139
|
+
# Delegate to the recordset's model, if it's defined.
|
140
|
+
unless from_get_recordset # prevent infinite recursion
|
141
|
+
if (recordset = self.get_recordset)
|
142
|
+
return @model = recordset.klass
|
143
|
+
end
|
129
144
|
end
|
145
|
+
|
146
|
+
# Try to determine model from controller name.
|
130
147
|
begin
|
131
148
|
return (@model = self.class.name.demodulize.match(/(.*)Controller/)[1].singularize.constantize)
|
132
149
|
rescue NameError
|
133
150
|
end
|
151
|
+
|
134
152
|
return nil
|
135
153
|
end
|
136
154
|
|
137
|
-
#
|
138
|
-
def
|
155
|
+
# Get the set of records this controller has access to.
|
156
|
+
def get_recordset
|
139
157
|
return @recordset if instance_variable_defined?(:@recordset) && @recordset
|
140
158
|
return (@recordset = self.class.recordset) if self.class.recordset
|
141
|
-
unless from_internal_get_model # prevent infinite recursion
|
142
|
-
model = self._get_model(from_internal_get_recordset: true)
|
143
|
-
return (@recordset = model.all) if model
|
144
|
-
end
|
145
|
-
return nil
|
146
|
-
end
|
147
159
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
160
|
+
# If there is a model, return that model's default scope (all records by default).
|
161
|
+
if (model = self.get_model(from_get_recordset: true))
|
162
|
+
return @recordset = model.all
|
163
|
+
end
|
152
164
|
|
153
|
-
|
154
|
-
def get_recordset
|
155
|
-
return _get_recordset
|
165
|
+
return nil
|
156
166
|
end
|
157
167
|
end
|
158
168
|
|
@@ -219,7 +229,7 @@ module RESTFramework::DestroyModelMixin
|
|
219
229
|
def destroy
|
220
230
|
@record = self.get_record
|
221
231
|
@record.destroy!
|
222
|
-
api_response(
|
232
|
+
api_response('')
|
223
233
|
end
|
224
234
|
end
|
225
235
|
|
@@ -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
|
@@ -14,10 +14,10 @@ end
|
|
14
14
|
class RESTFramework::ModelFilter < RESTFramework::BaseFilter
|
15
15
|
# Filter params for keys allowed by the current action's filterset_fields/fields config.
|
16
16
|
def _get_filter_params
|
17
|
-
fields = @controller.class.filterset_fields || @controller.send(:get_fields)
|
17
|
+
fields = @controller.class.filterset_fields&.map(&:to_s) || @controller.send(:get_fields)
|
18
18
|
return @controller.request.query_parameters.select { |p|
|
19
|
-
fields.include?(p
|
20
|
-
}.
|
19
|
+
fields.include?(p)
|
20
|
+
}.to_h.symbolize_keys # convert from HashWithIndifferentAccess to Hash w/keys
|
21
21
|
end
|
22
22
|
|
23
23
|
# Filter data according to the request query parameters.
|
@@ -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
|
@@ -1,6 +1,4 @@
|
|
1
1
|
class RESTFramework::BaseSerializer
|
2
|
-
attr_reader :errors
|
3
|
-
|
4
2
|
def initialize(object: nil, controller: nil, **kwargs)
|
5
3
|
@object = object
|
6
4
|
@controller = controller
|
@@ -8,9 +6,8 @@ class RESTFramework::BaseSerializer
|
|
8
6
|
end
|
9
7
|
|
10
8
|
|
11
|
-
# This serializer uses `.
|
12
|
-
#
|
13
|
-
# an array or a hash).
|
9
|
+
# This serializer uses `.serializable_hash` to convert objects to Ruby primitives (with the
|
10
|
+
# top-level being either an array or a hash).
|
14
11
|
class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
15
12
|
class_attribute :config
|
16
13
|
class_attribute :singular_config
|
@@ -21,12 +18,17 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
|
21
18
|
super(**kwargs)
|
22
19
|
|
23
20
|
if many.nil?
|
24
|
-
|
21
|
+
# Determine if we are dealing with many objects or just one.
|
22
|
+
@many = @object.is_a?(Enumerable)
|
25
23
|
else
|
26
24
|
@many = many
|
27
25
|
end
|
28
26
|
|
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
|
30
32
|
end
|
31
33
|
|
32
34
|
# Get controller action, if possible.
|
@@ -34,8 +36,8 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
|
34
36
|
return @controller&.action_name&.to_sym
|
35
37
|
end
|
36
38
|
|
37
|
-
# Get a locally defined configuration, if one is defined.
|
38
|
-
def
|
39
|
+
# Get a locally defined native serializer configuration, if one is defined.
|
40
|
+
def get_local_native_serializer_config
|
39
41
|
action = self.get_action
|
40
42
|
|
41
43
|
if action && self.action_config
|
@@ -45,43 +47,70 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
|
45
47
|
return self.action_config[action] if self.action_config[action]
|
46
48
|
end
|
47
49
|
|
48
|
-
# No action_config, so try singular/plural config.
|
49
|
-
return self.plural_config if @many && self.plural_config
|
50
|
-
return self.singular_config if
|
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
|
53
|
+
|
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
|
57
|
+
|
58
|
+
# Helper to get a native serializer configuration from the controller.
|
59
|
+
def get_controller_native_serializer_config
|
60
|
+
return nil unless @controller
|
61
|
+
|
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
|
51
67
|
|
52
|
-
|
53
|
-
return self.config
|
68
|
+
return controller_serializer || @controller.try(:native_serializer_config)
|
54
69
|
end
|
55
70
|
|
56
|
-
# Get a configuration passable to `
|
71
|
+
# Get a configuration passable to `serializable_hash` for the object.
|
57
72
|
def get_serializer_config
|
58
73
|
# Return a locally defined serializer config if one is defined.
|
59
|
-
if local_config = self.
|
74
|
+
if local_config = self.get_local_native_serializer_config
|
60
75
|
return local_config
|
61
76
|
end
|
62
77
|
|
63
|
-
# Return a serializer config if one is defined.
|
64
|
-
if serializer_config =
|
78
|
+
# Return a serializer config if one is defined on the controller.
|
79
|
+
if serializer_config = get_controller_native_serializer_config
|
65
80
|
return serializer_config
|
66
81
|
end
|
67
82
|
|
68
83
|
# If the config wasn't determined, build a serializer config from model fields.
|
69
|
-
fields = @controller.
|
70
|
-
|
71
|
-
|
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 = []
|
91
|
+
end
|
92
|
+
|
72
93
|
return {only: columns, methods: methods}
|
73
94
|
end
|
74
95
|
|
96
|
+
# By default, pass an empty configuration, allowing the serialization of all columns.
|
75
97
|
return {}
|
76
98
|
end
|
77
99
|
|
78
100
|
# Convert the object (record or recordset) to Ruby primitives.
|
79
101
|
def serialize
|
80
102
|
if @object
|
81
|
-
|
82
|
-
|
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
|
109
|
+
end
|
83
110
|
end
|
84
|
-
|
111
|
+
|
112
|
+
# Raise an error if we cannot serialize the object.
|
113
|
+
raise RESTFramework::UnserializableError.new(@object)
|
85
114
|
end
|
86
115
|
|
87
116
|
# Allow a serializer instance to be used as a hash directly in a nested serializer config.
|
@@ -113,6 +142,18 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
|
113
142
|
end
|
114
143
|
end
|
115
144
|
|
145
|
+
|
146
|
+
# :nocov:
|
116
147
|
# Alias NativeModelSerializer -> NativeSerializer.
|
117
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
|
+
)
|
157
|
+
end
|
118
158
|
end
|
159
|
+
# :nocov:
|
@@ -2,19 +2,21 @@ module RESTFramework
|
|
2
2
|
module Version
|
3
3
|
@_version = nil
|
4
4
|
|
5
|
-
def self.get_version
|
5
|
+
def self.get_version(skip_git: false)
|
6
6
|
# Return cached @_version, if available.
|
7
7
|
return @_version if @_version
|
8
8
|
|
9
9
|
# First, attempt to get the version from git.
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
10
|
+
unless skip_git
|
11
|
+
begin
|
12
|
+
version = `git describe 2>/dev/null`.strip
|
13
|
+
raise "blank version" if version.nil? || version.match(/^\w*$/)
|
14
|
+
# Check for local changes.
|
15
|
+
changes = `git status --porcelain 2>/dev/null`
|
16
|
+
version << '.localchanges' if changes.strip.length > 0
|
17
|
+
return version
|
18
|
+
rescue
|
19
|
+
end
|
18
20
|
end
|
19
21
|
|
20
22
|
# Git failed, so try to find a VERSION_STAMP.
|
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.
|
4
|
+
version: 0.2.0
|
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: 2021-03-
|
11
|
+
date: 2021-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -43,7 +43,10 @@ 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/errors.rb
|
46
47
|
- lib/rest_framework/filters.rb
|
48
|
+
- lib/rest_framework/generators.rb
|
49
|
+
- lib/rest_framework/generators/controller_generator.rb
|
47
50
|
- lib/rest_framework/paginators.rb
|
48
51
|
- lib/rest_framework/routers.rb
|
49
52
|
- lib/rest_framework/serializers.rb
|