rest_framework 0.2.1 → 0.3.2
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 +1 -0
- data/VERSION +1 -0
- data/lib/rest_framework/controller_mixins/base.rb +19 -15
- data/lib/rest_framework/controller_mixins/models.rb +62 -25
- data/lib/rest_framework/errors.rb +2 -0
- data/lib/rest_framework/filters.rb +32 -10
- data/lib/rest_framework/generators/controller_generator.rb +58 -17
- data/lib/rest_framework/paginators.rb +1 -0
- data/lib/rest_framework/routers.rb +29 -44
- data/lib/rest_framework/serializers.rb +7 -13
- data/lib/rest_framework/version.rb +18 -21
- metadata +4 -5
- data/app/views/rest_framework/default.html.erb +0 -0
- 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: cb9085823972b6991c739761eb1ec47ce012e9b0c40e0215199b1cc1f2e681c0
|
4
|
+
data.tar.gz: 7c211b54982077d586d35cd0c04401f1e7ec54cd5363234f5832651f605aee5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a92b41406fa3d2723fe29e53ff38c129453b3357e6d21f51fb79de797812a2d20ea0dd5936ace48834084d00887658daf7be63bbb65e98d7cb7eee21fbe2fd89
|
7
|
+
data.tar.gz: 652d9e6c68f1688202d7ee52617bb73491f358afffcd99960bda6edc3acf2a6dd2942e278bcfeb7e86876754f16d9b8911af716b61eb179f021188ecd61e9dc8
|
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
[](https://badge.fury.io/rb/rest_framework)
|
4
4
|
[](https://travis-ci.org/gregschmit/rails-rest-framework)
|
5
5
|
[](https://coveralls.io/github/gregschmit/rails-rest-framework?branch=master)
|
6
|
+
[](https://codeclimate.com/github/gregschmit/rails-rest-framework/maintainability)
|
6
7
|
|
7
8
|
A framework for DRY RESTful APIs in Ruby on Rails.
|
8
9
|
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.2
|
@@ -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.
|
@@ -124,7 +126,9 @@ module RESTFramework::BaseControllerMixin
|
|
124
126
|
begin
|
125
127
|
formatter = ActionDispatch::Routing::ConsoleFormatter::Sheet
|
126
128
|
rescue NameError
|
129
|
+
# :nocov:
|
127
130
|
formatter = ActionDispatch::Routing::ConsoleFormatter
|
131
|
+
# :nocov:
|
128
132
|
end
|
129
133
|
return ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes).format(
|
130
134
|
formatter.new
|
@@ -178,9 +182,9 @@ module RESTFramework::BaseControllerMixin
|
|
178
182
|
hkwargs = kwargs.merge(html_kwargs)
|
179
183
|
begin
|
180
184
|
render(**hkwargs)
|
181
|
-
rescue ActionView::MissingTemplate # fallback to rest_framework layout
|
185
|
+
rescue ActionView::MissingTemplate # fallback to rest_framework layout
|
182
186
|
hkwargs[:layout] = "rest_framework"
|
183
|
-
hkwargs[:
|
187
|
+
hkwargs[:html] = ''
|
184
188
|
render(**hkwargs)
|
185
189
|
end
|
186
190
|
}
|
@@ -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,
|
@@ -33,9 +37,13 @@ module RESTFramework::BaseModelControllerMixin
|
|
33
37
|
filterset_fields: nil,
|
34
38
|
ordering_fields: nil,
|
35
39
|
ordering_query_param: 'ordering',
|
40
|
+
search_fields: nil,
|
41
|
+
search_query_param: 'search',
|
42
|
+
search_case_sensitive: false,
|
36
43
|
|
37
44
|
# Other misc attributes.
|
38
|
-
|
45
|
+
create_from_recordset: true, # Option for `recordset.create` vs `Model.create` behavior.
|
46
|
+
filter_recordset_before_find: true, # Option to control if filtering is done before find.
|
39
47
|
}.each do |a, default|
|
40
48
|
unless base.respond_to?(a)
|
41
49
|
base.class_attribute(a)
|
@@ -60,11 +68,6 @@ module RESTFramework::BaseModelControllerMixin
|
|
60
68
|
return (action_config[action] if action) || self.class.send(generic_config_key)
|
61
69
|
end
|
62
70
|
|
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
71
|
# Get a list of fields for the current action.
|
69
72
|
def get_fields
|
70
73
|
return (
|
@@ -74,14 +77,37 @@ module RESTFramework::BaseModelControllerMixin
|
|
74
77
|
)
|
75
78
|
end
|
76
79
|
|
80
|
+
# Get a list of find_by fields for the current action.
|
81
|
+
def get_find_by_fields
|
82
|
+
return self.class.find_by_fields&.map(&:to_s) || self.get_fields
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get a list of find_by fields for the current action.
|
86
|
+
def get_filterset_fields
|
87
|
+
return self.class.filterset_fields&.map(&:to_s) || self.get_fields
|
88
|
+
end
|
89
|
+
|
90
|
+
# Get a list of ordering fields for the current action.
|
91
|
+
def get_ordering_fields
|
92
|
+
return self.class.ordering_fields&.map(&:to_s) || self.get_fields
|
93
|
+
end
|
94
|
+
|
95
|
+
# Get a list of search fields for the current action.
|
96
|
+
def get_search_fields
|
97
|
+
return self.class.search_fields&.map(&:to_s) || self.get_fields
|
98
|
+
end
|
99
|
+
|
100
|
+
# Get a list of parameters allowed for the current action.
|
101
|
+
def get_allowed_parameters
|
102
|
+
return _get_specific_action_config(:allowed_action_parameters, :allowed_parameters)&.map(&:to_s)
|
103
|
+
end
|
104
|
+
|
77
105
|
# Helper to get the configured serializer class, or `NativeSerializer` as a default.
|
78
|
-
# @return [RESTFramework::BaseSerializer]
|
79
106
|
def get_serializer_class
|
80
107
|
return self.class.serializer_class || RESTFramework::NativeSerializer
|
81
108
|
end
|
82
109
|
|
83
|
-
#
|
84
|
-
# @return [RESTFramework::BaseFilter]
|
110
|
+
# Helper to get filtering backends, defaulting to using `ModelFilter` and `ModelOrderingFilter`.
|
85
111
|
def get_filter_backends
|
86
112
|
return self.class.filter_backends || [
|
87
113
|
RESTFramework::ModelFilter, RESTFramework::ModelOrderingFilter
|
@@ -94,9 +120,7 @@ module RESTFramework::BaseModelControllerMixin
|
|
94
120
|
fields = self.get_allowed_parameters || self.get_fields
|
95
121
|
|
96
122
|
# Filter the request body.
|
97
|
-
body_params = request.request_parameters.select { |p|
|
98
|
-
fields.include?(p)
|
99
|
-
}
|
123
|
+
body_params = request.request_parameters.select { |p| fields.include?(p) }
|
100
124
|
|
101
125
|
# Add query params in place of missing body params, if configured.
|
102
126
|
if self.class.accept_generic_params_as_body_params
|
@@ -113,9 +137,7 @@ module RESTFramework::BaseModelControllerMixin
|
|
113
137
|
end
|
114
138
|
|
115
139
|
# Filter fields in exclude_body_fields.
|
116
|
-
(self.class.exclude_body_fields || []).each
|
117
|
-
body_params.delete(f.to_s)
|
118
|
-
end
|
140
|
+
(self.class.exclude_body_fields || []).each { |f| body_params.delete(f.to_s) }
|
119
141
|
|
120
142
|
body_params
|
121
143
|
end
|
@@ -123,14 +145,6 @@ module RESTFramework::BaseModelControllerMixin
|
|
123
145
|
alias :get_create_params :get_body_params
|
124
146
|
alias :get_update_params :get_body_params
|
125
147
|
|
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
148
|
# Get the model for this controller.
|
135
149
|
def get_model(from_get_recordset: false)
|
136
150
|
return @model if instance_variable_defined?(:@model) && @model
|
@@ -164,6 +178,29 @@ module RESTFramework::BaseModelControllerMixin
|
|
164
178
|
|
165
179
|
return nil
|
166
180
|
end
|
181
|
+
|
182
|
+
# Get a single record by primary key or another column, if allowed.
|
183
|
+
def get_record
|
184
|
+
recordset = self.get_recordset
|
185
|
+
find_by_fields = self.get_find_by_fields
|
186
|
+
find_by_key = self.get_model.primary_key
|
187
|
+
|
188
|
+
# Find by another column if it's permitted.
|
189
|
+
if find_by_query_param && params[find_by_query_param].in?(find_by_fields)
|
190
|
+
find_by_key = params[find_by_query_param]
|
191
|
+
end
|
192
|
+
|
193
|
+
# Filter recordset, if configured.
|
194
|
+
if self.filter_recordset_before_find
|
195
|
+
recordset = self.get_filtered_data(recordset)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Return the record.
|
199
|
+
if find_by_value = params[:id] # Route key is always :id by Rails convention.
|
200
|
+
return self.get_recordset.find_by!(find_by_key => find_by_value)
|
201
|
+
end
|
202
|
+
return nil
|
203
|
+
end
|
167
204
|
end
|
168
205
|
|
169
206
|
|
@@ -200,8 +237,8 @@ end
|
|
200
237
|
# Mixin for creating records.
|
201
238
|
module RESTFramework::CreateModelMixin
|
202
239
|
def create
|
203
|
-
if self.get_recordset.respond_to?(:create!) &&
|
204
|
-
# Create with any properties inherited from the recordset
|
240
|
+
if self.get_recordset.respond_to?(:create!) && self.create_from_recordset
|
241
|
+
# Create with any properties inherited from the recordset.
|
205
242
|
@record = self.get_recordset.create!(self.get_create_params)
|
206
243
|
else
|
207
244
|
# 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,8 +14,8 @@ 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.
|
18
|
-
return @controller.request.query_parameters.select { |p|
|
17
|
+
fields = @controller.send(:get_filterset_fields)
|
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
|
21
21
|
end
|
@@ -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
|
@@ -63,6 +71,20 @@ class RESTFramework::ModelOrderingFilter < RESTFramework::BaseFilter
|
|
63
71
|
end
|
64
72
|
|
65
73
|
|
66
|
-
#
|
67
|
-
|
68
|
-
#
|
74
|
+
# Multi-field text searching on models.
|
75
|
+
class RESTFramework::ModelSearchFilter < RESTFramework::BaseFilter
|
76
|
+
# Filter data according to the request query parameters.
|
77
|
+
def get_filtered_data(data)
|
78
|
+
fields = @controller.send(:get_search_fields)
|
79
|
+
search = @controller.request.query_parameters[@controller.send(:search_query_param)]
|
80
|
+
|
81
|
+
# Ensure we use array conditions to prevent SQL injection.
|
82
|
+
unless search.blank?
|
83
|
+
return data.where(fields.map { |f|
|
84
|
+
"CAST(#{f} AS text) #{@controller.send(:search_case_sensitive) ? "LIKE" : "ILIKE"} ?"
|
85
|
+
}.join(' OR '), *(["%#{search}%"] * fields.length))
|
86
|
+
end
|
87
|
+
|
88
|
+
return data
|
89
|
+
end
|
90
|
+
end
|
@@ -1,26 +1,67 @@
|
|
1
1
|
require 'rails/generators'
|
2
2
|
|
3
3
|
|
4
|
+
# Some projects don't have the inflection "REST" as an acronym, so this is a helper class to prevent
|
5
|
+
# this generator from being namespaced under `r_e_s_t_framework`.
|
6
|
+
# :nocov:
|
7
|
+
class RESTFrameworkCustomGeneratorControllerNamespace < String
|
8
|
+
def camelize
|
9
|
+
return "RESTFramework"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
# :nocov:
|
13
|
+
|
14
|
+
|
4
15
|
class RESTFramework::Generators::ControllerGenerator < Rails::Generators::Base
|
16
|
+
PATH_REGEX = /^\/*([a-z0-9_\/]*[a-z0-9_])(?:[\.a-z\/]*)$/
|
17
|
+
|
5
18
|
desc <<~END
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
`
|
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
|
19
|
+
Description:
|
20
|
+
Generates a new REST Framework controller.
|
21
|
+
|
22
|
+
Specify the controller as a path, including the module, if needed, like:
|
23
|
+
'parent_module/controller_name'.
|
24
|
+
|
25
|
+
Example:
|
26
|
+
`rails generate rest_framework:controller user_api/groups`
|
27
|
+
|
28
|
+
Generates a controller at `app/controllers/user_api/groups_controller.rb` named
|
29
|
+
`UserApi::GroupsController`.
|
21
30
|
END
|
22
31
|
|
23
|
-
|
24
|
-
|
32
|
+
argument :path, type: :string
|
33
|
+
class_option(
|
34
|
+
:parent_class,
|
35
|
+
type: :string,
|
36
|
+
default: 'ApplicationController',
|
37
|
+
desc: "Inheritance parent",
|
38
|
+
)
|
39
|
+
class_option(
|
40
|
+
:include_base,
|
41
|
+
type: :boolean,
|
42
|
+
default: false,
|
43
|
+
desc: "Include `BaseControllerMixin`, not `ModelControllerMixin`",
|
44
|
+
)
|
45
|
+
|
46
|
+
# Some projects may not have the inflection "REST" as an acronym, which changes this generator to
|
47
|
+
# be namespaced in `r_e_s_t_framework`, which is weird.
|
48
|
+
def self.namespace
|
49
|
+
return RESTFrameworkCustomGeneratorControllerNamespace.new("rest_framework:controller")
|
50
|
+
end
|
51
|
+
|
52
|
+
def create_rest_controller_file
|
53
|
+
unless (path_match = PATH_REGEX.match(self.path))
|
54
|
+
raise StandardError.new("Path isn't correct.")
|
55
|
+
end
|
56
|
+
|
57
|
+
cleaned_path = path_match[1]
|
58
|
+
content = <<~END
|
59
|
+
class #{cleaned_path.camelize}Controller < #{options[:parent_class]}
|
60
|
+
include RESTFramework::#{
|
61
|
+
options[:include_base] ? "BaseControllerMixin" : "ModelControllerMixin"
|
62
|
+
}
|
63
|
+
end
|
64
|
+
END
|
65
|
+
create_file("app/controllers/#{path}_controller.rb", content)
|
25
66
|
end
|
26
67
|
end
|
@@ -6,7 +6,7 @@ module ActionDispatch::Routing
|
|
6
6
|
# Internal helper to take extra_actions hash and convert to a consistent format.
|
7
7
|
protected def _parse_extra_actions(extra_actions)
|
8
8
|
return (extra_actions || {}).map do |k,v|
|
9
|
-
kwargs = {}
|
9
|
+
kwargs = {action: k}
|
10
10
|
path = k
|
11
11
|
|
12
12
|
# Convert structure to path/methods/kwargs.
|
@@ -25,11 +25,6 @@ module ActionDispatch::Routing
|
|
25
25
|
path = v.delete(:path)
|
26
26
|
end
|
27
27
|
|
28
|
-
# Set the action to be the action key unless it's already defined.
|
29
|
-
if !kwargs[:action]
|
30
|
-
kwargs[:action] = k
|
31
|
-
end
|
32
|
-
|
33
28
|
# Pass any further kwargs to the underlying Rails interface.
|
34
29
|
kwargs = kwargs.merge(v)
|
35
30
|
elsif v.is_a?(Symbol) || v.is_a?(String)
|
@@ -77,6 +72,16 @@ module ActionDispatch::Routing
|
|
77
72
|
return controller
|
78
73
|
end
|
79
74
|
|
75
|
+
# Interal interface for routing extra actions.
|
76
|
+
protected def _route_extra_actions(actions, &block)
|
77
|
+
actions.each do |action_config|
|
78
|
+
action_config[:methods].each do |m|
|
79
|
+
public_send(m, action_config[:path], **action_config[:kwargs])
|
80
|
+
end
|
81
|
+
yield if block_given?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
80
85
|
# Internal core implementation of the `rest_resource(s)` router, both singular and plural.
|
81
86
|
# @param default_singular [Boolean] the default plurality of the resource if the plurality is
|
82
87
|
# not otherwise defined by the controller
|
@@ -94,9 +99,11 @@ module ActionDispatch::Routing
|
|
94
99
|
kwargs[:controller] = name unless kwargs[:controller]
|
95
100
|
|
96
101
|
# determine plural/singular resource
|
97
|
-
|
102
|
+
force_singular = kwargs.delete(:force_singular)
|
103
|
+
force_plural = kwargs.delete(:force_plural)
|
104
|
+
if force_singular
|
98
105
|
singular = true
|
99
|
-
elsif
|
106
|
+
elsif force_plural
|
100
107
|
singular = false
|
101
108
|
elsif !controller_class.singleton_controller.nil?
|
102
109
|
singular = controller_class.singleton_controller
|
@@ -105,28 +112,20 @@ module ActionDispatch::Routing
|
|
105
112
|
end
|
106
113
|
resource_method = singular ? :resource : :resources
|
107
114
|
|
108
|
-
#
|
115
|
+
# Call either `resource` or `resources`, passing appropriate modifiers.
|
109
116
|
skip_undefined = kwargs.delete(:skip_undefined) || true
|
110
117
|
skip = controller_class.get_skip_actions(skip_undefined: skip_undefined)
|
111
118
|
public_send(resource_method, name, except: skip, **kwargs) do
|
112
119
|
if controller_class.respond_to?(:extra_member_actions)
|
113
120
|
member do
|
114
121
|
actions = self._parse_extra_actions(controller_class.extra_member_actions)
|
115
|
-
actions
|
116
|
-
action_config[:methods].each do |m|
|
117
|
-
public_send(m, action_config[:path], **action_config[:kwargs])
|
118
|
-
end
|
119
|
-
end
|
122
|
+
_route_extra_actions(actions)
|
120
123
|
end
|
121
124
|
end
|
122
125
|
|
123
126
|
collection do
|
124
127
|
actions = self._parse_extra_actions(controller_class.extra_actions)
|
125
|
-
actions
|
126
|
-
action_config[:methods].each do |m|
|
127
|
-
public_send(m, action_config[:path], **action_config[:kwargs])
|
128
|
-
end
|
129
|
-
end
|
128
|
+
_route_extra_actions(actions)
|
130
129
|
end
|
131
130
|
|
132
131
|
yield if block_given?
|
@@ -150,6 +149,7 @@ module ActionDispatch::Routing
|
|
150
149
|
# Route a controller without the default resourceful paths.
|
151
150
|
def rest_route(name=nil, **kwargs, &block)
|
152
151
|
controller = kwargs.delete(:controller) || name
|
152
|
+
route_root_to = kwargs.delete(:route_root_to)
|
153
153
|
if controller.is_a?(Class)
|
154
154
|
controller_class = controller
|
155
155
|
else
|
@@ -162,42 +162,27 @@ module ActionDispatch::Routing
|
|
162
162
|
# Route actions using the resourceful router, but skip all builtin actions.
|
163
163
|
actions = self._parse_extra_actions(controller_class.extra_actions)
|
164
164
|
public_send(:resource, name, only: [], **kwargs) do
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
end
|
169
|
-
yield if block_given?
|
165
|
+
# Route a root for this resource.
|
166
|
+
if route_root_to
|
167
|
+
get '', action: route_root_to
|
170
168
|
end
|
169
|
+
|
170
|
+
_route_extra_actions(actions, &block)
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|
174
174
|
# Route a controller's `#root` to '/' in the current scope/namespace, along with other actions.
|
175
|
-
# @param name [Symbol] the snake_case name of the controller
|
176
175
|
def rest_root(name=nil, **kwargs, &block)
|
177
176
|
# By default, use RootController#root.
|
178
177
|
root_action = kwargs.delete(:action) || :root
|
179
178
|
controller = kwargs.delete(:controller) || name || :root
|
180
179
|
|
181
|
-
#
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
controller_class = self._get_controller_class(controller, pluralize: false)
|
186
|
-
actions = self._parse_extra_actions(controller_class.extra_actions)
|
187
|
-
actions.each do |action_config|
|
188
|
-
# Add :action unless kwargs defines it.
|
189
|
-
unless action_config[:kwargs].key?(:action)
|
190
|
-
action_config[:kwargs][:action] = action_config[:path]
|
191
|
-
end
|
180
|
+
# Remove path if name is nil (routing to the root of current namespace).
|
181
|
+
unless name
|
182
|
+
kwargs[:path] = ''
|
183
|
+
end
|
192
184
|
|
193
|
-
|
194
|
-
public_send(
|
195
|
-
m,
|
196
|
-
File.join(name.to_s, action_config[:path].to_s),
|
197
|
-
controller: controller,
|
198
|
-
**action_config[:kwargs],
|
199
|
-
)
|
200
|
-
end
|
185
|
+
return rest_route(controller, route_root_to: root_action, **kwargs) do
|
201
186
|
yield if block_given?
|
202
187
|
end
|
203
188
|
end
|
@@ -31,7 +31,9 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
|
31
31
|
# Determine model either explicitly, or by inspecting @object or @controller.
|
32
32
|
@model = model
|
33
33
|
@model ||= @object.class if @object.is_a?(ActiveRecord::Base)
|
34
|
-
@model ||= @object[0].class if
|
34
|
+
@model ||= @object[0].class if (
|
35
|
+
@many && @object.is_a?(Enumerable) && @object.is_a?(ActiveRecord::Base)
|
36
|
+
)
|
35
37
|
@model ||= @controller.send(:get_model) if @controller
|
36
38
|
end
|
37
39
|
|
@@ -119,29 +121,21 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
|
119
121
|
|
120
122
|
# Allow a serializer instance to be used as a hash directly in a nested serializer config.
|
121
123
|
def [](key)
|
122
|
-
|
123
|
-
@_nested_config = self.get_serializer_config
|
124
|
-
end
|
124
|
+
@_nested_config ||= self.get_serializer_config
|
125
125
|
return @_nested_config[key]
|
126
126
|
end
|
127
127
|
def []=(key, value)
|
128
|
-
|
129
|
-
@_nested_config = self.get_serializer_config
|
130
|
-
end
|
128
|
+
@_nested_config ||= self.get_serializer_config
|
131
129
|
return @_nested_config[key] = value
|
132
130
|
end
|
133
131
|
|
134
132
|
# Allow a serializer class to be used as a hash directly in a nested serializer config.
|
135
133
|
def self.[](key)
|
136
|
-
|
137
|
-
@_nested_config = self.new.get_serializer_config
|
138
|
-
end
|
134
|
+
@_nested_config ||= self.new.get_serializer_config
|
139
135
|
return @_nested_config[key]
|
140
136
|
end
|
141
137
|
def self.[]=(key, value)
|
142
|
-
|
143
|
-
@_nested_config = self.new.get_serializer_config
|
144
|
-
end
|
138
|
+
@_nested_config ||= self.new.get_serializer_config
|
145
139
|
return @_nested_config[key] = value
|
146
140
|
end
|
147
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.2
|
4
|
+
version: 0.3.2
|
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-05-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -33,12 +33,11 @@ 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
|
-
- app/views/rest_framework/default.html.erb
|
40
40
|
- lib/rest_framework.rb
|
41
|
-
- lib/rest_framework/VERSION_STAMP
|
42
41
|
- lib/rest_framework/controller_mixins.rb
|
43
42
|
- lib/rest_framework/controller_mixins/base.rb
|
44
43
|
- lib/rest_framework/controller_mixins/models.rb
|
@@ -73,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
72
|
- !ruby/object:Gem::Version
|
74
73
|
version: '0'
|
75
74
|
requirements: []
|
76
|
-
rubygems_version: 3.
|
75
|
+
rubygems_version: 3.1.4
|
77
76
|
signing_key:
|
78
77
|
specification_version: 4
|
79
78
|
summary: A framework for DRY RESTful APIs in Ruby on Rails.
|
File without changes
|
@@ -1 +0,0 @@
|
|
1
|
-
0.2.1
|