rest_framework 0.1.3 → 0.2.0

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