rest_framework 1.0.0.rc1 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12d9337d2e8abbdde98e25233fc2cd9d1b3596b0f65a8a5b3210eee5d5a571bd
4
- data.tar.gz: e77d714a217b1ecc6ab24cdab384d7ce82d2680775e15e701fa4f1c2b2e8d88d
3
+ metadata.gz: 90b0053684520db23e669f2badfea90f4bc138106f79cfece16e17fc4ef449f0
4
+ data.tar.gz: 03a2d6ec4d1178fca154641ca6522df27592fa2b32b7024de76ceec827d23a8f
5
5
  SHA512:
6
- metadata.gz: 93d22bc0db33b0b99876bcf193b8f72bf7d12072a46c34d82e735c11223cb711329c685bda323f2cb4e7383c490f298f2225efa674ced397a275bd59597177dc
7
- data.tar.gz: 7d0f5375e43bb4c696f566c851875f00587175d5429a20b9e502350b46dc7d09a4398fedd3ddf91d9b02e3b0e8e262beb8198d5fbe8a267f1c02b5ec73b82a59
6
+ metadata.gz: bb23fb3f071a17bbc1aaf2f8c5b8571e47a46a8faeeeb43f851dc64d60d422b7a12ba924200e5ec4124b5b75fe06f868a8ee623c97345753f7d1dc3d5244c16d
7
+ data.tar.gz: 2828fe0ca8cf5415c781f09da370dc1e92e5408b21e0d77650151aa1d9234e0a31e1e4fb8db2737166acdeba4166b79f42659086fdbd0dcf6bd80150cfb73f11
data/README.md CHANGED
@@ -152,5 +152,9 @@ After you clone the repository, cd'ing into the directory should create a new ge
152
152
  using RVM. Then run `bin/setup` to install the appropriate gems and set things up.
153
153
 
154
154
  The top-level `bin/rails` proxies all Rails commands to the test project, so you can operate it via
155
- the usual commands (e.g., `rails test`, `rails server` and `rails console`). For development, use
156
- `foreman start` to run the web server and the job queue.
155
+ the usual commands (e.g., `rails test`, `rails console`). For development, use `bin/dev` to run the
156
+ web server and the job queue, which serves the test app and coverage/brakeman reports:
157
+
158
+ - Test App: [http://127.0.0.1:3000](http://127.0.0.1:3000)
159
+ - API: [http://127.0.0.1:3000/api](http://127.0.0.1:3000/api)
160
+ - Reports: [http://127.0.0.1:3000/reports](http://127.0.0.1:3000/reports)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0.rc1
1
+ 1.0.0
@@ -1,6 +1,6 @@
1
1
  class RESTFramework::Errors::NilPassedToRenderAPIError < RESTFramework::Errors::BaseError
2
2
  def message
3
- return <<~MSG.split("\n").join(" ")
3
+ <<~MSG.split("\n").join(" ")
4
4
  Payload of `nil` was passed to `render_api`; this is unsupported. If you want a blank
5
5
  response, pass `''` (an empty string) as the payload. If this was the result of a `find_by`
6
6
  (or similar Active Record method) not finding a record, you should use the bang version (e.g.,
@@ -5,7 +5,7 @@ class RESTFramework::Errors::UnknownModelError < RESTFramework::Errors::BaseErro
5
5
  end
6
6
 
7
7
  def message
8
- return <<~MSG.split("\n").join(" ")
8
+ <<~MSG.split("\n").join(" ")
9
9
  The model class for `#{@controller_class}` could not be determined. Any controller that
10
10
  includes `RESTFramework::BaseModelControllerMixin` (directly or indirectly) must either set
11
11
  the `model` attribute on the controller, or the model must be deducible from the controller
@@ -2,7 +2,7 @@
2
2
  class RESTFramework::Filters::OrderingFilter < RESTFramework::Filters::BaseFilter
3
3
  # Get a list of ordering fields for the current action.
4
4
  def _get_fields
5
- return @controller.class.ordering_fields&.map(&:to_s) || @controller.get_fields
5
+ @controller.class.ordering_fields&.map(&:to_s) || @controller.get_fields
6
6
  end
7
7
 
8
8
  # Convert ordering string to an ordering configuration.
@@ -34,7 +34,7 @@ class RESTFramework::Filters::OrderingFilter < RESTFramework::Filters::BaseFilte
34
34
  return ordering
35
35
  end
36
36
 
37
- return nil
37
+ nil
38
38
  end
39
39
 
40
40
  # Order data according to the request query parameters.
@@ -46,7 +46,7 @@ class RESTFramework::Filters::OrderingFilter < RESTFramework::Filters::BaseFilte
46
46
  return data.send(reorder ? :reorder : :order, ordering)
47
47
  end
48
48
 
49
- return data
49
+ data
50
50
  end
51
51
  end
52
52
 
@@ -13,18 +13,18 @@ class RESTFramework::Filters::QueryFilter < RESTFramework::Filters::BaseFilter
13
13
  true: true,
14
14
  false: false,
15
15
  null: nil,
16
- lt: ->(f, v) { {f => ...v} },
16
+ lt: ->(f, v) { { f => ...v } },
17
17
  # `gt` must negate `lte` because Rails doesn't support `>` with endless ranges.
18
- gt: ->(f, v) { Not.new({f => ..v}) },
19
- lte: ->(f, v) { {f => ..v} },
20
- gte: ->(f, v) { {f => v..} },
21
- not: ->(f, v) { Not.new({f => v}) },
22
- cont: ->(f, v) { ["#{f} LIKE ?", "%#{ActiveRecord::Base.sanitize_sql_like(v)}%"] },
18
+ gt: ->(f, v) { Not.new({ f => ..v }) },
19
+ lte: ->(f, v) { { f => ..v } },
20
+ gte: ->(f, v) { { f => v.. } },
21
+ not: ->(f, v) { Not.new({ f => v }) },
22
+ cont: ->(f, v) { [ "#{f} LIKE ?", "%#{ActiveRecord::Base.sanitize_sql_like(v)}%" ] },
23
23
  in: ->(f, v) {
24
24
  if v.is_a?(Array)
25
- {f => v.map { |v| v == "null" ? nil : v }}
25
+ { f => v.map { |v| v == "null" ? nil : v } }
26
26
  elsif v.is_a?(String)
27
- {f => v.split(",").map { |v| v == "null" ? nil : v }}
27
+ { f => v.split(",").map { |v| v == "null" ? nil : v } }
28
28
  end
29
29
  },
30
30
  }.freeze
@@ -33,7 +33,7 @@ class RESTFramework::Filters::QueryFilter < RESTFramework::Filters::BaseFilter
33
33
  # Get a list of filter fields for the current action.
34
34
  def _get_fields
35
35
  # Always return a list of strings; `@controller.get_fields` already does this.
36
- return @controller.class.filter_fields&.map(&:to_s) || @controller.get_fields
36
+ @controller.class.filter_fields&.map(&:to_s) || @controller.get_fields
37
37
  end
38
38
 
39
39
  # Helper to find a variation of a field using a predicate. For example, there could be a field
@@ -62,7 +62,7 @@ class RESTFramework::Filters::QueryFilter < RESTFramework::Filters::BaseFilter
62
62
  base_query = @controller.request.query_parameters.map { |field, v|
63
63
  # First, if field is a simple filterable field, return early.
64
64
  if field.in?(fields)
65
- next [field, v]
65
+ next [ field, v ]
66
66
  end
67
67
 
68
68
  # First, try to parse a simple predicate and check if it is filterable.
@@ -81,7 +81,7 @@ class RESTFramework::Filters::QueryFilter < RESTFramework::Filters::BaseFilter
81
81
  sub_fields = @controller.class.field_configuration[root_field][:sub_fields] || []
82
82
  if sub_field.in?(sub_fields)
83
83
  includes << root_field.to_sym
84
- next [field, v]
84
+ next [ field, v ]
85
85
  elsif pred_sub_field && pred_sub_field.in?(sub_fields)
86
86
  includes << root_field.to_sym
87
87
  field = pred_field
@@ -99,13 +99,12 @@ class RESTFramework::Filters::QueryFilter < RESTFramework::Filters::BaseFilter
99
99
  if cfg.is_a?(Proc)
100
100
  pred_queries << cfg.call(field, v)
101
101
  else
102
- pred_queries << {field => cfg}
102
+ pred_queries << { field => cfg }
103
103
  end
104
104
 
105
105
  next nil
106
106
  }.compact.to_h.symbolize_keys
107
107
 
108
- puts "GNS: #{base_query.inspect} #{pred_queries.inspect} #{includes.inspect}"
109
108
  return base_query, pred_queries, includes
110
109
  end
111
110
 
@@ -129,7 +128,7 @@ class RESTFramework::Filters::QueryFilter < RESTFramework::Filters::BaseFilter
129
128
  end
130
129
  end
131
130
 
132
- return data
131
+ data
133
132
  end
134
133
  end
135
134
 
@@ -20,7 +20,7 @@ class RESTFramework::Filters::RansackFilter < RESTFramework::Filters::BaseFilter
20
20
  return data.ransack(q, @controller.class.ransack_options || {}).result(distinct: distinct)
21
21
  end
22
22
 
23
- return data
23
+ data
24
24
  end
25
25
  end
26
26
 
@@ -6,7 +6,7 @@ class RESTFramework::Filters::SearchFilter < RESTFramework::Filters::BaseFilter
6
6
  end
7
7
 
8
8
  columns = @controller.class.get_model.column_names
9
- return @controller.get_fields.select { |f|
9
+ @controller.get_fields.select { |f|
10
10
  f.in?(RESTFramework.config.search_columns) && f.in?(columns)
11
11
  }
12
12
  end
@@ -30,12 +30,12 @@ class RESTFramework::Filters::SearchFilter < RESTFramework::Filters::BaseFilter
30
30
  fields.map { |f|
31
31
  "CAST(#{f} AS #{data_type}) #{@controller.class.search_ilike ? "ILIKE" : "LIKE"} ?"
32
32
  }.join(" OR "),
33
- *(["%#{search}%"] * fields.length),
33
+ *([ "%#{search}%" ] * fields.length),
34
34
  )
35
35
  end
36
36
  end
37
37
 
38
- return data
38
+ data
39
39
  end
40
40
  end
41
41
 
@@ -5,7 +5,7 @@ require "rails/generators"
5
5
  # :nocov:
6
6
  class RESTFrameworkCustomGeneratorControllerNamespace < String
7
7
  def camelize
8
- return "RESTFramework"
8
+ "RESTFramework"
9
9
  end
10
10
  end
11
11
  # :nocov:
@@ -41,7 +41,7 @@ class RESTFramework::Generators::ControllerGenerator < Rails::Generators::Base
41
41
  # Some projects may not have the inflection "REST" as an acronym, which changes this generator to
42
42
  # be namespaced in `r_e_s_t_framework`, which is weird.
43
43
  def self.namespace
44
- return RESTFrameworkCustomGeneratorControllerNamespace.new("rest_framework:controller")
44
+ RESTFrameworkCustomGeneratorControllerNamespace.new("rest_framework:controller")
45
45
  end
46
46
 
47
47
  def create_rest_controller_file
@@ -11,7 +11,7 @@ module RESTFramework::Mixins::BaseControllerMixin
11
11
  title: nil,
12
12
  description: nil,
13
13
  version: nil,
14
- inflect_acronyms: ["ID", "IDs", "REST", "API", "APIs"].freeze,
14
+ inflect_acronyms: [ "ID", "IDs", "REST", "API", "APIs" ].freeze,
15
15
  openapi_include_children: false,
16
16
 
17
17
  # Options related to serialization.
@@ -38,14 +38,14 @@ module RESTFramework::Mixins::BaseControllerMixin
38
38
 
39
39
  # Default action for API root.
40
40
  def root
41
- render(api: {message: "This is the API root."})
41
+ render(api: { message: "This is the API root." })
42
42
  end
43
43
 
44
44
  module ClassMethods
45
45
  # By default, this is the name of the controller class, titleized and with any custom inflection
46
46
  # acronyms applied.
47
47
  def get_title
48
- return self.title || RESTFramework::Utils.inflect(
48
+ self.title || RESTFramework::Utils.inflect(
49
49
  self.name.demodulize.chomp("Controller").titleize(keep_id_suffix: true),
50
50
  self.inflect_acronyms,
51
51
  )
@@ -53,10 +53,7 @@ module RESTFramework::Mixins::BaseControllerMixin
53
53
 
54
54
  # Get a label from a field/column name, titleized and inflected.
55
55
  def label_for(s)
56
- return RESTFramework::Utils.inflect(
57
- s.to_s.titleize(keep_id_suffix: true),
58
- self.inflect_acronyms,
59
- )
56
+ RESTFramework::Utils.inflect(s.to_s.titleize(keep_id_suffix: true), self.inflect_acronyms)
60
57
  end
61
58
 
62
59
  # Define any behavior to execute at the end of controller definition.
@@ -72,7 +69,7 @@ module RESTFramework::Mixins::BaseControllerMixin
72
69
  # :nocov:
73
70
 
74
71
  def openapi_response_content_types
75
- return @openapi_response_content_types ||= [
72
+ @openapi_response_content_types ||= [
76
73
  "text/html",
77
74
  self.serialize_to_json ? "application/json" : nil,
78
75
  self.serialize_to_xml ? "application/xml" : nil,
@@ -80,7 +77,7 @@ module RESTFramework::Mixins::BaseControllerMixin
80
77
  end
81
78
 
82
79
  def openapi_request_content_types
83
- return @openapi_request_content_types ||= [
80
+ @openapi_request_content_types ||= [
84
81
  "application/json",
85
82
  "application/x-www-form-urlencoded",
86
83
  "multipart/form-data",
@@ -91,7 +88,7 @@ module RESTFramework::Mixins::BaseControllerMixin
91
88
  resp_cts = self.openapi_response_content_types
92
89
  req_cts = self.openapi_request_content_types
93
90
 
94
- return routes.group_by { |r| r[:concat_path] }.map { |concat_path, routes|
91
+ routes.group_by { |r| r[:concat_path] }.map { |concat_path, routes|
95
92
  [
96
93
  concat_path.gsub(/:([0-9A-Za-z_-]+)/, "{\\1}"),
97
94
  routes.map { |route|
@@ -99,28 +96,24 @@ module RESTFramework::Mixins::BaseControllerMixin
99
96
  summary = metadata.delete(:label).presence || self.label_for(route[:action])
100
97
  description = metadata.delete(:description).presence
101
98
  extra_action = RESTFramework::EXTRA_ACTION_ROUTES.include?(route[:path])
102
- error_response = {"$ref" => "#/components/responses/BadRequest"}
103
- not_found_response = {"$ref" => "#/components/responses/NotFound"}
104
- spec = {tags: [tag], summary: summary, description: description}.compact
99
+ error_response = { "$ref" => "#/components/responses/BadRequest" }
100
+ not_found_response = { "$ref" => "#/components/responses/NotFound" }
101
+ spec = { tags: [ tag ], summary: summary, description: description }.compact
105
102
 
106
103
  # All routes should have a successful response.
107
104
  spec[:responses] = {
108
- 200 => {content: resp_cts.map { |ct| [ct, {}] }.to_h, description: "Success"},
105
+ 200 => { content: resp_cts.map { |ct| [ ct, {} ] }.to_h, description: "Success" },
109
106
  }
110
107
 
111
108
  # Builtin POST, PUT, PATCH, and DELETE should have a 400 and 404 response.
112
- if route[:verb].in?(["POST", "PUT", "PATCH", "DELETE"]) && !extra_action
109
+ if route[:verb].in?([ "POST", "PUT", "PATCH", "DELETE" ]) && !extra_action
113
110
  spec[:responses][400] = error_response
114
111
  spec[:responses][404] = not_found_response
115
112
  end
116
113
 
117
114
  # All POST, PUT, PATCH should have a request body.
118
- if route[:verb].in?(["POST", "PUT", "PATCH"])
119
- spec[:requestBody] ||= {
120
- content: req_cts.map { |ct|
121
- [ct, {}]
122
- }.to_h,
123
- }
115
+ if route[:verb].in?([ "POST", "PUT", "PATCH" ])
116
+ spec[:requestBody] ||= { content: req_cts.map { |ct| [ ct, {} ] }.to_h }
124
117
  end
125
118
 
126
119
  # Add remaining metadata as an extension.
@@ -134,7 +127,7 @@ module RESTFramework::Mixins::BaseControllerMixin
134
127
  name: p,
135
128
  in: "path",
136
129
  required: true,
137
- schema: {type: "integer"},
130
+ schema: { type: "integer" },
138
131
  }
139
132
  },
140
133
  },
@@ -146,23 +139,25 @@ module RESTFramework::Mixins::BaseControllerMixin
146
139
  def openapi_document(request, route_group_name, routes)
147
140
  server = request.base_url + request.original_fullpath.gsub(/\?.*/, "")
148
141
 
149
- return {
142
+ {
150
143
  openapi: "3.1.1",
151
144
  info: {
152
145
  title: self.get_title,
153
146
  description: self.description,
154
147
  version: self.version.to_s,
155
148
  }.compact,
156
- servers: [{url: server}],
149
+ servers: [ { url: server } ],
157
150
  paths: self.openapi_paths(routes, route_group_name),
158
- tags: [{name: route_group_name, description: self.description}.compact],
151
+ tags: [ { name: route_group_name, description: self.description }.compact ],
159
152
  components: {
160
153
  schemas: {
161
154
  "Error" => {
162
155
  type: "object",
163
- required: ["message"],
156
+ required: [ "message" ],
164
157
  properties: {
165
- message: {type: "string"}, errors: {type: "object"}, exception: {type: "string"}
158
+ message: { type: "string" },
159
+ errors: { type: "object" },
160
+ exception: { type: "string" },
166
161
  },
167
162
  },
168
163
  },
@@ -170,13 +165,19 @@ module RESTFramework::Mixins::BaseControllerMixin
170
165
  "BadRequest": {
171
166
  description: "Bad Request",
172
167
  content: self.openapi_response_content_types.map { |ct|
173
- [ct, ct == "text/html" ? {} : {schema: {"$ref" => "#/components/schemas/Error"}}]
168
+ [
169
+ ct,
170
+ ct == "text/html" ? {} : { schema: { "$ref" => "#/components/schemas/Error" } },
171
+ ]
174
172
  }.to_h,
175
173
  },
176
174
  "NotFound": {
177
175
  description: "Not Found",
178
176
  content: self.openapi_response_content_types.map { |ct|
179
- [ct, ct == "text/html" ? {} : {schema: {"$ref" => "#/components/schemas/Error"}}]
177
+ [
178
+ ct,
179
+ ct == "text/html" ? {} : { schema: { "$ref" => "#/components/schemas/Error" } },
180
+ ]
180
181
  }.to_h,
181
182
  },
182
183
  },
@@ -249,12 +250,12 @@ module RESTFramework::Mixins::BaseControllerMixin
249
250
  end
250
251
 
251
252
  def get_serializer_class
252
- return self.class.serializer_class
253
+ self.class.serializer_class
253
254
  end
254
255
 
255
256
  # Serialize the given data using the `serializer_class`.
256
257
  def serialize(data, **kwargs)
257
- return RESTFramework::Utils.wrap_ams(self.get_serializer_class).new(
258
+ RESTFramework::Utils.wrap_ams(self.get_serializer_class).new(
258
259
  data, controller: self, **kwargs
259
260
  ).serialize
260
261
  end
@@ -278,7 +279,7 @@ module RESTFramework::Mixins::BaseControllerMixin
278
279
  end
279
280
 
280
281
  def route_groups
281
- return @route_groups ||= RESTFramework::Utils.get_routes(Rails.application.routes, request)
282
+ @route_groups ||= RESTFramework::Utils.get_routes(Rails.application.routes, request)
282
283
  end
283
284
 
284
285
  # Render a browsable API for `html` format, along with basic `json`/`xml` formats, and with
@@ -379,7 +380,7 @@ module RESTFramework::Mixins::BaseControllerMixin
379
380
  end
380
381
  end
381
382
 
382
- return document
383
+ document
383
384
  end
384
385
 
385
386
  def options
@@ -7,7 +7,7 @@ module RESTFramework::Mixins::BulkCreateModelMixin
7
7
  # overloads the existing collection `POST` endpoint, so we add a special key to the OpenAPI
8
8
  # metadata to indicate bulk create is supported.
9
9
  def openapi_document
10
- return super.merge({"x-rrf-bulk-create": true})
10
+ super.merge({ "x-rrf-bulk-create": true })
11
11
  end
12
12
 
13
13
  def create
@@ -17,7 +17,7 @@ module RESTFramework::Mixins::BulkCreateModelMixin
17
17
  return render(api: serialized_records)
18
18
  end
19
19
 
20
- return super
20
+ super
21
21
  end
22
22
 
23
23
  # Perform the `create` call, and return the collection of (possibly) created records.
@@ -25,9 +25,7 @@ module RESTFramework::Mixins::BulkCreateModelMixin
25
25
  create_data = self.get_create_params(bulk_mode: true)[:_json]
26
26
 
27
27
  # Perform bulk create in a transaction.
28
- return ActiveRecord::Base.transaction do
29
- next self.get_create_from.create(create_data)
30
- end
28
+ ActiveRecord::Base.transaction { self.get_create_from.create(create_data) }
31
29
  end
32
30
  end
33
31
 
@@ -42,17 +40,15 @@ module RESTFramework::Mixins::BulkUpdateModelMixin
42
40
  # Perform the `update` call and return the collection of (possibly) updated records.
43
41
  def update_all!
44
42
  pk = self.class.get_model.primary_key
45
- update_data = if params[:_json].is_a?(Array)
43
+ data = if params[:_json].is_a?(Array)
46
44
  self.get_create_params(bulk_mode: :update)[:_json].index_by { |r| r[pk] }
47
45
  else
48
46
  create_params = self.get_create_params
49
- {create_params[pk] => create_params}
47
+ { create_params[pk] => create_params }
50
48
  end
51
49
 
52
50
  # Perform bulk update in a transaction.
53
- return ActiveRecord::Base.transaction do
54
- next self.get_recordset.update(update_data.keys, update_data.values)
55
- end
51
+ ActiveRecord::Base.transaction { self.get_recordset.update(data.keys, data.values) }
56
52
  end
57
53
  end
58
54
 
@@ -66,8 +62,7 @@ module RESTFramework::Mixins::BulkDestroyModelMixin
66
62
  end
67
63
 
68
64
  render(
69
- api: {message: "Bulk destroy requires an array of primary keys as input."},
70
- status: 400,
65
+ api: { message: "Bulk destroy requires an array of primary keys as input." }, status: 400,
71
66
  )
72
67
  end
73
68
 
@@ -77,9 +72,7 @@ module RESTFramework::Mixins::BulkDestroyModelMixin
77
72
  destroy_data = self.request.request_parameters[:_json]
78
73
 
79
74
  # Perform bulk destroy in a transaction.
80
- return ActiveRecord::Base.transaction do
81
- next self.get_recordset.where(pk => destroy_data).destroy_all
82
- end
75
+ ActiveRecord::Base.transaction { self.get_recordset.where(pk => destroy_data).destroy_all }
83
76
  end
84
77
  end
85
78