rest_framework 0.9.9 → 0.9.10
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 +15 -21
- data/VERSION +1 -1
- data/lib/rest_framework/filters/model_ordering_filter.rb +3 -1
- data/lib/rest_framework/filters/model_query_filter.rb +21 -7
- data/lib/rest_framework/mixins/model_controller_mixin.rb +10 -19
- data/lib/rest_framework/version.rb +1 -1
- data/lib/rest_framework.rb +15 -1
- 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: 2db255f618d449ef621f1337d182993c21b5d8d87e5e8c960c8b42093248c91d
|
4
|
+
data.tar.gz: 4612f2e11733ae69e418cd2877b8150e2a1dbd4fb0cd34ed29727dcec007f305
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04457aa989c261900e8e1e55829dbb3cc3aed3afda800433944497996a330a92fdff403351be8e4b83b7b44fea658ecd57a3ab20b4b9e41587398a52b8203680
|
7
|
+
data.tar.gz: e67d99fd29629b7bf31e37f3f27edc8e7d5d222172e6b91e5d2340eec0d891b340029097dc798411410d9c5ecbdffe316521c0dd47e8810c6fe897b0ff59624b
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
135
|
-
|
136
|
-
|
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
|
-
|
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.
|
1
|
+
0.9.10
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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:
|
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
|
-
#
|
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
|
-
#
|
592
|
-
if self.filter_recordset_before_find
|
593
|
-
|
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 =
|
589
|
+
return @record = collection.find(request.path_parameters[:id])
|
599
590
|
else
|
600
|
-
return @record =
|
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"
|
data/lib/rest_framework.rb
CHANGED
@@ -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
|
|
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.9.
|
4
|
+
version: 0.9.10
|
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: 2023-07-
|
11
|
+
date: 2023-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|