rest_framework 0.1.3 → 0.2.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: 96d6394bfb8dee90ee31a05289a86db5409a118a3d456b9bf927397ceea055a7
4
- data.tar.gz: 6dc5181f02dafae6d0831a7802480375097836eb3e3e799c78076d0331a2f728
3
+ metadata.gz: 8ef7499f4d1e10af1f2520d3b9847dc7f711bd1c7f4377efe21bd5b60c0dd9a7
4
+ data.tar.gz: b15a9b0abee58e315fdc3a1157ca04d7051630821d3ed2ca691564966b5fefca
5
5
  SHA512:
6
- metadata.gz: e811d984f82150ad789d1e3437fc178095dcaffa8270b15b96dd375d7dbf649e0983f8001a4fe09c4ea4abe89ba8c8cad9e44ae25c21bbecb65adf5542efd8fc
7
- data.tar.gz: 1d64e212a2bb7331cba06020e9a9405b54ab558e691f4ef349ffc156a702f352209108fc62265b2ff8369a15656bdc4b158db13201b630d0ae3e2853e3b7c553
6
+ metadata.gz: fb586a083989a67f206d5b5d6c14eb546949569e384d3b44c870de5f49f610b5848d53da87e01234542705304dd7d49bcf6f0037f0b89331c7fa2a1b084422ce
7
+ data.tar.gz: fe4c960cda0d2a9ba43cb7d5b1cfa84963802800829323c26b350841bbd34290e68cbbc4f14a3138a1cb95a28aafff76b2d9a78a41772950e00ad80159c8f2f3
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
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
+ [![Coverage Status](https://coveralls.io/repos/github/gregschmit/rails-rest-framework/badge.svg?branch=master)](https://coveralls.io/github/gregschmit/rails-rest-framework?branch=master)
5
6
 
6
7
  A framework for DRY RESTful APIs in Ruby on Rails.
7
8
 
@@ -78,8 +79,8 @@ class Api::ReadOnlyMoviesController < ApiController
78
79
  end
79
80
  ```
80
81
 
81
- Note that you can also override `get_model` and `get_recordset` instance methods to override the API
82
- behavior dynamically per-request.
82
+ Note that you can also override the `get_recordset` instance method to override the API behavior
83
+ dynamically per-request.
83
84
 
84
85
  ### Routing
85
86
 
@@ -4,8 +4,10 @@ end
4
4
 
5
5
  require_relative "rest_framework/controller_mixins"
6
6
  require_relative "rest_framework/engine"
7
+ require_relative "rest_framework/errors"
7
8
  require_relative "rest_framework/filters"
8
9
  require_relative "rest_framework/paginators"
9
10
  require_relative "rest_framework/routers"
10
11
  require_relative "rest_framework/serializers"
11
12
  require_relative "rest_framework/version"
13
+ require_relative "rest_framework/generators"
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.2.0
@@ -1,3 +1,4 @@
1
+ require_relative '../errors'
1
2
  require_relative '../serializers'
2
3
 
3
4
 
@@ -33,6 +34,9 @@ module RESTFramework::BaseControllerMixin
33
34
 
34
35
  # Add class attributes (with defaults) unless they already exist.
35
36
  {
37
+ filter_pk_from_request_body: true,
38
+ exclude_body_fields: [:created_at, :created_by, :updated_at, :updated_by],
39
+ accept_generic_params_as_body_params: true,
36
40
  extra_actions: nil,
37
41
  extra_member_actions: nil,
38
42
  filter_backends: nil,
@@ -42,14 +46,16 @@ module RESTFramework::BaseControllerMixin
42
46
  page_size_query_param: 'page_size',
43
47
  max_page_size: nil,
44
48
  serializer_class: nil,
49
+ serialize_to_json: true,
50
+ serialize_to_xml: true,
45
51
  singleton_controller: nil,
46
52
  skip_actions: nil,
47
53
  }.each do |a, default|
48
54
  unless base.respond_to?(a)
49
55
  base.class_attribute(a)
50
56
 
51
- # Set default manually so we can still support Rails 4. Maybe later we can use the
52
- # default parameter on `class_attribute`.
57
+ # Set default manually so we can still support Rails 4. Maybe later we can use the default
58
+ # parameter on `class_attribute`.
53
59
  base.send(:"#{a}=", default)
54
60
  end
55
61
  end
@@ -60,10 +66,10 @@ module RESTFramework::BaseControllerMixin
60
66
  base.alias_method(:extra_collection_actions=, :extra_actions=)
61
67
  end
62
68
 
63
- # skip csrf since this is an API
69
+ # Skip csrf since this is an API.
64
70
  base.skip_before_action(:verify_authenticity_token) rescue nil
65
71
 
66
- # handle some common exceptions
72
+ # Handle some common exceptions.
67
73
  base.rescue_from(ActiveRecord::RecordNotFound, with: :record_not_found)
68
74
  base.rescue_from(ActiveRecord::RecordInvalid, with: :record_invalid)
69
75
  base.rescue_from(ActiveRecord::RecordNotSaved, with: :record_not_saved)
@@ -129,39 +135,42 @@ module RESTFramework::BaseControllerMixin
129
135
 
130
136
  # Helper to render a browsable API for `html` format, along with basic `json`/`xml` formats, and
131
137
  # 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)
138
+ def api_response(payload, html_kwargs: nil, **kwargs)
133
139
  html_kwargs ||= {}
134
- json_kwargs ||= {}
135
- xml_kwargs ||= {}
136
-
137
- # allow blank (no-content) responses
138
- @blank = kwargs[:blank]
140
+ json_kwargs = kwargs.delete(:json_kwargs) || {}
141
+ xml_kwargs = kwargs.delete(:xml_kwargs) || {}
142
+
143
+ # Raise helpful error if payload is nil. Usually this happens when a record is not found (e.g.,
144
+ # when passing something like `User.find_by(id: some_id)` to `api_response`). The caller should
145
+ # actually be calling `find_by!` to raise ActiveRecord::RecordNotFound and allowing the REST
146
+ # framework to catch this error and return an appropriate error response.
147
+ if payload.nil?
148
+ raise RESTFramework::NilPassedToAPIResponseError
149
+ end
139
150
 
140
151
  respond_to do |format|
141
- if @blank
142
- format.json {head :no_content}
143
- format.xml {head :no_content}
152
+ if payload == ''
153
+ format.json {head :no_content} if self.serialize_to_json
154
+ format.xml {head :no_content} if self.serialize_to_xml
144
155
  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
- }
156
- end
156
+ format.json {
157
+ jkwargs = kwargs.merge(json_kwargs)
158
+ render(json: payload, layout: false, **jkwargs)
159
+ } if self.serialize_to_json
160
+ format.xml {
161
+ xkwargs = kwargs.merge(xml_kwargs)
162
+ render(xml: payload, layout: false, **xkwargs)
163
+ } if self.serialize_to_xml
164
+ # TODO: possibly support more formats here if supported?
157
165
  end
158
166
  format.html {
159
167
  @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)
168
+ if payload == ''
169
+ @json_payload = '' if self.serialize_to_json
170
+ @xml_payload = '' if self.serialize_to_xml
171
+ else
172
+ @json_payload = payload.to_json if self.serialize_to_json
173
+ @xml_payload = payload.to_xml if self.serialize_to_xml
165
174
  end
166
175
  @template_logo_text ||= "Rails REST Framework"
167
176
  @title ||= self.controller_name.camelize
@@ -16,17 +16,18 @@ module RESTFramework::BaseModelControllerMixin
16
16
  model: nil,
17
17
  recordset: nil,
18
18
 
19
- # Attributes for create/update parameters.
20
- allowed_parameters: nil,
21
- allowed_action_parameters: nil,
22
-
23
19
  # Attributes for configuring record fields.
24
20
  fields: nil,
25
21
  action_fields: nil,
26
22
 
23
+ # Attributes for create/update parameters.
24
+ allowed_parameters: nil,
25
+ allowed_action_parameters: nil,
26
+
27
27
  # Attributes for the default native serializer.
28
28
  native_serializer_config: nil,
29
- native_serializer_action_config: nil,
29
+ native_serializer_singular_config: nil,
30
+ native_serializer_plural_config: nil,
30
31
 
31
32
  # Attributes for default model filtering (and ordering).
32
33
  filterset_fields: nil,
@@ -34,13 +35,13 @@ module RESTFramework::BaseModelControllerMixin
34
35
  ordering_query_param: 'ordering',
35
36
 
36
37
  # Other misc attributes.
37
- disable_creation_from_recordset: nil, # Option to disable `recordset.create` behavior.
38
+ disable_creation_from_recordset: false, # Option to disable `recordset.create` behavior.
38
39
  }.each do |a, default|
39
40
  unless base.respond_to?(a)
40
41
  base.class_attribute(a)
41
42
 
42
- # Set default manually so we can still support Rails 4. Maybe later we can use the
43
- # default parameter on `class_attribute`.
43
+ # Set default manually so we can still support Rails 4. Maybe later we can use the default
44
+ # parameter on `class_attribute`.
44
45
  base.send(:"#{a}=", default)
45
46
  end
46
47
  end
@@ -49,36 +50,34 @@ module RESTFramework::BaseModelControllerMixin
49
50
 
50
51
  protected
51
52
 
52
- # Get a native serializer config for the current action.
53
- # @return [RESTFramework::NativeSerializer]
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
62
- end
53
+ def _get_specific_action_config(action_config_key, generic_config_key)
54
+ action_config = self.class.send(action_config_key) || {}
55
+ action = self.action_name&.to_sym
63
56
 
64
- return (action_serializer_config[action] if action) || self.class.native_serializer_config
65
- end
57
+ # Index action should use :list serializer if :index is not provided.
58
+ action = :list if action == :index && !action_config.key?(:index)
66
59
 
67
- # Helper to get the configured serializer class, or `NativeSerializer` as a default.
68
- # @return [RESTFramework::BaseSerializer]
69
- def get_serializer_class
70
- return self.class.serializer_class || RESTFramework::NativeSerializer
60
+ return (action_config[action] if action) || self.class.send(generic_config_key)
71
61
  end
72
62
 
73
63
  # Get a list of parameters allowed for the current action.
74
64
  def get_allowed_parameters
75
- allowed_action_parameters = self.class.allowed_action_parameters || {}
76
- action = self.action_name.to_sym
65
+ return _get_specific_action_config(:allowed_action_parameters, :allowed_parameters)&.map(&:to_s)
66
+ end
77
67
 
78
- # index action should use :list allowed parameters if :index is not provided
79
- action = :list if action == :index && !allowed_action_parameters.key?(:index)
68
+ # Get a list of fields for the current action.
69
+ def get_fields
70
+ return (
71
+ _get_specific_action_config(:action_fields, :fields)&.map(&:to_s) ||
72
+ self.get_model&.column_names ||
73
+ []
74
+ )
75
+ end
80
76
 
81
- return (allowed_action_parameters[action] if action) || self.class.allowed_parameters
77
+ # Helper to get the configured serializer class, or `NativeSerializer` as a default.
78
+ # @return [RESTFramework::BaseSerializer]
79
+ def get_serializer_class
80
+ return self.class.serializer_class || RESTFramework::NativeSerializer
82
81
  end
83
82
 
84
83
  # Get the list of filtering backends to use.
@@ -89,70 +88,81 @@ module RESTFramework::BaseModelControllerMixin
89
88
  ]
90
89
  end
91
90
 
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
91
+ # Filter the request body for keys in current action's allowed_parameters/fields config.
92
+ def get_body_params
93
+ return @_get_body_params ||= begin
94
+ fields = self.get_allowed_parameters || self.get_fields
95
+
96
+ # Filter the request body.
97
+ body_params = request.request_parameters.select { |p|
98
+ fields.include?(p)
99
+ }
100
+
101
+ # Add query params in place of missing body params, if configured.
102
+ if self.class.accept_generic_params_as_body_params
103
+ (fields - body_params.keys).each do |k|
104
+ if (value = params[k])
105
+ body_params[k] = value
106
+ end
107
+ end
108
+ end
96
109
 
97
- # index action should use :list fields if :index is not provided
98
- action = :list if action == :index && !action_fields.key?(:index)
110
+ # Filter primary key if configured.
111
+ if self.class.filter_pk_from_request_body
112
+ body_params.delete(self.get_model&.primary_key)
113
+ end
99
114
 
100
- return (action_fields[action] if action) || self.class.fields || []
101
- end
115
+ # Filter fields in exclude_body_fields.
116
+ (self.class.exclude_body_fields || []).each do |f|
117
+ body_params.delete(f.to_s)
118
+ end
102
119
 
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
- })
120
+ body_params
121
+ end
109
122
  end
110
123
  alias :get_create_params :get_body_params
111
124
  alias :get_update_params :get_body_params
112
125
 
113
- # Get a record by the primary key from the filtered recordset.
126
+ # Get a record by the primary key from the (non-filtered) recordset.
114
127
  def get_record
115
- records = self.get_filtered_data(self.get_recordset)
116
128
  if pk = params[self.get_model.primary_key]
117
- return records.find(pk)
129
+ return self.get_recordset.find(pk)
118
130
  end
119
131
  return nil
120
132
  end
121
133
 
122
- # Internal interface for get_model, protecting against infinite recursion with get_recordset.
123
- def _get_model(from_internal_get_recordset: false)
134
+ # Get the model for this controller.
135
+ def get_model(from_get_recordset: false)
124
136
  return @model if instance_variable_defined?(:@model) && @model
125
137
  return (@model = 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
138
+
139
+ # Delegate to the recordset's model, if it's defined.
140
+ unless from_get_recordset # prevent infinite recursion
141
+ if (recordset = self.get_recordset)
142
+ return @model = recordset.klass
143
+ end
129
144
  end
145
+
146
+ # Try to determine model from controller name.
130
147
  begin
131
148
  return (@model = self.class.name.demodulize.match(/(.*)Controller/)[1].singularize.constantize)
132
149
  rescue NameError
133
150
  end
151
+
134
152
  return nil
135
153
  end
136
154
 
137
- # Internal interface for get_recordset, protecting against infinite recursion with get_model.
138
- def _get_recordset(from_internal_get_model: false)
155
+ # Get the set of records this controller has access to.
156
+ def get_recordset
139
157
  return @recordset if instance_variable_defined?(:@recordset) && @recordset
140
158
  return (@recordset = 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
159
 
148
- # Get the model for this controller.
149
- def get_model
150
- return _get_model
151
- end
160
+ # If there is a model, return that model's default scope (all records by default).
161
+ if (model = self.get_model(from_get_recordset: true))
162
+ return @recordset = model.all
163
+ end
152
164
 
153
- # Get the set of records this controller has access to.
154
- def get_recordset
155
- return _get_recordset
165
+ return nil
156
166
  end
157
167
  end
158
168
 
@@ -219,7 +229,7 @@ module RESTFramework::DestroyModelMixin
219
229
  def destroy
220
230
  @record = self.get_record
221
231
  @record.destroy!
222
- api_response(nil)
232
+ api_response('')
223
233
  end
224
234
  end
225
235
 
@@ -0,0 +1,26 @@
1
+ # Top-level class for all REST Framework errors.
2
+ class RESTFramework::Error < StandardError
3
+ end
4
+
5
+ class RESTFramework::NilPassedToAPIResponseError < RESTFramework::Error
6
+ def message
7
+ return <<~MSG.split("\n").join(' ')
8
+ Payload of `nil` was passed to `api_response`; this is unsupported. If you want a blank
9
+ response, pass `''` (an empty string) as the payload. If this was the result of a `find_by`
10
+ (or similar Active Record method) not finding a record, you should use the bang version (e.g.,
11
+ `find_by!`) to raise `ActiveRecord::RecordNotFound`, which the REST controller will catch and
12
+ return an appropriate error response.
13
+ MSG
14
+ end
15
+ end
16
+
17
+ class RESTFramework::UnserializableError < RESTFramework::Error
18
+ def initialize(object)
19
+ @object = object
20
+ return super
21
+ end
22
+
23
+ def message
24
+ return "Unable to serialize `#{@object.inspect}` (of type `#{@object.class}`)."
25
+ end
26
+ end
@@ -14,10 +14,10 @@ end
14
14
  class RESTFramework::ModelFilter < RESTFramework::BaseFilter
15
15
  # Filter params for keys allowed by the current action's filterset_fields/fields config.
16
16
  def _get_filter_params
17
- fields = @controller.class.filterset_fields || @controller.send(:get_fields)
17
+ fields = @controller.class.filterset_fields&.map(&:to_s) || @controller.send(:get_fields)
18
18
  return @controller.request.query_parameters.select { |p|
19
- fields.include?(p.to_sym) || fields.include?(p.to_s)
20
- }.to_hash.symbolize_keys
19
+ fields.include?(p)
20
+ }.to_h.symbolize_keys # convert from HashWithIndifferentAccess to Hash w/keys
21
21
  end
22
22
 
23
23
  # Filter data according to the request query parameters.
@@ -0,0 +1,5 @@
1
+ module RESTFramework::Generators
2
+ end
3
+
4
+
5
+ require_relative 'generators/controller_generator'
@@ -0,0 +1,26 @@
1
+ require 'rails/generators'
2
+
3
+
4
+ class RESTFramework::Generators::ControllerGenerator < Rails::Generators::Base
5
+ desc <<~END
6
+ Description:
7
+ Stubs out a active_scaffolded controller. Pass the model name,
8
+ either CamelCased or under_scored.
9
+ The controller name is retrieved as a pluralized version of the model
10
+ name.
11
+ To create a controller within a module, specify the model name as a
12
+ path like 'parent_module/controller_name'.
13
+ This generates a controller class in app/controllers and invokes helper,
14
+ template engine and test framework generators.
15
+ Example:
16
+ `rails generate rest_framework:controller CreditCard`
17
+ Credit card controller with URLs like /credit_card/debit.
18
+ Controller: app/controllers/credit_cards_controller.rb
19
+ Functional Test: test/functional/credit_cards_controller_test.rb
20
+ Helper: app/helpers/credit_cards_helper.rb
21
+ END
22
+
23
+ def create_controller_file
24
+ create_file "app/controllers/some_controller.rb", "# Add initialization content here"
25
+ end
26
+ end
@@ -1,5 +1,6 @@
1
1
  require 'action_dispatch/routing/mapper'
2
2
 
3
+
3
4
  module ActionDispatch::Routing
4
5
  class Mapper
5
6
  # Internal helper to take extra_actions hash and convert to a consistent format.
@@ -1,6 +1,4 @@
1
1
  class RESTFramework::BaseSerializer
2
- attr_reader :errors
3
-
4
2
  def initialize(object: nil, controller: nil, **kwargs)
5
3
  @object = object
6
4
  @controller = controller
@@ -8,9 +6,8 @@ class RESTFramework::BaseSerializer
8
6
  end
9
7
 
10
8
 
11
- # This serializer uses `.as_json` to serialize objects. Despite the name, `.as_json` is an
12
- # `ActiveModel` method which converts objects to Ruby primitives (with the top-level being either
13
- # an array or a hash).
9
+ # This serializer uses `.serializable_hash` to convert objects to Ruby primitives (with the
10
+ # top-level being either an array or a hash).
14
11
  class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
15
12
  class_attribute :config
16
13
  class_attribute :singular_config
@@ -21,12 +18,17 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
21
18
  super(**kwargs)
22
19
 
23
20
  if many.nil?
24
- @many = @object.respond_to?(:count) ? @object.count : nil
21
+ # Determine if we are dealing with many objects or just one.
22
+ @many = @object.is_a?(Enumerable)
25
23
  else
26
24
  @many = many
27
25
  end
28
26
 
29
- @model = model || (@controller ? @controller.send(:get_model) : nil)
27
+ # Determine model either explicitly, or by inspecting @object or @controller.
28
+ @model = model
29
+ @model ||= @object.class if @object.is_a?(ActiveRecord::Base)
30
+ @model ||= @object[0].class if @many && @object[0].is_a?(ActiveRecord::Base)
31
+ @model ||= @controller.send(:get_model) if @controller
30
32
  end
31
33
 
32
34
  # Get controller action, if possible.
@@ -34,8 +36,8 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
34
36
  return @controller&.action_name&.to_sym
35
37
  end
36
38
 
37
- # Get a locally defined configuration, if one is defined.
38
- def get_local_serializer_config
39
+ # Get a locally defined native serializer configuration, if one is defined.
40
+ def get_local_native_serializer_config
39
41
  action = self.get_action
40
42
 
41
43
  if action && self.action_config
@@ -45,43 +47,70 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
45
47
  return self.action_config[action] if self.action_config[action]
46
48
  end
47
49
 
48
- # No action_config, so try singular/plural config.
49
- return self.plural_config if @many && self.plural_config
50
- return self.singular_config if !@many && self.singular_config
50
+ # No action_config, so try singular/plural config if explicitly instructed to via @many.
51
+ return self.plural_config if @many == true && self.plural_config
52
+ return self.singular_config if @many == false && self.singular_config
53
+
54
+ # Lastly, try returning the default config, or singular/plural config in that order.
55
+ return self.config || self.singular_config || self.plural_config
56
+ end
57
+
58
+ # Helper to get a native serializer configuration from the controller.
59
+ def get_controller_native_serializer_config
60
+ return nil unless @controller
61
+
62
+ if @many == true
63
+ controller_serializer = @controller.try(:native_serializer_plural_config)
64
+ elsif @many == false
65
+ controller_serializer = @controller.try(:native_serializer_singular_config)
66
+ end
51
67
 
52
- # Lastly, try returning the default config.
53
- return self.config
68
+ return controller_serializer || @controller.try(:native_serializer_config)
54
69
  end
55
70
 
56
- # Get a configuration passable to `as_json` for the object.
71
+ # Get a configuration passable to `serializable_hash` for the object.
57
72
  def get_serializer_config
58
73
  # Return a locally defined serializer config if one is defined.
59
- if local_config = self.get_local_serializer_config
74
+ if local_config = self.get_local_native_serializer_config
60
75
  return local_config
61
76
  end
62
77
 
63
- # Return a serializer config if one is defined.
64
- if serializer_config = @controller.send(:get_native_serializer_config)
78
+ # Return a serializer config if one is defined on the controller.
79
+ if serializer_config = get_controller_native_serializer_config
65
80
  return serializer_config
66
81
  end
67
82
 
68
83
  # If the config wasn't determined, build a serializer config from model fields.
69
- fields = @controller.try(:get_fields) if @controller
70
- unless fields.blank?
71
- columns, methods = fields.partition { |f| f.to_s.in?(@model.column_names) }
84
+ fields = @controller.send(:get_fields) if @controller
85
+ if fields
86
+ if @model
87
+ columns, methods = fields.partition { |f| f.in?(@model.column_names) }
88
+ else
89
+ columns = fields
90
+ methods = []
91
+ end
92
+
72
93
  return {only: columns, methods: methods}
73
94
  end
74
95
 
96
+ # By default, pass an empty configuration, allowing the serialization of all columns.
75
97
  return {}
76
98
  end
77
99
 
78
100
  # Convert the object (record or recordset) to Ruby primitives.
79
101
  def serialize
80
102
  if @object
81
- @many = @object.respond_to?(:each) if @many.nil?
82
- return @object.as_json(self.get_serializer_config)
103
+ begin
104
+ if @object.is_a?(Enumerable)
105
+ return @object.map { |r| r.serializable_hash(self.get_serializer_config) }
106
+ end
107
+ return @object.serializable_hash(self.get_serializer_config)
108
+ rescue NoMethodError
109
+ end
83
110
  end
84
- return nil
111
+
112
+ # Raise an error if we cannot serialize the object.
113
+ raise RESTFramework::UnserializableError.new(@object)
85
114
  end
86
115
 
87
116
  # Allow a serializer instance to be used as a hash directly in a nested serializer config.
@@ -113,6 +142,18 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
113
142
  end
114
143
  end
115
144
 
145
+
146
+ # :nocov:
116
147
  # Alias NativeModelSerializer -> NativeSerializer.
117
148
  class RESTFramework::NativeModelSerializer < RESTFramework::NativeSerializer
149
+ def initialize(**kwargs)
150
+ super
151
+ ActiveSupport::Deprecation.warn(
152
+ <<~MSG.split("\n").join(' ')
153
+ RESTFramework::NativeModelSerializer is deprecated and will be removed in future versions of
154
+ REST Framework; you should use RESTFramework::NativeSerializer instead.
155
+ MSG
156
+ )
157
+ end
118
158
  end
159
+ # :nocov:
@@ -2,19 +2,21 @@ module RESTFramework
2
2
  module Version
3
3
  @_version = nil
4
4
 
5
- def self.get_version
5
+ def self.get_version(skip_git: false)
6
6
  # Return cached @_version, if available.
7
7
  return @_version if @_version
8
8
 
9
9
  # First, attempt to get the version from git.
10
- begin
11
- version = `git describe 2>/dev/null`.strip
12
- raise "blank version" if version.nil? || version.match(/^\w*$/)
13
- # Check for local changes.
14
- changes = `git status --porcelain 2>/dev/null`
15
- version << '.localchanges' if changes.strip.length > 0
16
- return version
17
- rescue
10
+ unless skip_git
11
+ begin
12
+ version = `git describe 2>/dev/null`.strip
13
+ raise "blank version" if version.nil? || version.match(/^\w*$/)
14
+ # Check for local changes.
15
+ changes = `git status --porcelain 2>/dev/null`
16
+ version << '.localchanges' if changes.strip.length > 0
17
+ return version
18
+ rescue
19
+ end
18
20
  end
19
21
 
20
22
  # Git failed, so try to find a VERSION_STAMP.
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.1.3
4
+ version: 0.2.0
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: 2021-03-07 00:00:00.000000000 Z
11
+ date: 2021-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -43,7 +43,10 @@ files:
43
43
  - lib/rest_framework/controller_mixins/base.rb
44
44
  - lib/rest_framework/controller_mixins/models.rb
45
45
  - lib/rest_framework/engine.rb
46
+ - lib/rest_framework/errors.rb
46
47
  - lib/rest_framework/filters.rb
48
+ - lib/rest_framework/generators.rb
49
+ - lib/rest_framework/generators/controller_generator.rb
47
50
  - lib/rest_framework/paginators.rb
48
51
  - lib/rest_framework/routers.rb
49
52
  - lib/rest_framework/serializers.rb