rest_framework 0.1.0 → 0.1.1

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: 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