rest_framework 0.2.4 → 0.3.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/VERSION +1 -0
- data/lib/rest_framework/controller_mixins/base.rb +15 -13
- data/lib/rest_framework/controller_mixins/models.rb +54 -25
- data/lib/rest_framework/errors.rb +2 -0
- data/lib/rest_framework/filters.rb +14 -6
- data/lib/rest_framework/routers.rb +4 -2
- data/lib/rest_framework/serializers.rb +4 -12
- data/lib/rest_framework/version.rb +18 -21
- metadata +3 -3
- data/lib/rest_framework/VERSION_STAMP +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a4c53fa557df9090c749745efce556214252b76e2115df09917970e9a9c8b5e
|
4
|
+
data.tar.gz: 5c33946ae697618f726532e9b45056f2e112d3f81f6efb5c9a37ab8d9b3b002e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ddfd6544b96284e871684d2f448e622631bd88631ea78ebe4170d2ff2fe4f1654901ef86a96584e3c5d0a2538bcef5d29f85152795aa689df58fc98cb05f7b3b
|
7
|
+
data.tar.gz: 1ab60515fcf543c0df574a6cf3913648736d19d559b1d9fbe55f891ce3e6926026357369580430ada33b6e294a081d8058b297bb3f146b3d17f4671b10683d55
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.0
|
@@ -79,8 +79,12 @@ module RESTFramework::BaseControllerMixin
|
|
79
79
|
|
80
80
|
protected
|
81
81
|
|
82
|
-
# Helper to get
|
83
|
-
|
82
|
+
# Helper to get the configured serializer class.
|
83
|
+
def get_serializer_class
|
84
|
+
return self.class.serializer_class
|
85
|
+
end
|
86
|
+
|
87
|
+
# Helper to get filtering backends, defaulting to no backends.
|
84
88
|
def get_filter_backends
|
85
89
|
return self.class.filter_backends || []
|
86
90
|
end
|
@@ -95,16 +99,10 @@ module RESTFramework::BaseControllerMixin
|
|
95
99
|
return data
|
96
100
|
end
|
97
101
|
|
98
|
-
# Helper to get the configured serializer class.
|
99
|
-
# @return [RESTFramework::BaseSerializer]
|
100
|
-
def get_serializer_class
|
101
|
-
return self.class.serializer_class
|
102
|
-
end
|
103
|
-
|
104
102
|
def record_invalid(e)
|
105
|
-
return api_response(
|
106
|
-
|
107
|
-
)
|
103
|
+
return api_response({
|
104
|
+
message: "Record invalid.", exception: e, errors: e.record&.errors
|
105
|
+
}, status: 400)
|
108
106
|
end
|
109
107
|
|
110
108
|
def record_not_found(e)
|
@@ -112,11 +110,15 @@ module RESTFramework::BaseControllerMixin
|
|
112
110
|
end
|
113
111
|
|
114
112
|
def record_not_saved(e)
|
115
|
-
return api_response({
|
113
|
+
return api_response({
|
114
|
+
message: "Record not saved.", exception: e, errors: e.record&.errors
|
115
|
+
}, status: 406)
|
116
116
|
end
|
117
117
|
|
118
118
|
def record_not_destroyed(e)
|
119
|
-
return api_response({
|
119
|
+
return api_response({
|
120
|
+
message: "Record not destroyed.", exception: e, errors: e.record&.errors
|
121
|
+
}, status: 406)
|
120
122
|
end
|
121
123
|
|
122
124
|
# Helper for showing routes under a controller action, used for the browsable API.
|
@@ -20,6 +20,10 @@ module RESTFramework::BaseModelControllerMixin
|
|
20
20
|
fields: nil,
|
21
21
|
action_fields: nil,
|
22
22
|
|
23
|
+
# Attributes for finding records.
|
24
|
+
find_by_fields: nil,
|
25
|
+
find_by_query_param: 'find_by',
|
26
|
+
|
23
27
|
# Attributes for create/update parameters.
|
24
28
|
allowed_parameters: nil,
|
25
29
|
allowed_action_parameters: nil,
|
@@ -35,7 +39,8 @@ module RESTFramework::BaseModelControllerMixin
|
|
35
39
|
ordering_query_param: 'ordering',
|
36
40
|
|
37
41
|
# Other misc attributes.
|
38
|
-
|
42
|
+
create_from_recordset: true, # Option for `recordset.create` vs `Model.create` behavior.
|
43
|
+
filter_recordset_before_find: true, # Option to control if filtering is done before find.
|
39
44
|
}.each do |a, default|
|
40
45
|
unless base.respond_to?(a)
|
41
46
|
base.class_attribute(a)
|
@@ -60,11 +65,6 @@ module RESTFramework::BaseModelControllerMixin
|
|
60
65
|
return (action_config[action] if action) || self.class.send(generic_config_key)
|
61
66
|
end
|
62
67
|
|
63
|
-
# Get a list of parameters allowed for the current action.
|
64
|
-
def get_allowed_parameters
|
65
|
-
return _get_specific_action_config(:allowed_action_parameters, :allowed_parameters)&.map(&:to_s)
|
66
|
-
end
|
67
|
-
|
68
68
|
# Get a list of fields for the current action.
|
69
69
|
def get_fields
|
70
70
|
return (
|
@@ -74,14 +74,32 @@ module RESTFramework::BaseModelControllerMixin
|
|
74
74
|
)
|
75
75
|
end
|
76
76
|
|
77
|
+
# Get a list of find_by fields for the current action.
|
78
|
+
def get_find_by_fields
|
79
|
+
return self.class.find_by_fields&.map(&:to_s) || self.get_fields
|
80
|
+
end
|
81
|
+
|
82
|
+
# Get a list of find_by fields for the current action.
|
83
|
+
def get_filterset_fields
|
84
|
+
return self.class.filterset_fields&.map(&:to_s) || self.get_fields
|
85
|
+
end
|
86
|
+
|
87
|
+
# Get a list of ordering fields for the current action.
|
88
|
+
def get_ordering_fields
|
89
|
+
return self.class.ordering_fields&.map(&:to_s) || self.get_fields
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get a list of parameters allowed for the current action.
|
93
|
+
def get_allowed_parameters
|
94
|
+
return _get_specific_action_config(:allowed_action_parameters, :allowed_parameters)&.map(&:to_s)
|
95
|
+
end
|
96
|
+
|
77
97
|
# Helper to get the configured serializer class, or `NativeSerializer` as a default.
|
78
|
-
# @return [RESTFramework::BaseSerializer]
|
79
98
|
def get_serializer_class
|
80
99
|
return self.class.serializer_class || RESTFramework::NativeSerializer
|
81
100
|
end
|
82
101
|
|
83
|
-
#
|
84
|
-
# @return [RESTFramework::BaseFilter]
|
102
|
+
# Helper to get filtering backends, defaulting to using `ModelFilter` and `ModelOrderingFilter`.
|
85
103
|
def get_filter_backends
|
86
104
|
return self.class.filter_backends || [
|
87
105
|
RESTFramework::ModelFilter, RESTFramework::ModelOrderingFilter
|
@@ -94,9 +112,7 @@ module RESTFramework::BaseModelControllerMixin
|
|
94
112
|
fields = self.get_allowed_parameters || self.get_fields
|
95
113
|
|
96
114
|
# Filter the request body.
|
97
|
-
body_params = request.request_parameters.select { |p|
|
98
|
-
fields.include?(p)
|
99
|
-
}
|
115
|
+
body_params = request.request_parameters.select { |p| fields.include?(p) }
|
100
116
|
|
101
117
|
# Add query params in place of missing body params, if configured.
|
102
118
|
if self.class.accept_generic_params_as_body_params
|
@@ -113,9 +129,7 @@ module RESTFramework::BaseModelControllerMixin
|
|
113
129
|
end
|
114
130
|
|
115
131
|
# Filter fields in exclude_body_fields.
|
116
|
-
(self.class.exclude_body_fields || []).each
|
117
|
-
body_params.delete(f.to_s)
|
118
|
-
end
|
132
|
+
(self.class.exclude_body_fields || []).each { |f| body_params.delete(f.to_s) }
|
119
133
|
|
120
134
|
body_params
|
121
135
|
end
|
@@ -123,14 +137,6 @@ module RESTFramework::BaseModelControllerMixin
|
|
123
137
|
alias :get_create_params :get_body_params
|
124
138
|
alias :get_update_params :get_body_params
|
125
139
|
|
126
|
-
# Get a record by the primary key from the (non-filtered) recordset.
|
127
|
-
def get_record
|
128
|
-
if pk = params[self.get_model.primary_key]
|
129
|
-
return self.get_recordset.find(pk)
|
130
|
-
end
|
131
|
-
return nil
|
132
|
-
end
|
133
|
-
|
134
140
|
# Get the model for this controller.
|
135
141
|
def get_model(from_get_recordset: false)
|
136
142
|
return @model if instance_variable_defined?(:@model) && @model
|
@@ -164,6 +170,29 @@ module RESTFramework::BaseModelControllerMixin
|
|
164
170
|
|
165
171
|
return nil
|
166
172
|
end
|
173
|
+
|
174
|
+
# Get a single record by primary key or another column, if allowed.
|
175
|
+
def get_record
|
176
|
+
recordset = self.get_recordset
|
177
|
+
find_by_fields = self.get_find_by_fields
|
178
|
+
find_by_key = self.get_model.primary_key
|
179
|
+
|
180
|
+
# Find by another column if it's permitted.
|
181
|
+
if find_by_query_param && params[find_by_query_param].in?(find_by_fields)
|
182
|
+
find_by_key = params[find_by_query_param]
|
183
|
+
end
|
184
|
+
|
185
|
+
# Filter recordset, if configured.
|
186
|
+
if self.filter_recordset_before_find
|
187
|
+
recordset = self.get_filtered_data(recordset)
|
188
|
+
end
|
189
|
+
|
190
|
+
# Return the record.
|
191
|
+
if find_by_value = params[:id] # Route key is always :id by Rails convention.
|
192
|
+
return self.get_recordset.find_by!(find_by_key => find_by_value)
|
193
|
+
end
|
194
|
+
return nil
|
195
|
+
end
|
167
196
|
end
|
168
197
|
|
169
198
|
|
@@ -200,8 +229,8 @@ end
|
|
200
229
|
# Mixin for creating records.
|
201
230
|
module RESTFramework::CreateModelMixin
|
202
231
|
def create
|
203
|
-
if self.get_recordset.respond_to?(:create!) &&
|
204
|
-
# Create with any properties inherited from the recordset
|
232
|
+
if self.get_recordset.respond_to?(:create!) && self.create_from_recordset
|
233
|
+
# Create with any properties inherited from the recordset.
|
205
234
|
@record = self.get_recordset.create!(self.get_create_params)
|
206
235
|
else
|
207
236
|
# Otherwise, perform a "bare" create.
|
@@ -2,6 +2,7 @@
|
|
2
2
|
class RESTFramework::Error < StandardError
|
3
3
|
end
|
4
4
|
|
5
|
+
|
5
6
|
class RESTFramework::NilPassedToAPIResponseError < RESTFramework::Error
|
6
7
|
def message
|
7
8
|
return <<~MSG.split("\n").join(' ')
|
@@ -14,6 +15,7 @@ class RESTFramework::NilPassedToAPIResponseError < RESTFramework::Error
|
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
18
|
+
|
17
19
|
class RESTFramework::UnserializableError < RESTFramework::Error
|
18
20
|
def initialize(object)
|
19
21
|
@object = object
|
@@ -14,7 +14,7 @@ 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.
|
17
|
+
fields = @controller.send(:get_filterset_fields)
|
18
18
|
return @controller.request.query_parameters.select { |p|
|
19
19
|
fields.include?(p)
|
20
20
|
}.to_h.symbolize_keys # convert from HashWithIndifferentAccess to Hash w/keys
|
@@ -36,17 +36,25 @@ end
|
|
36
36
|
class RESTFramework::ModelOrderingFilter < RESTFramework::BaseFilter
|
37
37
|
# Convert ordering string to an ordering configuration.
|
38
38
|
def _get_ordering
|
39
|
-
return nil
|
39
|
+
return nil if @controller.class.ordering_query_param.blank?
|
40
|
+
ordering_fields = @controller.send(:get_ordering_fields)
|
40
41
|
|
41
42
|
order_string = @controller.params[@controller.class.ordering_query_param]
|
42
43
|
unless order_string.blank?
|
43
|
-
|
44
|
+
ordering = {}
|
45
|
+
order_string.split(',').each do |field|
|
44
46
|
if field[0] == '-'
|
45
|
-
|
47
|
+
column = field[1..-1]
|
48
|
+
direction = :desc
|
46
49
|
else
|
47
|
-
|
50
|
+
column = field
|
51
|
+
direction = :asc
|
48
52
|
end
|
49
|
-
|
53
|
+
if column.in?(ordering_fields)
|
54
|
+
ordering[column.to_sym] = direction
|
55
|
+
end
|
56
|
+
end
|
57
|
+
return ordering
|
50
58
|
end
|
51
59
|
|
52
60
|
return nil
|
@@ -99,9 +99,11 @@ module ActionDispatch::Routing
|
|
99
99
|
kwargs[:controller] = name unless kwargs[:controller]
|
100
100
|
|
101
101
|
# determine plural/singular resource
|
102
|
-
|
102
|
+
force_singular = kwargs.delete(:force_singular)
|
103
|
+
force_plural = kwargs.delete(:force_plural)
|
104
|
+
if force_singular
|
103
105
|
singular = true
|
104
|
-
elsif
|
106
|
+
elsif force_plural
|
105
107
|
singular = false
|
106
108
|
elsif !controller_class.singleton_controller.nil?
|
107
109
|
singular = controller_class.singleton_controller
|
@@ -121,29 +121,21 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
|
121
121
|
|
122
122
|
# Allow a serializer instance to be used as a hash directly in a nested serializer config.
|
123
123
|
def [](key)
|
124
|
-
|
125
|
-
@_nested_config = self.get_serializer_config
|
126
|
-
end
|
124
|
+
@_nested_config ||= self.get_serializer_config
|
127
125
|
return @_nested_config[key]
|
128
126
|
end
|
129
127
|
def []=(key, value)
|
130
|
-
|
131
|
-
@_nested_config = self.get_serializer_config
|
132
|
-
end
|
128
|
+
@_nested_config ||= self.get_serializer_config
|
133
129
|
return @_nested_config[key] = value
|
134
130
|
end
|
135
131
|
|
136
132
|
# Allow a serializer class to be used as a hash directly in a nested serializer config.
|
137
133
|
def self.[](key)
|
138
|
-
|
139
|
-
@_nested_config = self.new.get_serializer_config
|
140
|
-
end
|
134
|
+
@_nested_config ||= self.new.get_serializer_config
|
141
135
|
return @_nested_config[key]
|
142
136
|
end
|
143
137
|
def self.[]=(key, value)
|
144
|
-
|
145
|
-
@_nested_config = self.new.get_serializer_config
|
146
|
-
end
|
138
|
+
@_nested_config ||= self.new.get_serializer_config
|
147
139
|
return @_nested_config[key] = value
|
148
140
|
end
|
149
141
|
end
|
@@ -1,35 +1,32 @@
|
|
1
|
+
# Do not use Rails-specific helper methods here (e.g., `blank?`) so the module can run standalone.
|
1
2
|
module RESTFramework
|
2
3
|
module Version
|
3
|
-
|
4
|
+
VERSION_FILEPATH = File.expand_path("../../VERSION", __dir__)
|
4
5
|
|
5
6
|
def self.get_version(skip_git: false)
|
6
|
-
# Return cached @_version, if available.
|
7
|
-
return @_version if @_version
|
8
|
-
|
9
7
|
# First, attempt to get the version from git.
|
10
8
|
unless skip_git
|
11
|
-
|
12
|
-
|
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
|
9
|
+
version = `git describe --dirty --broken 2>/dev/null`&.strip
|
10
|
+
return version unless !version || version.empty?
|
20
11
|
end
|
21
12
|
|
22
|
-
# Git failed, so try to find a
|
13
|
+
# Git failed or was skipped, so try to find a VERSION file.
|
23
14
|
begin
|
24
|
-
version = File.read(
|
25
|
-
unless version
|
26
|
-
|
27
|
-
end
|
28
|
-
rescue
|
15
|
+
version = File.read(VERSION_FILEPATH)&.strip
|
16
|
+
return version unless !version || version.blank?
|
17
|
+
rescue SystemCallError
|
29
18
|
end
|
30
19
|
|
31
|
-
# No
|
32
|
-
return '
|
20
|
+
# No VERSION file, so version is unknown.
|
21
|
+
return 'unknown'
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.stamp_version
|
25
|
+
File.write(VERSION_FILEPATH, RESTFramework::VERSION)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.unstamp_version
|
29
|
+
File.delete(VERSION_FILEPATH) if File.exist?(VERSION_FILEPATH)
|
33
30
|
end
|
34
31
|
end
|
35
32
|
|
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.3.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-
|
11
|
+
date: 2021-04-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -33,12 +33,12 @@ extra_rdoc_files: []
|
|
33
33
|
files:
|
34
34
|
- LICENSE
|
35
35
|
- README.md
|
36
|
+
- VERSION
|
36
37
|
- app/views/layouts/rest_framework.html.erb
|
37
38
|
- app/views/rest_framework/_head.html.erb
|
38
39
|
- app/views/rest_framework/_routes.html.erb
|
39
40
|
- app/views/rest_framework/default.html.erb
|
40
41
|
- lib/rest_framework.rb
|
41
|
-
- lib/rest_framework/VERSION_STAMP
|
42
42
|
- lib/rest_framework/controller_mixins.rb
|
43
43
|
- lib/rest_framework/controller_mixins/base.rb
|
44
44
|
- lib/rest_framework/controller_mixins/models.rb
|
@@ -1 +0,0 @@
|
|
1
|
-
0.2.4
|