rest_framework 0.1.0 → 0.1.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: 46db4e7aa05600caaa4c1f9f645f417b4d3cd9e5bcde7da0b54d3c5fa84e872e
4
- data.tar.gz: 95c9d018b687795f80c38b43289f19ffd44eb1b170d883c164b590ab6b1c20d0
3
+ metadata.gz: 7cb7ba6d5e7861525dca18f908afa1f398f614af654223ac03de15f7014dafaa
4
+ data.tar.gz: 7938b5b0225e089f2cd3a168b80763ebcc63dd65655ae4c87b045e923477191a
5
5
  SHA512:
6
- metadata.gz: 2c376d191ffa5ae9de932dceb8411362a00be789ff9aa3733f038608da890fc5437d981576e3b17d402b857f7966cb15e437d7cbe2c8447191088a9a2249134f
7
- data.tar.gz: 72e31acb2e66c6d8d2af2dd7375e7732a93b5e66b1c1fedf4c1d781421422c582958d32c6022305cd9cc2ca9dda51e2c2f6c540b1737e11eb3506501ef91b618
6
+ metadata.gz: cb1583fe183387972e1537bc2b90ebe9b224381e46515ffd202f3ad86cb4a545f906264644fff537b5937f8e4f985f39eb3123c6d47e2e8e5c1be31b425aad54
7
+ data.tar.gz: 3a8b3a6e63a4ca03250d8e6697eda885abd6940f8b580f073ffa0c237a59a535d4997aa4591651c01cda61fd3f021508ed69d298a19512f783a9604b6dbcdd7c
data/README.md CHANGED
@@ -3,15 +3,20 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/rest_framework.svg)](https://badge.fury.io/rb/rest_framework)
4
4
  [![Build Status](https://travis-ci.org/gregschmit/rails-rest-framework.svg?branch=master)](https://travis-ci.org/gregschmit/rails-rest-framework)
5
5
 
6
- Rails REST Framework helps you build awesome Web APIs in Ruby on Rails.
6
+ A framework for DRY RESTful APIs in Ruby on Rails.
7
7
 
8
8
  **The Problem**: Building controllers for APIs usually involves writing a lot of redundant CRUD
9
- logic, and routing them can be obnoxious.
9
+ logic, and routing them can be obnoxious. Building and maintaining features like ordering,
10
+ filtering, and pagination can be tedious.
10
11
 
11
- **The Solution**: This gem handles the common logic so you can focus on the parts of your API which
12
- make it unique.
12
+ **The Solution**: This framework implements browsable API responses, CRUD actions for your models,
13
+ and features like ordering/filtering/pagination, so you can focus on building awesome APIs.
13
14
 
14
- To see detailed documentation, visit https://rails-rest-framework.com.
15
+ Website/Guide: [https://rails-rest-framework.com](https://rails-rest-framework.com)
16
+
17
+ Source: [https://github.com/gregschmit/rails-rest-framework](https://github.com/gregschmit/rails-rest-framework)
18
+
19
+ YARD Docs: [https://rubydoc.info/gems/rest_framework](https://rubydoc.info/gems/rest_framework)
15
20
 
16
21
  ## Installation
17
22
 
@@ -110,7 +115,7 @@ end
110
115
  After you clone the repository, cd'ing into the directory should create a new gemset if you are
111
116
  using RVM. Then run `bundle install` to install the appropriate gems.
112
117
 
113
- To run the full test suite:
118
+ To run the test suite:
114
119
 
115
120
  ```shell
116
121
  $ rake test
@@ -1,6 +1,7 @@
1
1
  module RESTFramework
2
2
  end
3
3
 
4
+
4
5
  require_relative "rest_framework/controller_mixins"
5
6
  require_relative "rest_framework/engine"
6
7
  require_relative "rest_framework/filters"
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
@@ -1,2 +1,6 @@
1
+ module RESTFramework::ControllerMixins
2
+ end
3
+
4
+
1
5
  require_relative 'controller_mixins/base'
2
6
  require_relative 'controller_mixins/models'
@@ -1,194 +1,180 @@
1
1
  require_relative '../serializers'
2
2
 
3
- module RESTFramework
4
-
5
- # This module provides the common functionality for any controller mixins, a `root` action, and
6
- # the ability to route arbitrary actions with `extra_actions`. This is also where `api_response`
7
- # is defined.
8
- module BaseControllerMixin
9
- # Default action for API root.
10
- def root
11
- api_response({message: "This is the root of your awesome API!"})
12
- end
13
3
 
14
- module ClassMethods
15
- def get_skip_actions(skip_undefined: true)
16
- # first, skip explicitly skipped actions
17
- skip = self.skip_actions || []
4
+ # This module provides the common functionality for any controller mixins, a `root` action, and
5
+ # the ability to route arbitrary actions with `extra_actions`. This is also where `api_response`
6
+ # is defined.
7
+ module RESTFramework::BaseControllerMixin
8
+ # Default action for API root.
9
+ def root
10
+ api_response({message: "This is the root of your awesome API!"})
11
+ end
18
12
 
19
- # now add methods which don't exist, since we don't want to route those
20
- if skip_undefined
21
- [:index, :new, :create, :show, :edit, :update, :destroy].each do |a|
22
- skip << a unless self.method_defined?(a)
23
- end
24
- end
13
+ module ClassMethods
14
+ # Helper to get the actions that should be skipped.
15
+ def get_skip_actions(skip_undefined: true)
16
+ # First, skip explicitly skipped actions.
17
+ skip = self.skip_actions || []
25
18
 
26
- return skip
19
+ # Now add methods which don't exist, since we don't want to route those.
20
+ if skip_undefined
21
+ [:index, :new, :create, :show, :edit, :update, :destroy].each do |a|
22
+ skip << a unless self.method_defined?(a)
23
+ end
27
24
  end
28
- end
29
25
 
30
- def self.included(base)
31
- if base.is_a? Class
32
- base.extend ClassMethods
33
-
34
- # Add class attributes (with defaults) unless they already exist.
35
- {
36
- extra_actions: nil,
37
- extra_member_actions: nil,
38
- filter_backends: nil,
39
- paginator_class: nil,
40
- page_size: nil,
41
- page_query_param: 'page',
42
- page_size_query_param: 'page_size',
43
- max_page_size: nil,
44
- serializer_class: nil,
45
- singleton_controller: nil,
46
- skip_actions: nil,
47
- }.each do |a, default|
48
- unless base.respond_to?(a)
49
- base.class_attribute(a)
50
-
51
- # Set default manually so we can still support Rails 4. Maybe later we can use the
52
- # default parameter on `class_attribute`.
53
- base.send(:"#{a}=", default)
54
- end
55
- end
26
+ return skip
27
+ end
28
+ end
56
29
 
57
- # Alias `extra_actions` to `extra_collection_actions`.
58
- unless base.respond_to?(:extra_collection_actions)
59
- base.alias_method(:extra_collection_actions, :extra_actions)
60
- base.alias_method(:extra_collection_actions=, :extra_actions=)
30
+ def self.included(base)
31
+ if base.is_a? Class
32
+ base.extend ClassMethods
33
+
34
+ # Add class attributes (with defaults) unless they already exist.
35
+ {
36
+ extra_actions: nil,
37
+ extra_member_actions: nil,
38
+ filter_backends: nil,
39
+ paginator_class: nil,
40
+ page_size: 20,
41
+ page_query_param: 'page',
42
+ page_size_query_param: 'page_size',
43
+ max_page_size: nil,
44
+ serializer_class: nil,
45
+ singleton_controller: nil,
46
+ skip_actions: nil,
47
+ }.each do |a, default|
48
+ unless base.respond_to?(a)
49
+ base.class_attribute(a)
50
+
51
+ # Set default manually so we can still support Rails 4. Maybe later we can use the
52
+ # default parameter on `class_attribute`.
53
+ base.send(:"#{a}=", default)
61
54
  end
55
+ end
62
56
 
63
- # skip csrf since this is an API
64
- base.skip_before_action(:verify_authenticity_token) rescue nil
65
-
66
- # handle some common exceptions
67
- base.rescue_from(ActiveRecord::RecordNotFound, with: :record_not_found)
68
- base.rescue_from(ActiveRecord::RecordInvalid, with: :record_invalid)
69
- base.rescue_from(ActiveRecord::RecordNotSaved, with: :record_not_saved)
70
- base.rescue_from(ActiveRecord::RecordNotDestroyed, with: :record_not_destroyed)
57
+ # Alias `extra_actions` to `extra_collection_actions`.
58
+ unless base.respond_to?(:extra_collection_actions)
59
+ base.alias_method(:extra_collection_actions, :extra_actions)
60
+ base.alias_method(:extra_collection_actions=, :extra_actions=)
71
61
  end
72
- end
73
62
 
74
- protected
63
+ # skip csrf since this is an API
64
+ base.skip_before_action(:verify_authenticity_token) rescue nil
75
65
 
76
- # Helper to get filtering backends with a sane default.
77
- def get_filter_backends
78
- return self.class.filter_backends || []
66
+ # handle some common exceptions
67
+ base.rescue_from(ActiveRecord::RecordNotFound, with: :record_not_found)
68
+ base.rescue_from(ActiveRecord::RecordInvalid, with: :record_invalid)
69
+ base.rescue_from(ActiveRecord::RecordNotSaved, with: :record_not_saved)
70
+ base.rescue_from(ActiveRecord::RecordNotDestroyed, with: :record_not_destroyed)
79
71
  end
72
+ end
80
73
 
81
- # Filter the recordset over all configured filter backends.
82
- def get_filtered_data(data)
83
- self.get_filter_backends.each do |filter_class|
84
- filter = filter_class.new(controller: self)
85
- data = filter.get_filtered_data(data)
86
- end
74
+ protected
87
75
 
88
- return data
89
- end
76
+ # Helper to get filtering backends with a sane default.
77
+ # @return [RESTFramework::BaseFilter]
78
+ def get_filter_backends
79
+ return self.class.filter_backends || []
80
+ end
90
81
 
91
- # Helper to get the configured serializer class.
92
- def get_serializer_class
93
- return self.class.serializer_class
82
+ # Helper to filter an arbitrary data set over all configured filter backends.
83
+ def get_filtered_data(data)
84
+ self.get_filter_backends.each do |filter_class|
85
+ filter = filter_class.new(controller: self)
86
+ data = filter.get_filtered_data(data)
94
87
  end
95
88
 
96
- # Get a native serializer config for the current action.
97
- def get_native_serializer_config
98
- action_serializer_config = self.class.native_serializer_action_config || {}
99
- action = self.action_name.to_sym
100
-
101
- # Handle case where :index action is not defined.
102
- if action == :index && !action_serializer_config.key?(:index)
103
- # Default is :show if `singleton_controller`, otherwise :list.
104
- action = self.class.singleton_controller ? :show : :list
105
- end
89
+ return data
90
+ end
106
91
 
107
- return (action_serializer_config[action] if action) || self.class.native_serializer_config
108
- end
92
+ # Helper to get the configured serializer class.
93
+ # @return [RESTFramework::BaseSerializer]
94
+ def get_serializer_class
95
+ return self.class.serializer_class
96
+ end
109
97
 
110
- def record_invalid(e)
111
- return api_response(
112
- {message: "Record invalid.", exception: e, errors: e.record.errors}, status: 400
113
- )
114
- end
98
+ def record_invalid(e)
99
+ return api_response(
100
+ {message: "Record invalid.", exception: e, errors: e.record.errors}, status: 400
101
+ )
102
+ end
115
103
 
116
- def record_not_found(e)
117
- return api_response({message: "Record not found.", exception: e}, status: 404)
118
- end
104
+ def record_not_found(e)
105
+ return api_response({message: "Record not found.", exception: e}, status: 404)
106
+ end
119
107
 
120
- def record_not_saved(e)
121
- return api_response({message: "Record not saved.", exception: e}, status: 406)
122
- end
108
+ def record_not_saved(e)
109
+ return api_response({message: "Record not saved.", exception: e}, status: 406)
110
+ end
123
111
 
124
- def record_not_destroyed(e)
125
- return api_response({message: "Record not destroyed.", exception: e}, status: 406)
126
- end
112
+ def record_not_destroyed(e)
113
+ return api_response({message: "Record not destroyed.", exception: e}, status: 406)
114
+ end
127
115
 
128
- # Helper for showing routes under a controller action, used for the browsable API.
129
- def _get_routes
130
- begin
131
- formatter = ActionDispatch::Routing::ConsoleFormatter::Sheet
132
- rescue NameError
133
- formatter = ActionDispatch::Routing::ConsoleFormatter
134
- end
135
- return ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes).format(
136
- formatter.new
137
- ).lines.drop(1).map { |r| r.split.last(3) }.map { |r|
138
- {verb: r[0], path: r[1], action: r[2]}
139
- }.select { |r| r[:path].start_with?(request.path) }
116
+ # Helper for showing routes under a controller action, used for the browsable API.
117
+ def _get_routes
118
+ begin
119
+ formatter = ActionDispatch::Routing::ConsoleFormatter::Sheet
120
+ rescue NameError
121
+ formatter = ActionDispatch::Routing::ConsoleFormatter
140
122
  end
123
+ return ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes).format(
124
+ formatter.new
125
+ ).lines.drop(1).map { |r| r.split.last(3) }.map { |r|
126
+ {verb: r[0], path: r[1], action: r[2]}
127
+ }.select { |r| r[:path].start_with?(request.path) }
128
+ end
141
129
 
142
- # Helper alias for `respond_to`/`render`. `payload` should be already serialized to Ruby
143
- # primitives.
144
- def api_response(payload, html_kwargs: nil, json_kwargs: nil, xml_kwargs: nil, **kwargs)
145
- html_kwargs ||= {}
146
- json_kwargs ||= {}
147
- xml_kwargs ||= {}
148
-
149
- # allow blank (no-content) responses
150
- @blank = kwargs[:blank]
151
-
152
- respond_to do |format|
153
- if @blank
154
- format.json {head :no_content}
155
- format.xml {head :no_content}
156
- else
157
- if payload.respond_to?(:to_json)
158
- format.json {
159
- kwargs = kwargs.merge(json_kwargs)
160
- render(json: payload, layout: false, **kwargs)
161
- }
162
- end
163
- if payload.respond_to?(:to_xml)
164
- format.xml {
165
- kwargs = kwargs.merge(xml_kwargs)
166
- render(xml: payload, layout: false, **kwargs)
167
- }
168
- end
130
+ # Helper to render a browsable API for `html` format, along with basic `json`/`xml` formats, and
131
+ # with support or passing custom `kwargs` to the underlying `render` calls.
132
+ def api_response(payload, html_kwargs: nil, json_kwargs: nil, xml_kwargs: nil, **kwargs)
133
+ html_kwargs ||= {}
134
+ json_kwargs ||= {}
135
+ xml_kwargs ||= {}
136
+
137
+ # allow blank (no-content) responses
138
+ @blank = kwargs[:blank]
139
+
140
+ respond_to do |format|
141
+ if @blank
142
+ format.json {head :no_content}
143
+ format.xml {head :no_content}
144
+ else
145
+ if payload.respond_to?(:to_json)
146
+ format.json {
147
+ jkwargs = kwargs.merge(json_kwargs)
148
+ render(json: payload, layout: false, **jkwargs)
149
+ }
150
+ end
151
+ if payload.respond_to?(:to_xml)
152
+ format.xml {
153
+ xkwargs = kwargs.merge(xml_kwargs)
154
+ render(xml: payload, layout: false, **xkwargs)
155
+ }
169
156
  end
170
- format.html {
171
- @payload = payload
172
- @json_payload = ''
173
- @xml_payload = ''
174
- unless @blank
175
- @json_payload = payload.to_json if payload.respond_to?(:to_json)
176
- @xml_payload = payload.to_xml if payload.respond_to?(:to_xml)
177
- end
178
- @template_logo_text ||= "Rails REST Framework"
179
- @title ||= self.controller_name.camelize
180
- @routes ||= self._get_routes
181
- kwargs = kwargs.merge(html_kwargs)
182
- begin
183
- render(**kwargs)
184
- rescue ActionView::MissingTemplate # fallback to rest_framework layout/view
185
- kwargs[:layout] = "rest_framework"
186
- kwargs[:template] = "rest_framework/default"
187
- render(**kwargs)
188
- end
189
- }
190
157
  end
158
+ format.html {
159
+ @payload = payload
160
+ @json_payload = ''
161
+ @xml_payload = ''
162
+ unless @blank
163
+ @json_payload = payload.to_json if payload.respond_to?(:to_json)
164
+ @xml_payload = payload.to_xml if payload.respond_to?(:to_xml)
165
+ end
166
+ @template_logo_text ||= "Rails REST Framework"
167
+ @title ||= self.controller_name.camelize
168
+ @routes ||= self._get_routes
169
+ hkwargs = kwargs.merge(html_kwargs)
170
+ begin
171
+ render(**hkwargs)
172
+ rescue ActionView::MissingTemplate # fallback to rest_framework layout/view
173
+ hkwargs[:layout] = "rest_framework"
174
+ hkwargs[:template] = "rest_framework/default"
175
+ render(**hkwargs)
176
+ end
177
+ }
191
178
  end
192
179
  end
193
-
194
180
  end
@@ -1,228 +1,257 @@
1
1
  require_relative 'base'
2
2
  require_relative '../filters'
3
3
 
4
- module RESTFramework
5
-
6
- module BaseModelControllerMixin
7
- include BaseControllerMixin
8
- def self.included(base)
9
- if base.is_a? Class
10
- BaseControllerMixin.included(base)
11
-
12
- # Add class attributes (with defaults) unless they already exist.
13
- {
14
- # Core attributes related to models.
15
- model: nil,
16
- recordset: nil,
17
-
18
- # Attributes for create/update parameters.
19
- allowed_parameters: nil,
20
- allowed_action_parameters: nil,
21
-
22
- # Attributes for configuring record fields.
23
- fields: nil,
24
- action_fields: nil,
25
-
26
- # Attributes for the default native serializer.
27
- native_serializer_config: nil,
28
- native_serializer_action_config: nil,
29
-
30
- # Attributes for default model filtering (and ordering).
31
- filterset_fields: nil,
32
- ordering_fields: nil,
33
- ordering_query_param: 'ordering',
34
-
35
- # Other misc attributes.
36
- disable_creation_from_recordset: nil, # Option to disable `recordset.create` behavior.
37
- }.each do |a, default|
38
- unless base.respond_to?(a)
39
- base.class_attribute(a)
40
-
41
- # Set default manually so we can still support Rails 4. Maybe later we can use the
42
- # default parameter on `class_attribute`.
43
- base.send(:"#{a}=", default)
44
- end
4
+
5
+ # This module provides the core functionality for controllers based on models.
6
+ module RESTFramework::BaseModelControllerMixin
7
+ include RESTFramework::BaseControllerMixin
8
+
9
+ def self.included(base)
10
+ if base.is_a? Class
11
+ RESTFramework::BaseControllerMixin.included(base)
12
+
13
+ # Add class attributes (with defaults) unless they already exist.
14
+ {
15
+ # Core attributes related to models.
16
+ model: nil,
17
+ recordset: nil,
18
+
19
+ # Attributes for create/update parameters.
20
+ allowed_parameters: nil,
21
+ allowed_action_parameters: nil,
22
+
23
+ # Attributes for configuring record fields.
24
+ fields: nil,
25
+ action_fields: nil,
26
+
27
+ # Attributes for the default native serializer.
28
+ native_serializer_config: nil,
29
+ native_serializer_action_config: nil,
30
+
31
+ # Attributes for default model filtering (and ordering).
32
+ filterset_fields: nil,
33
+ ordering_fields: nil,
34
+ ordering_query_param: 'ordering',
35
+
36
+ # Other misc attributes.
37
+ disable_creation_from_recordset: nil, # Option to disable `recordset.create` behavior.
38
+ }.each do |a, default|
39
+ unless base.respond_to?(a)
40
+ base.class_attribute(a)
41
+
42
+ # Set default manually so we can still support Rails 4. Maybe later we can use the
43
+ # default parameter on `class_attribute`.
44
+ base.send(:"#{a}=", default)
45
45
  end
46
46
  end
47
47
  end
48
+ end
48
49
 
49
- protected
50
+ protected
50
51
 
51
- # Helper to get the configured serializer class, or `NativeModelSerializer` as a default.
52
- def get_serializer_class
53
- return self.class.serializer_class || RESTFramework::NativeModelSerializer
52
+ # Get a native serializer config for the current action.
53
+ # @return [RESTFramework::NativeModelSerializer]
54
+ def get_native_serializer_config
55
+ action_serializer_config = self.class.native_serializer_action_config || {}
56
+ action = self.action_name.to_sym
57
+
58
+ # Handle case where :index action is not defined.
59
+ if action == :index && !action_serializer_config.key?(:index)
60
+ # Default is :show if `singleton_controller`, otherwise :list.
61
+ action = self.class.singleton_controller ? :show : :list
54
62
  end
55
63
 
56
- # Get a list of parameters allowed for the current action.
57
- def get_allowed_parameters
58
- allowed_action_parameters = self.class.allowed_action_parameters || {}
59
- action = self.action_name.to_sym
64
+ return (action_serializer_config[action] if action) || self.class.native_serializer_config
65
+ end
60
66
 
61
- # index action should use :list allowed parameters if :index is not provided
62
- action = :list if action == :index && !allowed_action_parameters.key?(:index)
67
+ # Helper to get the configured serializer class, or `NativeModelSerializer` as a default.
68
+ # @return [RESTFramework::BaseSerializer]
69
+ def get_serializer_class
70
+ return self.class.serializer_class || RESTFramework::NativeModelSerializer
71
+ end
63
72
 
64
- return (allowed_action_parameters[action] if action) || self.class.allowed_parameters
65
- end
73
+ # Get a list of parameters allowed for the current action.
74
+ def get_allowed_parameters
75
+ allowed_action_parameters = self.class.allowed_action_parameters || {}
76
+ action = self.action_name.to_sym
66
77
 
67
- # Get the list of filtering backends to use.
68
- def get_filter_backends
69
- return self.class.filter_backends || [
70
- RESTFramework::ModelFilter, RESTFramework::ModelOrderingFilter
71
- ]
72
- end
78
+ # index action should use :list allowed parameters if :index is not provided
79
+ action = :list if action == :index && !allowed_action_parameters.key?(:index)
73
80
 
74
- # Get a list of fields for the current action.
75
- def get_fields
76
- action_fields = self.class.action_fields || {}
77
- action = self.action_name.to_sym
81
+ return (allowed_action_parameters[action] if action) || self.class.allowed_parameters
82
+ end
78
83
 
79
- # index action should use :list fields if :index is not provided
80
- action = :list if action == :index && !action_fields.key?(:index)
84
+ # Get the list of filtering backends to use.
85
+ # @return [RESTFramework::BaseFilter]
86
+ def get_filter_backends
87
+ return self.class.filter_backends || [
88
+ RESTFramework::ModelFilter, RESTFramework::ModelOrderingFilter
89
+ ]
90
+ end
81
91
 
82
- return (action_fields[action] if action) || self.class.fields || []
83
- end
92
+ # Get a list of fields for the current action.
93
+ def get_fields
94
+ action_fields = self.class.action_fields || {}
95
+ action = self.action_name.to_sym
84
96
 
85
- # Filter the request body for keys in current action's allowed_parameters/fields config.
86
- def get_body_params
87
- fields = self.get_allowed_parameters || self.get_fields
88
- return @get_body_params ||= (request.request_parameters.select { |p|
89
- fields.include?(p.to_sym) || fields.include?(p.to_s)
90
- })
91
- end
92
- alias :get_create_params :get_body_params
93
- alias :get_update_params :get_body_params
94
-
95
- # Get a record by `id` or return a single record if recordset is filtered down to a single
96
- # record.
97
- def get_record
98
- records = self.get_filtered_data(self.get_recordset)
99
- if params['id'] # direct lookup
100
- return records.find(params['id'])
101
- elsif records.length == 1
102
- return records[0]
103
- end
104
- return nil
105
- end
97
+ # index action should use :list fields if :index is not provided
98
+ action = :list if action == :index && !action_fields.key?(:index)
106
99
 
107
- # Internal interface for get_model, protecting against infinite recursion with get_recordset.
108
- def _get_model(from_internal_get_recordset: false)
109
- return @model if instance_variable_defined?(:@model) && @model
110
- return self.class.model if self.class.model
111
- unless from_internal_get_recordset # prevent infinite recursion
112
- recordset = self._get_recordset(from_internal_get_model: true)
113
- return (@model = recordset.klass) if recordset
114
- end
115
- begin
116
- return (@model = self.class.name.demodulize.match(/(.*)Controller/)[1].singularize.constantize)
117
- rescue NameError
118
- end
119
- return nil
120
- end
100
+ return (action_fields[action] if action) || self.class.fields || []
101
+ end
121
102
 
122
- # Internal interface for get_recordset, protecting against infinite recursion with get_model.
123
- def _get_recordset(from_internal_get_model: false)
124
- return @recordset if instance_variable_defined?(:@recordset) && @recordset
125
- return self.class.recordset if self.class.recordset
126
- unless from_internal_get_model # prevent infinite recursion
127
- model = self._get_model(from_internal_get_recordset: true)
128
- return (@recordset = model.all) if model
129
- end
130
- return nil
103
+ # Filter the request body for keys in current action's allowed_parameters/fields config.
104
+ def get_body_params
105
+ fields = self.get_allowed_parameters || self.get_fields
106
+ return @get_body_params ||= (request.request_parameters.select { |p|
107
+ fields.include?(p.to_sym) || fields.include?(p.to_s)
108
+ })
109
+ end
110
+ alias :get_create_params :get_body_params
111
+ alias :get_update_params :get_body_params
112
+
113
+ # Get a record by the primary key from the filtered recordset.
114
+ def get_record
115
+ records = self.get_filtered_data(self.get_recordset)
116
+ if pk = params[self.model.primary_key]
117
+ return records.find(pk)
131
118
  end
119
+ return nil
120
+ end
132
121
 
133
- # Get the model for this controller.
134
- def get_model
135
- return _get_model
122
+ # Internal interface for get_model, protecting against infinite recursion with get_recordset.
123
+ def _get_model(from_internal_get_recordset: false)
124
+ return @model if instance_variable_defined?(:@model) && @model
125
+ return self.class.model if self.class.model
126
+ unless from_internal_get_recordset # prevent infinite recursion
127
+ recordset = self._get_recordset(from_internal_get_model: true)
128
+ return (@model = recordset.klass) if recordset
136
129
  end
137
-
138
- # Get the set of records this controller has access to.
139
- def get_recordset
140
- return _get_recordset
130
+ begin
131
+ return (@model = self.class.name.demodulize.match(/(.*)Controller/)[1].singularize.constantize)
132
+ rescue NameError
141
133
  end
134
+ return nil
142
135
  end
143
136
 
144
- module ListModelMixin
145
- def index
146
- @records = self.get_filtered_data(self.get_recordset)
137
+ # Internal interface for get_recordset, protecting against infinite recursion with get_model.
138
+ def _get_recordset(from_internal_get_model: false)
139
+ return @recordset if instance_variable_defined?(:@recordset) && @recordset
140
+ return self.class.recordset if self.class.recordset
141
+ unless from_internal_get_model # prevent infinite recursion
142
+ model = self._get_model(from_internal_get_recordset: true)
143
+ return (@recordset = model.all) if model
144
+ end
145
+ return nil
146
+ end
147
147
 
148
- # Handle pagination, if enabled.
149
- if self.class.paginator_class
150
- paginator = self.class.paginator_class.new(data: @records, controller: self)
151
- page = paginator.get_page
152
- serialized_page = self.get_serializer_class.new(object: page, controller: self).serialize
153
- data = paginator.get_paginated_response(serialized_page)
154
- else
155
- data = self.get_serializer_class.new(object: @records, controller: self).serialize
156
- end
148
+ # Get the model for this controller.
149
+ def get_model
150
+ return _get_model
151
+ end
157
152
 
158
- return api_response(data)
159
- end
153
+ # Get the set of records this controller has access to.
154
+ def get_recordset
155
+ return _get_recordset
160
156
  end
157
+ end
158
+
161
159
 
162
- module ShowModelMixin
163
- def show
164
- @record = self.get_record
165
- serialized_record = self.get_serializer_class.new(object: @record, controller: self).serialize
166
- return api_response(serialized_record)
160
+ # Mixin for listing records.
161
+ module RESTFramework::ListModelMixin
162
+ def index
163
+ @records = self.get_filtered_data(self.get_recordset)
164
+
165
+ # Handle pagination, if enabled.
166
+ if self.class.paginator_class
167
+ paginator = self.class.paginator_class.new(data: @records, controller: self)
168
+ page = paginator.get_page
169
+ serialized_page = self.get_serializer_class.new(object: page, controller: self).serialize
170
+ data = paginator.get_paginated_response(serialized_page)
171
+ else
172
+ data = self.get_serializer_class.new(object: @records, controller: self).serialize
167
173
  end
174
+
175
+ return api_response(data)
168
176
  end
177
+ end
169
178
 
170
- module CreateModelMixin
171
- def create
172
- if self.get_recordset.respond_to?(:create!) && !self.disable_creation_from_recordset
173
- # Create with any properties inherited from the recordset (like associations).
174
- @record = self.get_recordset.create!(self.get_create_params)
175
- else
176
- # Otherwise, perform a "bare" create.
177
- @record = self.get_model.create!(self.get_create_params)
178
- end
179
- serialized_record = self.get_serializer_class.new(object: @record, controller: self).serialize
180
- return api_response(serialized_record)
181
- end
179
+
180
+ # Mixin for showing records.
181
+ module RESTFramework::ShowModelMixin
182
+ def show
183
+ @record = self.get_record
184
+ serialized_record = self.get_serializer_class.new(object: @record, controller: self).serialize
185
+ return api_response(serialized_record)
182
186
  end
187
+ end
188
+
183
189
 
184
- module UpdateModelMixin
185
- def update
186
- @record = self.get_record
187
- @record.update!(self.get_update_params)
188
- serialized_record = self.get_serializer_class.new(object: @record, controller: self).serialize
189
- return api_response(serialized_record)
190
+ # Mixin for creating records.
191
+ module RESTFramework::CreateModelMixin
192
+ def create
193
+ if self.get_recordset.respond_to?(:create!) && !self.disable_creation_from_recordset
194
+ # Create with any properties inherited from the recordset (like associations).
195
+ @record = self.get_recordset.create!(self.get_create_params)
196
+ else
197
+ # Otherwise, perform a "bare" create.
198
+ @record = self.get_model.create!(self.get_create_params)
190
199
  end
200
+ serialized_record = self.get_serializer_class.new(object: @record, controller: self).serialize
201
+ return api_response(serialized_record)
191
202
  end
203
+ end
192
204
 
193
- module DestroyModelMixin
194
- def destroy
195
- @record = self.get_record
196
- @record.destroy!
197
- api_response(nil)
198
- end
205
+
206
+ # Mixin for updating records.
207
+ module RESTFramework::UpdateModelMixin
208
+ def update
209
+ @record = self.get_record
210
+ @record.update!(self.get_update_params)
211
+ serialized_record = self.get_serializer_class.new(object: @record, controller: self).serialize
212
+ return api_response(serialized_record)
199
213
  end
214
+ end
200
215
 
201
- module ReadOnlyModelControllerMixin
202
- include BaseModelControllerMixin
203
- def self.included(base)
204
- if base.is_a? Class
205
- BaseModelControllerMixin.included(base)
206
- end
207
- end
208
216
 
209
- include ListModelMixin
210
- include ShowModelMixin
217
+ # Mixin for destroying records.
218
+ module RESTFramework::DestroyModelMixin
219
+ def destroy
220
+ @record = self.get_record
221
+ @record.destroy!
222
+ api_response(nil)
211
223
  end
224
+ end
212
225
 
213
- module ModelControllerMixin
214
- include BaseModelControllerMixin
215
- def self.included(base)
216
- if base.is_a? Class
217
- BaseModelControllerMixin.included(base)
218
- end
226
+
227
+ # Mixin that includes show/list mixins.
228
+ module RESTFramework::ReadOnlyModelControllerMixin
229
+ include RESTFramework::BaseModelControllerMixin
230
+
231
+ def self.included(base)
232
+ if base.is_a? Class
233
+ RESTFramework::BaseModelControllerMixin.included(base)
219
234
  end
235
+ end
236
+
237
+ include RESTFramework::ListModelMixin
238
+ include RESTFramework::ShowModelMixin
239
+ end
220
240
 
221
- include ListModelMixin
222
- include ShowModelMixin
223
- include CreateModelMixin
224
- include UpdateModelMixin
225
- include DestroyModelMixin
241
+
242
+ # Mixin that includes all the CRUD mixins.
243
+ module RESTFramework::ModelControllerMixin
244
+ include RESTFramework::BaseModelControllerMixin
245
+
246
+ def self.included(base)
247
+ if base.is_a? Class
248
+ RESTFramework::BaseModelControllerMixin.included(base)
249
+ end
226
250
  end
227
251
 
252
+ include RESTFramework::ListModelMixin
253
+ include RESTFramework::ShowModelMixin
254
+ include RESTFramework::CreateModelMixin
255
+ include RESTFramework::UpdateModelMixin
256
+ include RESTFramework::DestroyModelMixin
228
257
  end