rest_framework 0.6.13 → 0.7.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fcc66a1bc2a8c965fcaf04106db51a08c98623a642803634fc96d5eccce9283f
4
- data.tar.gz: 06c49e1eb116813833c01810379a7f377023bb37a0bb57429c254224f8b260d9
3
+ metadata.gz: e303d3d1aae81be9cdf85affab8095f8cfe3115ba3beb0a22a7c6b8c9c91f17d
4
+ data.tar.gz: '058ae280bbe91f87b88ad4284bea00cbb914e577c56280e3c0c0d7f0c1e55126'
5
5
  SHA512:
6
- metadata.gz: 6398f494b9c920a0e9765733c6be871d0cdee94fc12a2863fa7bf0bfc1bba41f601713a53b61ba3d57fcd1b3b45001eadbe6ac26b77b4bd69595ad1ae84e2f06
7
- data.tar.gz: 2610d28a5d5b98fd6be70105d04a292d2acb4c3efc9d5ece74ba1a00b90e4968a55ccefc824773c2609015298221c5315ea929440e29923bba9500ce495c8c78
6
+ metadata.gz: 8a831e67a8b0328f6ee80262543ddea4be439ac47b05a7615304728bc6ed708b3563ab3bd4c278a95d57b2e8bfb614e31a8b196649c3f0d51e55c14a0c1b7ced
7
+ data.tar.gz: 25d0b5ef65981c58dee75c168243c9f19dd8ae1c4acb86fbfa84ba80ca3794e6b5e88a392892a988070980b9f1c7b906e61fef9249e5d2f2c74ce70f5a467872
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.13
1
+ 0.7.1
@@ -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].freeze,
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"].freeze,
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 of your awesome API!"})
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
- actions[action] = {path: "", methods: methods} if self.method_defined?(action)
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
- actions[action] = {path: "", methods: methods} if self.method_defined?(action)
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
- name: self.metadata&.name || self.controller_name.titleize,
55
- description: self.metadata&.description,
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,18 @@ 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
+ # :nocov:
119
+ def rrf_finalize
120
+ if RESTFramework.config.freeze_config
121
+ self::RRF_BASE_CONTROLLER_CONFIG.keys.each { |k|
122
+ v = self.send(k)
123
+ v.freeze if v.is_a?(Hash) || v.is_a?(Array)
124
+ }
125
+ end
126
+ end
127
+ # :nocov:
65
128
  end
66
129
 
67
130
  def self.included(base)
@@ -70,34 +133,7 @@ module RESTFramework::BaseControllerMixin
70
133
  base.extend(ClassMethods)
71
134
 
72
135
  # 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|
136
+ RRF_BASE_CONTROLLER_CONFIG.each do |a, default|
101
137
  next if base.respond_to?(a)
102
138
 
103
139
  base.class_attribute(a)
@@ -126,6 +162,21 @@ module RESTFramework::BaseControllerMixin
126
162
  base.rescue_from(ActiveRecord::RecordNotSaved, with: :record_not_saved)
127
163
  base.rescue_from(ActiveRecord::RecordNotDestroyed, with: :record_not_destroyed)
128
164
  base.rescue_from(ActiveModel::UnknownAttributeError, with: :unknown_attribute_error)
165
+
166
+ # Use `TracePoint` hook to automatically call `rrf_finalize`.
167
+ unless RESTFramework.config.disable_auto_finalize
168
+ # :nocov:
169
+ TracePoint.trace(:end) do |t|
170
+ next if base != t.self
171
+
172
+ base.rrf_finalize
173
+
174
+ # It's important to disable the trace once we've found the end of the base class definition,
175
+ # for performance.
176
+ t.disable
177
+ end
178
+ # :nocov:
179
+ end
129
180
  end
130
181
 
131
182
  # Get the configured serializer class.
@@ -269,7 +320,8 @@ module RESTFramework::BaseControllerMixin
269
320
  @xml_payload = payload.to_xml if self.class.serialize_to_xml
270
321
  end
271
322
  @template_logo_text ||= "Rails REST Framework"
272
- @title ||= self.controller_name.titleize
323
+ @title ||= self.class.get_title
324
+ @description ||= self.class.description
273
325
  @route_props, @route_groups = RESTFramework::Utils.get_routes(
274
326
  Rails.application.routes, request
275
327
  )
@@ -5,8 +5,46 @@ 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
- IGNORE_VALIDATORS_WITH_KEYS = [:if, :unless]
47
+ IGNORE_VALIDATORS_WITH_KEYS = [:if, :unless].freeze
10
48
 
11
49
  # Get the model for this controller.
12
50
  def get_model(from_get_recordset: false)
@@ -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 # Prevent infinite recursion.
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&.column_names || []
84
+ fields ||= self.fields || model.column_names || []
42
85
  fields = fields.map(&:to_s)
43
- columns = model&.columns_hash
44
- column_defaults = model&.column_defaults
45
- attributes = model&._default_attributes
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, kind: nil, label: nil, primary_key: nil, required: nil, read_only: 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&.primary_key == f
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
 
@@ -115,79 +162,17 @@ module RESTFramework::BaseModelControllerMixin
115
162
 
116
163
  # Get a hash of metadata to be rendered in the `OPTIONS` response. Cache the result.
117
164
  def get_options_metadata(fields: nil)
118
- return super().merge(
119
- {
120
- fields: self.get_fields_metadata(fields: fields),
121
- },
122
- )
165
+ return super().merge({fields: self.get_fields_metadata(fields: fields)})
123
166
  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
167
 
168
+ def setup_delegation
184
169
  # Delegate extra actions.
185
- base.extra_actions&.each do |action, config|
170
+ self.extra_actions&.each do |action, config|
186
171
  next unless config.is_a?(Hash) && config[:delegate]
172
+ next unless self.get_model.respond_to?(action)
187
173
 
188
- base.define_method(action) do
174
+ self.define_method(action) do
189
175
  model = self.class.get_model
190
- next unless model.respond_to?(action)
191
176
 
192
177
  if model.method(action).parameters.last&.first == :keyrest
193
178
  return api_response(model.send(action, **params))
@@ -198,12 +183,12 @@ module RESTFramework::BaseModelControllerMixin
198
183
  end
199
184
 
200
185
  # Delegate extra member actions.
201
- base.extra_member_actions&.each do |action, config|
186
+ self.extra_member_actions&.each do |action, config|
202
187
  next unless config.is_a?(Hash) && config[:delegate]
188
+ next unless self.get_model.method_defined?(action)
203
189
 
204
- base.define_method(action) do
190
+ self.define_method(action) do
205
191
  record = self.get_record
206
- next unless record.respond_to?(action)
207
192
 
208
193
  if record.method(action).parameters.last&.first == :keyrest
209
194
  return api_response(record.send(action, **params))
@@ -212,10 +197,40 @@ module RESTFramework::BaseModelControllerMixin
212
197
  end
213
198
  end
214
199
  end
200
+ end
201
+
202
+ # Define any behavior to execute at the end of controller definition.
203
+ # :nocov:
204
+ def rrf_finalize
205
+ super
206
+ self.setup_delegation
207
+ # self.setup_channel
208
+
209
+ if RESTFramework.config.freeze_config
210
+ self::RRF_BASE_MODEL_CONTROLLER_CONFIG.keys.each { |k|
211
+ v = self.send(k)
212
+ v.freeze if v.is_a?(Hash) || v.is_a?(Array)
213
+ }
214
+ end
215
+ end
216
+ # :nocov:
217
+ end
218
+
219
+ def self.included(base)
220
+ return unless base.is_a?(Class)
221
+
222
+ RESTFramework::BaseControllerMixin.included(base)
223
+ base.extend(ClassMethods)
224
+
225
+ # Add class attributes (with defaults) unless they already exist.
226
+ RRF_BASE_MODEL_CONTROLLER_CONFIG.each do |a, default|
227
+ next if base.respond_to?(a)
215
228
 
216
- # It's important to disable the trace once we've found the end of the base class definition,
217
- # for performance.
218
- t.disable
229
+ base.class_attribute(a)
230
+
231
+ # Set default manually so we can still support Rails 4. Maybe later we can use the default
232
+ # parameter on `class_attribute`.
233
+ base.send(:"#{a}=", default)
219
234
  end
220
235
  end
221
236
 
@@ -1,7 +1,7 @@
1
1
  require "rails/generators"
2
2
 
3
3
  # Most projects don't have the inflection "REST" as an acronym, so this is a helper class to prevent
4
- # this generator from being namespaced under `r_e_s_t_framework`.
4
+ # this generator from being namespaced as `"r_e_s_t_framework"`.
5
5
  # :nocov:
6
6
  class RESTFrameworkCustomGeneratorControllerNamespace < String
7
7
  def camelize
@@ -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
- actions.each do |action, config|
43
- config[:methods].each do |m|
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|
@@ -114,7 +114,7 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
114
114
  return subcfg unless subcfg
115
115
 
116
116
  if subcfg.is_a?(Array)
117
- subcfg = subcfg.transform_values(&:to_sym)
117
+ subcfg = subcfg.map(&:to_sym)
118
118
 
119
119
  if add
120
120
  # Only add fields which are not already included.
@@ -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
- def self.parse_extra_actions(extra_actions)
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
- # Ensure methods is an array.
14
- if v[:methods].is_a?(String) || v[:methods].is_a?(Symbol)
15
- methods = [v.delete(:methods)]
16
- else
17
- methods = v.delete(:methods)
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&.except(:delegate)
27
- elsif v.is_a?(Symbol) || v.is_a?(String)
28
- methods = [v]
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
- [k, {path: path, methods: methods, kwargs: kwargs}.compact]
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
@@ -7,12 +7,37 @@ module RESTFramework
7
7
  BUILTIN_MEMBER_ACTIONS = {
8
8
  show: :get,
9
9
  edit: :get,
10
- update: [:put, :patch],
10
+ update: [:put, :patch].freeze,
11
11
  destroy: :delete,
12
12
  }.freeze
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 finalization.
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.6.13
4
+ version: 0.7.1
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-28 00:00:00.000000000 Z
11
+ date: 2023-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails