rest_framework 0.6.13 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|