rest_framework 0.6.13 → 0.7.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 -1
- data/app/views/layouts/rest_framework.html.erb +3 -0
- data/lib/rest_framework/controller_mixins/base.rb +84 -36
- data/lib/rest_framework/controller_mixins/models.rb +94 -77
- data/lib/rest_framework/routers.rb +7 -11
- data/lib/rest_framework/utils.rb +40 -11
- data/lib/rest_framework.rb +25 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bcc4c1783a1d0dc42b862d9daf32c6d9ef2ae029483f61ac7c2e971c6c0558db
|
4
|
+
data.tar.gz: '091682390838dd5f5c5db8ea2e216f336863f06b77488c88dbc68b5c59b68e22'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 441f74ad60ade750e7483364b9e5a73fb58cb9452dc8a5e11988e154758fba345391b44bf87143a6c3e1f9ff1ec6fb773c51af30990776fedeac7c5d96461676
|
7
|
+
data.tar.gz: 0acb74c34c862ba9fd74e9b76ea4c1b9b413aac4989250aab3ee0b6a1cf0a1643f8095ad552d4f8e2c53cc2b2bbdf60c4efbd87d8a48605cab6d051c0082da2d
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
@@ -57,6 +57,9 @@
|
|
57
57
|
<% end %>
|
58
58
|
<button type="button" class="btn btn-primary" onclick="rrfRefresh(this)">GET</button>
|
59
59
|
</div>
|
60
|
+
<% if @description.present? %>
|
61
|
+
<br><br><p style="display: inline-block; margin-bottom: 0"><%= @description %></p>
|
62
|
+
<% end %>
|
60
63
|
</div>
|
61
64
|
</div>
|
62
65
|
<hr/>
|
@@ -6,12 +6,59 @@ require_relative "../utils"
|
|
6
6
|
# the ability to route arbitrary actions with `extra_actions`. This is also where `api_response`
|
7
7
|
# is defined.
|
8
8
|
module RESTFramework::BaseControllerMixin
|
9
|
+
RRF_BASE_CONTROLLER_CONFIG = {
|
10
|
+
filter_pk_from_request_body: true,
|
11
|
+
exclude_body_fields: [:created_at, :created_by, :updated_at, :updated_by],
|
12
|
+
accept_generic_params_as_body_params: false,
|
13
|
+
show_backtrace: false,
|
14
|
+
extra_actions: nil,
|
15
|
+
extra_member_actions: nil,
|
16
|
+
filter_backends: nil,
|
17
|
+
singleton_controller: nil,
|
18
|
+
|
19
|
+
# Metadata and display options.
|
20
|
+
title: nil,
|
21
|
+
description: nil,
|
22
|
+
inflect_acronyms: ["ID", "REST", "API"],
|
23
|
+
|
24
|
+
# Options related to serialization.
|
25
|
+
rescue_unknown_format_with: :json,
|
26
|
+
serializer_class: nil,
|
27
|
+
serialize_to_json: true,
|
28
|
+
serialize_to_xml: true,
|
29
|
+
|
30
|
+
# Options related to pagination.
|
31
|
+
paginator_class: nil,
|
32
|
+
page_size: 20,
|
33
|
+
page_query_param: "page",
|
34
|
+
page_size_query_param: "page_size",
|
35
|
+
max_page_size: nil,
|
36
|
+
|
37
|
+
# Option to disable serializer adapters by default, mainly introduced because Active Model
|
38
|
+
# Serializers will do things like serialize `[]` into `{"":[]}`.
|
39
|
+
disable_adapters_by_default: true,
|
40
|
+
}
|
41
|
+
|
9
42
|
# Default action for API root.
|
10
43
|
def root
|
11
|
-
api_response({message: "This is the root
|
44
|
+
api_response({message: "This is the API root."})
|
12
45
|
end
|
13
46
|
|
14
47
|
module ClassMethods
|
48
|
+
# Get the title of this controller. By default, this is the name of the controller class,
|
49
|
+
# titleized and with any custom inflection acronyms applied.
|
50
|
+
def get_title
|
51
|
+
return self.title || RESTFramework::Utils.inflect(
|
52
|
+
self.name.demodulize.chomp("Controller").titleize(keep_id_suffix: true),
|
53
|
+
self.inflect_acronyms,
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Get a label from a field/column name, titleized and inflected.
|
58
|
+
def get_label(s)
|
59
|
+
return RESTFramework::Utils.inflect(s.titleize(keep_id_suffix: true), self.inflect_acronyms)
|
60
|
+
end
|
61
|
+
|
15
62
|
# Collect actions (including extra actions) metadata for this controller.
|
16
63
|
def get_actions_metadata
|
17
64
|
actions = {}
|
@@ -20,12 +67,14 @@ module RESTFramework::BaseControllerMixin
|
|
20
67
|
RESTFramework::BUILTIN_ACTIONS.merge(
|
21
68
|
RESTFramework::RRF_BUILTIN_ACTIONS,
|
22
69
|
).each do |action, methods|
|
23
|
-
|
70
|
+
if self.method_defined?(action)
|
71
|
+
actions[action] = {path: "", methods: methods, type: :builtin}
|
72
|
+
end
|
24
73
|
end
|
25
74
|
|
26
75
|
# Add extra actions.
|
27
76
|
if extra_actions = self.try(:extra_actions)
|
28
|
-
actions.merge!(RESTFramework::Utils.parse_extra_actions(extra_actions))
|
77
|
+
actions.merge!(RESTFramework::Utils.parse_extra_actions(extra_actions, controller: self))
|
29
78
|
end
|
30
79
|
|
31
80
|
return actions
|
@@ -37,12 +86,14 @@ module RESTFramework::BaseControllerMixin
|
|
37
86
|
|
38
87
|
# Start with builtin actions.
|
39
88
|
RESTFramework::BUILTIN_MEMBER_ACTIONS.each do |action, methods|
|
40
|
-
|
89
|
+
if self.method_defined?(action)
|
90
|
+
actions[action] = {path: "", methods: methods, type: :builtin}
|
91
|
+
end
|
41
92
|
end
|
42
93
|
|
43
94
|
# Add extra actions.
|
44
95
|
if extra_actions = self.try(:extra_member_actions)
|
45
|
-
actions.merge!(RESTFramework::Utils.parse_extra_actions(extra_actions))
|
96
|
+
actions.merge!(RESTFramework::Utils.parse_extra_actions(extra_actions, controller: self))
|
46
97
|
end
|
47
98
|
|
48
99
|
return actions
|
@@ -51,8 +102,8 @@ module RESTFramework::BaseControllerMixin
|
|
51
102
|
# Get a hash of metadata to be rendered in the `OPTIONS` response. Cache the result.
|
52
103
|
def get_options_metadata
|
53
104
|
return @_base_options_metadata ||= {
|
54
|
-
|
55
|
-
description: self.
|
105
|
+
title: self.get_title,
|
106
|
+
description: self.description,
|
56
107
|
renders: [
|
57
108
|
"text/html",
|
58
109
|
self.serialize_to_json ? "application/json" : nil,
|
@@ -62,6 +113,16 @@ module RESTFramework::BaseControllerMixin
|
|
62
113
|
member_actions: self.get_member_actions_metadata,
|
63
114
|
}.compact
|
64
115
|
end
|
116
|
+
|
117
|
+
# Define any behavior to execute at the end of controller definition.
|
118
|
+
def rrf_finalize
|
119
|
+
if RESTFramework.config.freeze_config
|
120
|
+
self::RRF_BASE_CONTROLLER_CONFIG.keys.each { |k|
|
121
|
+
v = self.send(k)
|
122
|
+
v.freeze if v.is_a?(Hash) || v.is_a?(Array)
|
123
|
+
}
|
124
|
+
end
|
125
|
+
end
|
65
126
|
end
|
66
127
|
|
67
128
|
def self.included(base)
|
@@ -70,34 +131,7 @@ module RESTFramework::BaseControllerMixin
|
|
70
131
|
base.extend(ClassMethods)
|
71
132
|
|
72
133
|
# Add class attributes (with defaults) unless they already exist.
|
73
|
-
|
74
|
-
filter_pk_from_request_body: true,
|
75
|
-
exclude_body_fields: [:created_at, :created_by, :updated_at, :updated_by],
|
76
|
-
accept_generic_params_as_body_params: false,
|
77
|
-
show_backtrace: false,
|
78
|
-
extra_actions: nil,
|
79
|
-
extra_member_actions: nil,
|
80
|
-
filter_backends: nil,
|
81
|
-
singleton_controller: nil,
|
82
|
-
metadata: nil,
|
83
|
-
|
84
|
-
# Options related to serialization.
|
85
|
-
rescue_unknown_format_with: :json,
|
86
|
-
serializer_class: nil,
|
87
|
-
serialize_to_json: true,
|
88
|
-
serialize_to_xml: true,
|
89
|
-
|
90
|
-
# Options related to pagination.
|
91
|
-
paginator_class: nil,
|
92
|
-
page_size: 20,
|
93
|
-
page_query_param: "page",
|
94
|
-
page_size_query_param: "page_size",
|
95
|
-
max_page_size: nil,
|
96
|
-
|
97
|
-
# Option to disable serializer adapters by default, mainly introduced because Active Model
|
98
|
-
# Serializers will do things like serialize `[]` into `{"":[]}`.
|
99
|
-
disable_adapters_by_default: true,
|
100
|
-
}.each do |a, default|
|
134
|
+
RRF_BASE_CONTROLLER_CONFIG.each do |a, default|
|
101
135
|
next if base.respond_to?(a)
|
102
136
|
|
103
137
|
base.class_attribute(a)
|
@@ -126,6 +160,19 @@ module RESTFramework::BaseControllerMixin
|
|
126
160
|
base.rescue_from(ActiveRecord::RecordNotSaved, with: :record_not_saved)
|
127
161
|
base.rescue_from(ActiveRecord::RecordNotDestroyed, with: :record_not_destroyed)
|
128
162
|
base.rescue_from(ActiveModel::UnknownAttributeError, with: :unknown_attribute_error)
|
163
|
+
|
164
|
+
# Use `TracePoint` hook to automatically call `rrf_finalize`.
|
165
|
+
unless RESTFramework.config.disable_auto_finalize
|
166
|
+
TracePoint.trace(:end) do |t|
|
167
|
+
next if base != t.self
|
168
|
+
|
169
|
+
base.rrf_finalize
|
170
|
+
|
171
|
+
# It's important to disable the trace once we've found the end of the base class definition,
|
172
|
+
# for performance.
|
173
|
+
t.disable
|
174
|
+
end
|
175
|
+
end
|
129
176
|
end
|
130
177
|
|
131
178
|
# Get the configured serializer class.
|
@@ -269,7 +316,8 @@ module RESTFramework::BaseControllerMixin
|
|
269
316
|
@xml_payload = payload.to_xml if self.class.serialize_to_xml
|
270
317
|
end
|
271
318
|
@template_logo_text ||= "Rails REST Framework"
|
272
|
-
@title ||= self.
|
319
|
+
@title ||= self.class.get_title
|
320
|
+
@description ||= self.class.description
|
273
321
|
@route_props, @route_groups = RESTFramework::Utils.get_routes(
|
274
322
|
Rails.application.routes, request
|
275
323
|
)
|
@@ -5,6 +5,44 @@ require_relative "../filters"
|
|
5
5
|
module RESTFramework::BaseModelControllerMixin
|
6
6
|
include RESTFramework::BaseControllerMixin
|
7
7
|
|
8
|
+
RRF_BASE_MODEL_CONTROLLER_CONFIG = {
|
9
|
+
# Core attributes related to models.
|
10
|
+
model: nil,
|
11
|
+
recordset: nil,
|
12
|
+
|
13
|
+
# Attributes for configuring record fields.
|
14
|
+
fields: nil,
|
15
|
+
action_fields: nil,
|
16
|
+
|
17
|
+
# Attributes for finding records.
|
18
|
+
find_by_fields: nil,
|
19
|
+
find_by_query_param: "find_by",
|
20
|
+
|
21
|
+
# Attributes for create/update parameters.
|
22
|
+
allowed_parameters: nil,
|
23
|
+
allowed_action_parameters: nil,
|
24
|
+
|
25
|
+
# Attributes for the default native serializer.
|
26
|
+
native_serializer_config: nil,
|
27
|
+
native_serializer_singular_config: nil,
|
28
|
+
native_serializer_plural_config: nil,
|
29
|
+
native_serializer_only_query_param: "only",
|
30
|
+
native_serializer_except_query_param: "except",
|
31
|
+
|
32
|
+
# Attributes for default model filtering, ordering, and searching.
|
33
|
+
filterset_fields: nil,
|
34
|
+
ordering_fields: nil,
|
35
|
+
ordering_query_param: "ordering",
|
36
|
+
ordering_no_reorder: false,
|
37
|
+
search_fields: nil,
|
38
|
+
search_query_param: "search",
|
39
|
+
search_ilike: false,
|
40
|
+
|
41
|
+
# Other misc attributes.
|
42
|
+
create_from_recordset: true, # Option for `recordset.create` vs `Model.create` behavior.
|
43
|
+
filter_recordset_before_find: true, # Control if filtering is done before find.
|
44
|
+
}
|
45
|
+
|
8
46
|
module ClassMethods
|
9
47
|
IGNORE_VALIDATORS_WITH_KEYS = [:if, :unless]
|
10
48
|
|
@@ -19,8 +57,8 @@ module RESTFramework::BaseModelControllerMixin
|
|
19
57
|
rescue NameError
|
20
58
|
end
|
21
59
|
|
22
|
-
# Delegate to the recordset's model, if it's defined.
|
23
|
-
unless from_get_recordset
|
60
|
+
# Delegate to the recordset's model, if it's defined. This option prevents infinite recursion.
|
61
|
+
unless from_get_recordset
|
24
62
|
# Instantiate a new controller to get the recordset.
|
25
63
|
controller = self.new
|
26
64
|
controller.request = ActionController::TestRequest.new
|
@@ -34,24 +72,34 @@ module RESTFramework::BaseModelControllerMixin
|
|
34
72
|
return nil
|
35
73
|
end
|
36
74
|
|
75
|
+
# Override `get_label` to include ActiveRecord i18n-translated column names.
|
76
|
+
def get_label(s)
|
77
|
+
return self.get_model.human_attribute_name(s, default: super)
|
78
|
+
end
|
79
|
+
|
37
80
|
# Get metadata about the resource's fields.
|
38
81
|
def get_fields_metadata(fields: nil)
|
39
82
|
# Get metadata sources.
|
40
83
|
model = self.get_model
|
41
|
-
fields ||= self.fields || model
|
84
|
+
fields ||= self.fields || model.column_names || []
|
42
85
|
fields = fields.map(&:to_s)
|
43
|
-
columns = model
|
44
|
-
column_defaults = model
|
45
|
-
attributes = model
|
86
|
+
columns = model.columns_hash
|
87
|
+
column_defaults = model.column_defaults
|
88
|
+
attributes = model._default_attributes
|
46
89
|
|
47
90
|
return fields.map { |f|
|
48
91
|
# Initialize metadata to make the order consistent.
|
49
92
|
metadata = {
|
50
|
-
type: nil,
|
93
|
+
type: nil,
|
94
|
+
kind: nil,
|
95
|
+
label: self.get_label(f),
|
96
|
+
primary_key: nil,
|
97
|
+
required: nil,
|
98
|
+
read_only: nil,
|
51
99
|
}
|
52
100
|
|
53
101
|
# Determine `primary_key` based on model.
|
54
|
-
if model
|
102
|
+
if model.primary_key == f
|
55
103
|
metadata[:primary_key] = true
|
56
104
|
end
|
57
105
|
|
@@ -59,7 +107,6 @@ module RESTFramework::BaseModelControllerMixin
|
|
59
107
|
if column = columns[f]
|
60
108
|
metadata[:type] = column.type
|
61
109
|
metadata[:required] = true unless column.null
|
62
|
-
metadata[:label] = column.human_name.instance_eval { |n| n == "Id" ? "ID" : n }
|
63
110
|
metadata[:kind] = "column"
|
64
111
|
end
|
65
112
|
|
@@ -121,73 +168,15 @@ module RESTFramework::BaseModelControllerMixin
|
|
121
168
|
},
|
122
169
|
)
|
123
170
|
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def self.included(base)
|
127
|
-
return unless base.is_a?(Class)
|
128
|
-
|
129
|
-
RESTFramework::BaseControllerMixin.included(base)
|
130
|
-
base.extend(ClassMethods)
|
131
|
-
|
132
|
-
# Add class attributes (with defaults) unless they already exist.
|
133
|
-
{
|
134
|
-
# Core attributes related to models.
|
135
|
-
model: nil,
|
136
|
-
recordset: nil,
|
137
|
-
|
138
|
-
# Attributes for configuring record fields.
|
139
|
-
fields: nil,
|
140
|
-
action_fields: nil,
|
141
|
-
metadata_fields: nil,
|
142
|
-
|
143
|
-
# Attributes for finding records.
|
144
|
-
find_by_fields: nil,
|
145
|
-
find_by_query_param: "find_by",
|
146
|
-
|
147
|
-
# Attributes for create/update parameters.
|
148
|
-
allowed_parameters: nil,
|
149
|
-
allowed_action_parameters: nil,
|
150
|
-
|
151
|
-
# Attributes for the default native serializer.
|
152
|
-
native_serializer_config: nil,
|
153
|
-
native_serializer_singular_config: nil,
|
154
|
-
native_serializer_plural_config: nil,
|
155
|
-
native_serializer_only_query_param: "only",
|
156
|
-
native_serializer_except_query_param: "except",
|
157
|
-
|
158
|
-
# Attributes for default model filtering, ordering, and searching.
|
159
|
-
filterset_fields: nil,
|
160
|
-
ordering_fields: nil,
|
161
|
-
ordering_query_param: "ordering",
|
162
|
-
ordering_no_reorder: false,
|
163
|
-
search_fields: nil,
|
164
|
-
search_query_param: "search",
|
165
|
-
search_ilike: false,
|
166
|
-
|
167
|
-
# Other misc attributes.
|
168
|
-
create_from_recordset: true, # Option for `recordset.create` vs `Model.create` behavior.
|
169
|
-
filter_recordset_before_find: true, # Control if filtering is done before find.
|
170
|
-
}.each do |a, default|
|
171
|
-
next if base.respond_to?(a)
|
172
|
-
|
173
|
-
base.class_attribute(a)
|
174
|
-
|
175
|
-
# Set default manually so we can still support Rails 4. Maybe later we can use the default
|
176
|
-
# parameter on `class_attribute`.
|
177
|
-
base.send(:"#{a}=", default)
|
178
|
-
end
|
179
|
-
|
180
|
-
# Actions to run at the end of the class definition.
|
181
|
-
TracePoint.trace(:end) do |t|
|
182
|
-
next if base != t.self
|
183
171
|
|
172
|
+
def setup_delegation
|
184
173
|
# Delegate extra actions.
|
185
|
-
|
174
|
+
self.extra_actions&.each do |action, config|
|
186
175
|
next unless config.is_a?(Hash) && config[:delegate]
|
176
|
+
next unless self.get_model.respond_to?(action)
|
187
177
|
|
188
|
-
|
178
|
+
self.define_method(action) do
|
189
179
|
model = self.class.get_model
|
190
|
-
next unless model.respond_to?(action)
|
191
180
|
|
192
181
|
if model.method(action).parameters.last&.first == :keyrest
|
193
182
|
return api_response(model.send(action, **params))
|
@@ -198,12 +187,12 @@ module RESTFramework::BaseModelControllerMixin
|
|
198
187
|
end
|
199
188
|
|
200
189
|
# Delegate extra member actions.
|
201
|
-
|
190
|
+
self.extra_member_actions&.each do |action, config|
|
202
191
|
next unless config.is_a?(Hash) && config[:delegate]
|
192
|
+
next unless self.get_model.method_defined?(action)
|
203
193
|
|
204
|
-
|
194
|
+
self.define_method(action) do
|
205
195
|
record = self.get_record
|
206
|
-
next unless record.respond_to?(action)
|
207
196
|
|
208
197
|
if record.method(action).parameters.last&.first == :keyrest
|
209
198
|
return api_response(record.send(action, **params))
|
@@ -212,10 +201,38 @@ module RESTFramework::BaseModelControllerMixin
|
|
212
201
|
end
|
213
202
|
end
|
214
203
|
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Define any behavior to execute at the end of controller definition.
|
207
|
+
def rrf_finalize
|
208
|
+
super
|
209
|
+
self.setup_delegation
|
210
|
+
# self.setup_channel
|
215
211
|
|
216
|
-
|
217
|
-
|
218
|
-
|
212
|
+
if RESTFramework.config.freeze_config
|
213
|
+
self::RRF_BASE_MODEL_CONTROLLER_CONFIG.keys.each { |k|
|
214
|
+
v = self.send(k)
|
215
|
+
v.freeze if v.is_a?(Hash) || v.is_a?(Array)
|
216
|
+
}
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.included(base)
|
222
|
+
return unless base.is_a?(Class)
|
223
|
+
|
224
|
+
RESTFramework::BaseControllerMixin.included(base)
|
225
|
+
base.extend(ClassMethods)
|
226
|
+
|
227
|
+
# Add class attributes (with defaults) unless they already exist.
|
228
|
+
RRF_BASE_MODEL_CONTROLLER_CONFIG.each do |a, default|
|
229
|
+
next if base.respond_to?(a)
|
230
|
+
|
231
|
+
base.class_attribute(a)
|
232
|
+
|
233
|
+
# Set default manually so we can still support Rails 4. Maybe later we can use the default
|
234
|
+
# parameter on `class_attribute`.
|
235
|
+
base.send(:"#{a}=", default)
|
219
236
|
end
|
220
237
|
end
|
221
238
|
|
@@ -39,8 +39,10 @@ module ActionDispatch::Routing
|
|
39
39
|
|
40
40
|
# Interal interface for routing extra actions.
|
41
41
|
def _route_extra_actions(actions, &block)
|
42
|
-
|
43
|
-
|
42
|
+
parsed_actions = RESTFramework::Utils.parse_extra_actions(actions)
|
43
|
+
|
44
|
+
parsed_actions.each do |action, config|
|
45
|
+
[config[:methods]].flatten.each do |m|
|
44
46
|
public_send(m, config[:path], action: action, **(config[:kwargs] || {}))
|
45
47
|
end
|
46
48
|
yield if block_given?
|
@@ -84,17 +86,13 @@ module ActionDispatch::Routing
|
|
84
86
|
public_send(resource_method, name, except: skip, **kwargs) do
|
85
87
|
if controller_class.respond_to?(:extra_member_actions)
|
86
88
|
member do
|
87
|
-
self._route_extra_actions(
|
88
|
-
RESTFramework::Utils.parse_extra_actions(controller_class.extra_member_actions),
|
89
|
-
)
|
89
|
+
self._route_extra_actions(controller_class.extra_member_actions)
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
93
|
collection do
|
94
94
|
# Route extra controller-defined actions.
|
95
|
-
self._route_extra_actions(
|
96
|
-
RESTFramework::Utils.parse_extra_actions(controller_class.extra_actions),
|
97
|
-
)
|
95
|
+
self._route_extra_actions(controller_class.extra_actions)
|
98
96
|
|
99
97
|
# Route extra RRF-defined actions.
|
100
98
|
RESTFramework::RRF_BUILTIN_ACTIONS.each do |action, methods|
|
@@ -155,9 +153,7 @@ module ActionDispatch::Routing
|
|
155
153
|
|
156
154
|
collection do
|
157
155
|
# Route extra controller-defined actions.
|
158
|
-
self._route_extra_actions(
|
159
|
-
RESTFramework::Utils.parse_extra_actions(controller_class.extra_actions),
|
160
|
-
)
|
156
|
+
self._route_extra_actions(controller_class.extra_actions)
|
161
157
|
|
162
158
|
# Route extra RRF-defined actions.
|
163
159
|
RESTFramework::RRF_BUILTIN_ACTIONS.each do |action, methods|
|
data/lib/rest_framework/utils.rb
CHANGED
@@ -1,20 +1,30 @@
|
|
1
1
|
module RESTFramework::Utils
|
2
2
|
HTTP_METHOD_ORDERING = %w(GET POST PUT PATCH DELETE OPTIONS HEAD)
|
3
3
|
|
4
|
-
# Convert `extra_actions` hash to a consistent format: `{path:, methods:, kwargs:}
|
5
|
-
|
4
|
+
# Convert `extra_actions` hash to a consistent format: `{path:, methods:, kwargs:}`, and
|
5
|
+
# additional metadata fields.
|
6
|
+
#
|
7
|
+
# If a controller is provided, labels will be added to any metadata fields.
|
8
|
+
def self.parse_extra_actions(extra_actions, controller: nil)
|
6
9
|
return (extra_actions || {}).map { |k, v|
|
7
10
|
path = k
|
8
11
|
|
9
12
|
# Convert structure to path/methods/kwargs.
|
10
13
|
if v.is_a?(Hash) # Allow kwargs to be used to define path differently from the key.
|
14
|
+
# Symbolize keys (which also makes a copy so we don't mutate the original).
|
11
15
|
v = v.symbolize_keys
|
16
|
+
methods = v.delete(:methods)
|
12
17
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
# First, remove any metadata keys.
|
19
|
+
delegate = v.delete(:delegate)
|
20
|
+
fields = v.delete(:fields)
|
21
|
+
|
22
|
+
# Add label to fields.
|
23
|
+
if controller && fields
|
24
|
+
fields = fields.map { |f| [f, {}] }.to_h if f.is_a?(Array)
|
25
|
+
fields&.each do |field, cfg|
|
26
|
+
cfg[:label] = controller.get_label(field)
|
27
|
+
end
|
18
28
|
end
|
19
29
|
|
20
30
|
# Override path if it's provided.
|
@@ -23,14 +33,24 @@ module RESTFramework::Utils
|
|
23
33
|
end
|
24
34
|
|
25
35
|
# Pass any further kwargs to the underlying Rails interface.
|
26
|
-
kwargs = v.presence
|
27
|
-
elsif v.is_a?(
|
28
|
-
methods = [
|
36
|
+
kwargs = v.presence
|
37
|
+
elsif v.is_a?(Array) && v.length == 1
|
38
|
+
methods = v[0]
|
29
39
|
else
|
30
40
|
methods = v
|
31
41
|
end
|
32
42
|
|
33
|
-
[
|
43
|
+
next [
|
44
|
+
k,
|
45
|
+
{
|
46
|
+
path: path,
|
47
|
+
methods: methods,
|
48
|
+
kwargs: kwargs,
|
49
|
+
delegate: delegate,
|
50
|
+
fields: fields,
|
51
|
+
type: :extra,
|
52
|
+
}.compact,
|
53
|
+
]
|
34
54
|
}.to_h
|
35
55
|
end
|
36
56
|
|
@@ -108,4 +128,13 @@ module RESTFramework::Utils
|
|
108
128
|
[request.params[:controller] == c ? 0 : 1, c.count("/"), c]
|
109
129
|
}.to_h
|
110
130
|
end
|
131
|
+
|
132
|
+
# Custom inflector for RESTful controllers.
|
133
|
+
def self.inflect(s, acronyms=nil)
|
134
|
+
acronyms&.each do |acronym|
|
135
|
+
s = s.gsub(/\b#{acronym}\b/i, acronym)
|
136
|
+
end
|
137
|
+
|
138
|
+
return s
|
139
|
+
end
|
111
140
|
end
|
data/lib/rest_framework.rb
CHANGED
@@ -13,6 +13,31 @@ module RESTFramework
|
|
13
13
|
RRF_BUILTIN_ACTIONS = {
|
14
14
|
options: :options,
|
15
15
|
}.freeze
|
16
|
+
|
17
|
+
# Global configuration should be kept minimal, as controller-level configurations allows multiple
|
18
|
+
# APIs to be defined to behave differently.
|
19
|
+
class Config
|
20
|
+
# Do not run `rrf_finalize` on controllers automatically using a `TracePoint` hook. This is a
|
21
|
+
# performance option and must be global because we have to determine this before any
|
22
|
+
# controller-specific configuration is set. If this is set to `true`, then you must manually
|
23
|
+
# call `rrf_finalize` after any configuration on each controller that needs to participate
|
24
|
+
# in:
|
25
|
+
# - Model delegation, for the helper methods to be defined dynamically.
|
26
|
+
# - Websockets, for `::Channel` class to be defined dynamically.
|
27
|
+
# - Controller configuration finalizatino.
|
28
|
+
attr_accessor :disable_auto_finalize
|
29
|
+
|
30
|
+
# Freeze configuration attributes during finalization to prevent accidental mutation.
|
31
|
+
attr_accessor :freeze_config
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.config
|
35
|
+
return @config ||= Config.new
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.configure
|
39
|
+
yield(self.config)
|
40
|
+
end
|
16
41
|
end
|
17
42
|
|
18
43
|
require_relative "rest_framework/controller_mixins"
|
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.7.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: 2022-12-
|
11
|
+
date: 2022-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|