rest_framework 0.9.9 → 0.9.11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b816fbe24e240ce66a74b2a3c18fd0a75a449c878c9a0126e4c14ddbaafe5b8
4
- data.tar.gz: 383836204fd789afd4270794e0d7da57e05b8b8e8f537dd19c7fa2bf6ff1e2eb
3
+ metadata.gz: 04ef01197efeca7c6276edbf2ed4c9ef3f50e4fc4d1df927c3d530953d5aab2a
4
+ data.tar.gz: 71bd781956d163f33cdc7fd6aa3950ce93d4f86a0c03325e2bebcd2ba1dfc74b
5
5
  SHA512:
6
- metadata.gz: 0a845ce1b30eab8ad874e432268092e5e9117200a320d9446a3e333a88d202257987767fd6b9bad2b7b0768c52e564e1a0beede15408027c7c48c8b91beffcd4
7
- data.tar.gz: a1ae6ccfd603c7759d5db717249790a7b124bc06b39ff3f88eba16527874ec8788e4fdcb149c4e96ca0a6a8dae1ea3361519acfb830e1dfda6bc5ea2b521177b
6
+ metadata.gz: 96cbf98cec8f47c54309fa5b2b46c90c60249e7da1fa7142ef85c041bfee1154b9fd60c5d420d145a2dfb852df4d8af906e3577cb2f5f70d0bc6fd2da9beeb97
7
+ data.tar.gz: 4b0ab5e4d529f54e4890c8e0da2763ee8915a393ce1260da5c7ce70d6ef940c64e36f15d5a5b70cca255ec881c53b4c48bbcd5f95f2e7d83cc88e233b623b327
data/README.md CHANGED
@@ -7,12 +7,10 @@
7
7
 
8
8
  A framework for DRY RESTful APIs in Ruby on Rails.
9
9
 
10
- **The Problem**: Building controllers for APIs usually involves writing a lot of redundant CRUD
11
- logic, and routing them can be obnoxious. Building and maintaining features like ordering,
12
- filtering, and pagination can be tedious.
10
+ **The Problem**: Building controllers for APIs usually involves writing a lot of redundant CRUD logic, and routing them can be obnoxious.
11
+ Building and maintaining features like ordering, filtering, and pagination can be tedious.
13
12
 
14
- **The Solution**: This framework implements browsable API responses, CRUD actions for your models,
15
- and features like ordering/filtering/pagination, so you can focus on building awesome APIs.
13
+ **The Solution**: This framework implements browsable API responses, CRUD actions for your models, and features like ordering/filtering/pagination, so you can focus on building awesome APIs.
16
14
 
17
15
  Website/Guide: [rails-rest-framework.com](https://rails-rest-framework.com)
18
16
 
@@ -40,8 +38,7 @@ bundle install
40
38
 
41
39
  This section provides some simple examples to quickly get you started using the framework.
42
40
 
43
- For the purpose of this example, you'll want to add an `api_controller.rb` to your controllers, as
44
- well as a directory for the resources:
41
+ For the purpose of this example, you'll want to add an `api_controller.rb` to your controllers, as well as a directory for the resources:
45
42
 
46
43
  ```text
47
44
  controllers/
@@ -54,8 +51,7 @@ controllers/
54
51
 
55
52
  ### Controller Mixins
56
53
 
57
- The root `ApiController` can include any common behavior you want to share across all your API
58
- controllers:
54
+ The root `ApiController` can include any common behavior you want to share across all your API controllers:
59
55
 
60
56
  ```ruby
61
57
  class ApiController < ApplicationController
@@ -71,9 +67,8 @@ class ApiController < ApplicationController
71
67
  end
72
68
  ```
73
69
 
74
- A root controller can provide actions that exist on the root of your API. It's best to define a
75
- dedicated root controller, rather than using the `ApiController` for this purpose, so that actions
76
- don't propagate to child controllers:
70
+ A root controller can provide actions that exist on the root of your API.
71
+ It's best to define a dedicated root controller, rather than using the `ApiController` for this purpose, so that actions don't propagate to child controllers:
77
72
 
78
73
  ```ruby
79
74
  class Api::RootController < ApiController
@@ -119,7 +114,8 @@ class Api::MoviesController < ApiController
119
114
  end
120
115
  ```
121
116
 
122
- You can also configure a resource's fields dynamically using `include` and `exclude` keys:
117
+ When `fields` is nil, then it will default to all columns.
118
+ The `fields` attribute can also be a hash to include or exclude fields rather than defining them manually:
123
119
 
124
120
  ```ruby
125
121
  class Api::UsersController < ApiController
@@ -131,10 +127,9 @@ end
131
127
 
132
128
  ### Routing
133
129
 
134
- You can use Rails' `resource`/`resources` routers to route your API, however if you want
135
- `extra_actions` / `extra_member_actions` to be routed automatically, then you can use `rest_route`
136
- for non-resourceful controllers, or `rest_resource` / `rest_resources` resourceful routers. To route
137
- the root, use `rest_root`.
130
+ Use `rest_route` for non-resourceful controllers, or `rest_resource` / `rest_resources` resourceful routers.
131
+ These routers add some features to the Rails builtin `resource`/`resources` routers, such as automatically routing extra actions defined on the controller.
132
+ To route the root, use `rest_root`.
138
133
 
139
134
  ```ruby
140
135
  Rails.application.routes.draw do
@@ -151,8 +146,7 @@ end
151
146
 
152
147
  ## Development/Testing
153
148
 
154
- After you clone the repository, cd'ing into the directory should create a new gemset if you are
155
- using RVM. Then run `bin/setup` to install the appropriate gems and set things up.
149
+ After you clone the repository, cd'ing into the directory should create a new gemset if you are using RVM.
150
+ Then run `bin/setup` to install the appropriate gems and set things up.
156
151
 
157
- The top-level `bin/rails` proxies all Rails commands to the test project, so you can operate it via
158
- the usual commands (e.g., `rails test`, `rails server` and `rails console`).
152
+ The top-level `bin/rails` proxies all Rails commands to the test project, so you can operate it via the usual commands (e.g., `rails test`, `rails server` and `rails console`).
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.9
1
+ 0.9.11
@@ -90,9 +90,6 @@
90
90
  html[data-bs-theme="dark"] .rrf-routes .rrf-route-group-header:hover {
91
91
  background-color: #333;
92
92
  }
93
- .rrf-routes .rrf-route-group-header td {
94
- cursor: pointer;
95
- }
96
93
 
97
94
  /* Disable bootstrap's collapsing animation because in tables it causes delayed jerkiness. */
98
95
  .rrf-routes .collapsing {
@@ -194,7 +191,7 @@
194
191
  // Initialize dark/light mode before page fully loads to prevent flash.
195
192
  rrfSetSelectedMode(localStorage.getItem("rrfMode"))
196
193
 
197
- // Initialize dark/light mode after page load (mostly so mode component is updated).
194
+ // Initialize dark/light mode after page load (duplicate so mode component is updated).
198
195
  document.addEventListener("DOMContentLoaded", (event) => {
199
196
  rrfSetSelectedMode(localStorage.getItem("rrfMode"))
200
197
 
@@ -16,7 +16,7 @@
16
16
  <%# Render any other groups under dropdowns. %>
17
17
  <% @route_groups.drop(1).each_with_index do |(name, route_group), index| %>
18
18
  <tr data-bs-toggle="collapse" data-bs-target="#route-group-<%= index %>" class="rrf-route-group-header">
19
- <td colspan="3" class="text-center user-select-none"><%= name %></td>
19
+ <td colspan="3" class="text-center user-select-none" style="cursor: pointer"><%= name %></td>
20
20
  </tr>
21
21
  <tbody id="route-group-<%= index %>" class="collapse">
22
22
  <% route_group.each do |route| %>
@@ -15,6 +15,7 @@ class RESTFramework::Filters::ModelOrderingFilter < RESTFramework::Filters::Base
15
15
 
16
16
  if order_string.present?
17
17
  ordering = {}.with_indifferent_access
18
+
18
19
  order_string.split(",").each do |field|
19
20
  if field[0] == "-"
20
21
  column = field[1..-1]
@@ -24,10 +25,11 @@ class RESTFramework::Filters::ModelOrderingFilter < RESTFramework::Filters::Base
24
25
  direction = :asc
25
26
  end
26
27
 
27
- next if !column.in?(fields) && column.split(".").first.in?(fields)
28
+ next if !column.in?(fields) && !column.split(".").first.in?(fields)
28
29
 
29
30
  ordering[column] = direction
30
31
  end
32
+
31
33
  return ordering
32
34
  end
33
35
 
@@ -1,5 +1,7 @@
1
1
  # A simple filtering backend that supports filtering a recordset based on query parameters.
2
2
  class RESTFramework::Filters::ModelQueryFilter < RESTFramework::Filters::BaseFilter
3
+ NIL_VALUES = ["nil", "null"].freeze
4
+
3
5
  # Get a list of filterset fields for the current action.
4
6
  def _get_fields
5
7
  # Always return a list of strings; `@controller.get_fields` already does this.
@@ -9,8 +11,9 @@ class RESTFramework::Filters::ModelQueryFilter < RESTFramework::Filters::BaseFil
9
11
  # Filter params for keys allowed by the current action's filterset_fields/fields config.
10
12
  def _get_filter_params
11
13
  fields = self._get_fields
14
+ includes = []
12
15
 
13
- return @controller.request.query_parameters.select { |p, _|
16
+ filter_params = @controller.request.query_parameters.select { |p, _|
14
17
  # Remove any trailing `__in` from the field name.
15
18
  field = p.chomp("__in")
16
19
 
@@ -20,7 +23,12 @@ class RESTFramework::Filters::ModelQueryFilter < RESTFramework::Filters::BaseFil
20
23
  next false unless field.in?(fields)
21
24
 
22
25
  sub_fields = @controller.class.get_field_config(field)[:sub_fields] || []
23
- next sub_field.in?(sub_fields)
26
+ if sub_field.in?(sub_fields)
27
+ includes << field.to_sym
28
+ next true
29
+ end
30
+
31
+ next false
24
32
  end
25
33
 
26
34
  next field.in?(fields)
@@ -28,21 +36,27 @@ class RESTFramework::Filters::ModelQueryFilter < RESTFramework::Filters::BaseFil
28
36
  # Convert fields ending in `__in` to array values.
29
37
  if p.end_with?("__in")
30
38
  p = p.chomp("__in")
31
- v = v.split(",")
39
+ v = v.split(",").map { |v| v.in?(NIL_VALUES) ? nil : v }
32
40
  end
33
41
 
34
42
  # Convert "nil" and "null" to nil.
35
- if v == "nil" || v == "null"
36
- v = nil
37
- end
43
+ v = nil if v.in?(NIL_VALUES)
38
44
 
39
45
  [p, v]
40
46
  }.to_h.symbolize_keys
47
+
48
+ return filter_params, includes
41
49
  end
42
50
 
43
51
  # Filter data according to the request query parameters.
44
52
  def get_filtered_data(data)
45
- if filter_params = self._get_filter_params.presence
53
+ filter_params, includes = self._get_filter_params
54
+
55
+ if filter_params.any?
56
+ if includes.any?
57
+ data = data.includes(*includes)
58
+ end
59
+
46
60
  return data.where(**filter_params)
47
61
  end
48
62
 
@@ -33,17 +33,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
33
33
  # Options for handling request body parameters.
34
34
  allowed_parameters: nil,
35
35
  filter_pk_from_request_body: true,
36
- exclude_body_fields: %w[
37
- created_at
38
- created_by
39
- created_by_id
40
- updated_at
41
- updated_by
42
- updated_by_id
43
- _method
44
- utf8
45
- authenticity_token
46
- ].freeze,
36
+ exclude_body_fields: RESTFramework.config.exclude_body_fields,
47
37
 
48
38
  # Attributes for the default native serializer.
49
39
  native_serializer_config: nil,
@@ -515,7 +505,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
515
505
  ActionController::Parameters.new(data).permit(*allowed_params)
516
506
  end
517
507
 
518
- # Filter primary key if configured.
508
+ # Filter primary key, if configured.
519
509
  if self.filter_pk_from_request_body && bulk_mode != :update
520
510
  body_params.delete(pk)
521
511
  end
@@ -527,7 +517,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
527
517
  #
528
518
  # rubocop:disable Layout/LineLength
529
519
  #
530
- # Good example base64 image:
520
+ # Example base64 image:
531
521
  # data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=
532
522
  #
533
523
  # rubocop:enable Layout/LineLength
@@ -572,7 +562,6 @@ module RESTFramework::Mixins::BaseModelControllerMixin
572
562
  # Cache the result.
573
563
  return @record if @record
574
564
 
575
- recordset = self.get_recordset
576
565
  find_by_key = self.class.get_model.primary_key
577
566
  is_pk = true
578
567
 
@@ -588,16 +577,18 @@ module RESTFramework::Mixins::BaseModelControllerMixin
588
577
  end
589
578
  end
590
579
 
591
- # Filter recordset, if configured.
592
- if self.filter_recordset_before_find
593
- recordset = self.get_records
580
+ # Get the recordset, filtering if configured.
581
+ collection = if self.filter_recordset_before_find
582
+ self.get_records
583
+ else
584
+ self.get_recordset
594
585
  end
595
586
 
596
587
  # Return the record. Route key is always `:id` by Rails convention.
597
588
  if is_pk
598
- return @record = recordset.find(request.path_parameters[:id])
589
+ return @record = collection.find(request.path_parameters[:id])
599
590
  else
600
- return @record = recordset.find_by!(find_by_key => request.path_parameters[:id])
591
+ return @record = collection.find_by!(find_by_key => request.path_parameters[:id])
601
592
  end
602
593
  end
603
594
 
@@ -1,5 +1,5 @@
1
- # Do not use Rails-specific helper methods here (e.g., `blank?`) so the module can run standalone.
2
1
  module RESTFramework
2
+ # Do not use Rails-specific helper methods here (e.g., `blank?`) so the module can run standalone.
3
3
  module Version
4
4
  VERSION_FILEPATH = File.expand_path("../../VERSION", __dir__)
5
5
  UNKNOWN = "0-unknown"
@@ -122,9 +122,19 @@ module RESTFramework
122
122
  # Global configuration should be kept minimal, as controller-level configurations allows multiple
123
123
  # APIs to be defined to behave differently.
124
124
  class Config
125
- DEFAULT_EXCLUDE_ASSOCIATION_CLASSES = [].freeze
126
125
  DEFAULT_LABEL_FIELDS = %w(name label login title email username url).freeze
127
126
  DEFAULT_SEARCH_COLUMNS = DEFAULT_LABEL_FIELDS + %w(description note).freeze
127
+ DEFAULT_EXCLUDE_BODY_FIELDS = %w[
128
+ created_at
129
+ created_by
130
+ created_by_id
131
+ updated_at
132
+ updated_by
133
+ updated_by_id
134
+ _method
135
+ utf8
136
+ authenticity_token
137
+ ].freeze
128
138
 
129
139
  # Do not run `rrf_finalize` on controllers automatically using a `TracePoint` hook. This is a
130
140
  # performance option and must be global because we have to determine this before any
@@ -155,6 +165,9 @@ module RESTFramework
155
165
  # The default search columns to use when generating search filters.
156
166
  attr_accessor :search_columns
157
167
 
168
+ # The default list of fields to exclude from the body of the request.
169
+ attr_accessor :exclude_body_fields
170
+
158
171
  # Option to use vendored assets (requires sprockets or propshaft) rather than linking to
159
172
  # external assets (the default).
160
173
  attr_accessor :use_vendored_assets
@@ -163,6 +176,7 @@ module RESTFramework
163
176
  self.show_backtrace = Rails.env.development?
164
177
  self.label_fields = DEFAULT_LABEL_FIELDS
165
178
  self.search_columns = DEFAULT_SEARCH_COLUMNS
179
+ self.exclude_body_fields = DEFAULT_EXCLUDE_BODY_FIELDS
166
180
  end
167
181
  end
168
182