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.
@@ -1,65 +1,68 @@
1
- module RESTFramework
2
- class BaseFilter
3
- def initialize(controller:)
4
- @controller = controller
5
- end
1
+ class RESTFramework::BaseFilter
2
+ def initialize(controller:)
3
+ @controller = controller
4
+ end
6
5
 
7
- def get_filtered_data(data)
8
- raise NotImplementedError
9
- end
6
+ def get_filtered_data(data)
7
+ raise NotImplementedError
10
8
  end
9
+ end
11
10
 
12
- # A simple filtering backend that supports filtering a recordset based on fields defined on the
13
- # controller class.
14
- class ModelFilter < BaseFilter
15
- # Filter params for keys allowed by the current action's filterset_fields/fields config.
16
- def _get_filter_params
17
- fields = @controller.class.filterset_fields || @controller.send(:get_fields)
18
- return @controller.request.query_parameters.select { |p|
19
- fields.include?(p.to_sym) || fields.include?(p.to_s)
20
- }.to_hash.symbolize_keys
21
- end
22
11
 
23
- def get_filtered_data(data)
24
- filter_params = self._get_filter_params
25
- unless filter_params.blank?
26
- return data.where(**filter_params)
27
- end
12
+ # A simple filtering backend that supports filtering a recordset based on fields defined on the
13
+ # controller class.
14
+ class RESTFramework::ModelFilter < RESTFramework::BaseFilter
15
+ # Filter params for keys allowed by the current action's filterset_fields/fields config.
16
+ def _get_filter_params
17
+ fields = @controller.class.filterset_fields || @controller.send(:get_fields)
18
+ return @controller.request.query_parameters.select { |p|
19
+ fields.include?(p.to_sym) || fields.include?(p.to_s)
20
+ }.to_hash.symbolize_keys
21
+ end
28
22
 
29
- return data
23
+ # Filter data according to the request query parameters.
24
+ def get_filtered_data(data)
25
+ filter_params = self._get_filter_params
26
+ unless filter_params.blank?
27
+ return data.where(**filter_params)
30
28
  end
29
+
30
+ return data
31
31
  end
32
+ end
32
33
 
33
- # A filter backend which handles ordering of the recordset.
34
- class ModelOrderingFilter < BaseFilter
35
- # Convert ordering string to an ordering configuration.
36
- def _get_ordering
37
- return nil unless @controller.class.ordering_query_param
38
34
 
39
- order_string = @controller.params[@controller.class.ordering_query_param]
40
- unless order_string.blank?
41
- return order_string.split(',').map { |field|
42
- if field[0] == '-'
43
- [field[1..-1].to_sym, :desc]
44
- else
45
- [field.to_sym, :asc]
46
- end
47
- }.to_h
48
- end
35
+ # A filter backend which handles ordering of the recordset.
36
+ class RESTFramework::ModelOrderingFilter < RESTFramework::BaseFilter
37
+ # Convert ordering string to an ordering configuration.
38
+ def _get_ordering
39
+ return nil unless @controller.class.ordering_query_param
49
40
 
50
- return nil
41
+ order_string = @controller.params[@controller.class.ordering_query_param]
42
+ unless order_string.blank?
43
+ return order_string.split(',').map { |field|
44
+ if field[0] == '-'
45
+ [field[1..-1].to_sym, :desc]
46
+ else
47
+ [field.to_sym, :asc]
48
+ end
49
+ }.to_h
51
50
  end
52
51
 
53
- def get_filtered_data(data)
54
- ordering = self._get_ordering
55
- if ordering && !ordering.empty?
56
- return data.order(_get_ordering)
57
- end
58
- return data
59
- end
52
+ return nil
60
53
  end
61
54
 
62
- # TODO: implement searching within fields rather than exact match filtering (ModelFilter)
63
- # class ModelSearchFilter < BaseFilter
64
- # end
55
+ # Order data according to the request query parameters.
56
+ def get_filtered_data(data)
57
+ ordering = self._get_ordering
58
+ if ordering && !ordering.empty?
59
+ return data.order(_get_ordering)
60
+ end
61
+ return data
62
+ end
65
63
  end
64
+
65
+
66
+ # TODO: implement searching within fields rather than exact match filtering (ModelFilter)
67
+ # class RESTFramework::ModelSearchFilter < RESTFramework::BaseFilter
68
+ # end
@@ -1,84 +1,95 @@
1
- module RESTFramework
2
-
3
- # A simple paginator based on page numbers.
4
- #
5
- # Example: http://example.com/api/users/?page=3&page_size=50
6
- class PageNumberPaginator
7
- def initialize(data:, controller:, **kwargs)
8
- @data = data
9
- @controller = controller
10
- @count = data.count
11
- @page_size = self._page_size
12
-
13
- @total_pages = @count / @page_size
14
- @total_pages += 1 if (@count % @page_size != 0)
15
- end
1
+ class RESTFramework::BasePaginator
2
+ def initialize(data:, controller:, **kwargs)
3
+ @data = data
4
+ @controller = controller
5
+ end
16
6
 
17
- def _page_size
18
- page_size = nil
7
+ # Get the page and return it so the caller can serialize it.
8
+ def get_page
9
+ raise NotImplementedError
10
+ end
19
11
 
20
- # Get from context, if allowed.
21
- if @controller.class.page_size_query_param
22
- page_size = @controller.params[@controller.class.page_size_query_param].presence
23
- if page_size
24
- page_size = page_size.to_i
25
- end
26
- end
12
+ # Wrap the serialized page with appropriate metadata.
13
+ def get_paginated_response(serialized_page)
14
+ raise NotImplementedError
15
+ end
16
+ end
27
17
 
28
- # Otherwise, get from config.
29
- if !page_size && @controller.class.page_size
30
- page_size = @controller.class.page_size
31
- end
32
18
 
33
- # Fallback to a page size of 15.
34
- page_size = 15 unless page_size
19
+ # A simple paginator based on page numbers.
20
+ #
21
+ # Example: http://example.com/api/users/?page=3&page_size=50
22
+ class RESTFramework::PageNumberPaginator < RESTFramework::BasePaginator
23
+ def initialize(**kwargs)
24
+ super
25
+ @count = @data.count
26
+ @page_size = self._page_size
27
+
28
+ @total_pages = @count / @page_size
29
+ @total_pages += 1 if (@count % @page_size != 0)
30
+ end
31
+
32
+ def _page_size
33
+ page_size = nil
35
34
 
36
- # Ensure we don't exceed the max page size.
37
- if @controller.class.max_page_size && page_size > @controller.class.max_page_size
38
- page_size = @controller.class.max_page_size
35
+ # Get from context, if allowed.
36
+ if @controller.class.page_size_query_param
37
+ if page_size = @controller.params[@controller.class.page_size_query_param].presence
38
+ page_size = page_size.to_i
39
39
  end
40
+ end
40
41
 
41
- # Ensure we return at least 1.
42
- return page_size.zero? ? 1 : page_size
42
+ # Otherwise, get from config.
43
+ if !page_size && @controller.class.page_size
44
+ page_size = @controller.class.page_size
43
45
  end
44
46
 
45
- def _page_query_param
46
- return @controller.class.page_query_param&.to_sym
47
+ # Ensure we don't exceed the max page size.
48
+ if @controller.class.max_page_size && page_size > @controller.class.max_page_size
49
+ page_size = @controller.class.max_page_size
47
50
  end
48
51
 
49
- def get_page(page_number=nil)
50
- # If page number isn't provided, infer from the params or use 1 as a fallback value.
51
- if !page_number
52
- page_number = @controller&.params&.[](self._page_query_param)
53
- if page_number.blank?
52
+ # Ensure we return at least 1.
53
+ return page_size.zero? ? 1 : page_size
54
+ end
55
+
56
+ def _page_query_param
57
+ return @controller.class.page_query_param&.to_sym
58
+ end
59
+
60
+ # Get the page and return it so the caller can serialize it.
61
+ def get_page(page_number=nil)
62
+ # If page number isn't provided, infer from the params or use 1 as a fallback value.
63
+ if !page_number
64
+ page_number = @controller&.params&.[](self._page_query_param)
65
+ if page_number.blank?
66
+ page_number = 1
67
+ else
68
+ page_number = page_number.to_i
69
+ if page_number.zero?
54
70
  page_number = 1
55
- else
56
- page_number = page_number.to_i
57
- if page_number.zero?
58
- page_number = 1
59
- end
60
71
  end
61
72
  end
62
- @page_number = page_number
63
-
64
- # Get the data page and return it so the caller can serialize the data in the proper format.
65
- page_index = @page_number - 1
66
- return @data.limit(@page_size).offset(page_index * @page_size)
67
73
  end
74
+ @page_number = page_number
68
75
 
69
- # Wrap the serialized page with appripriate metadata.
70
- def get_paginated_response(serialized_page)
71
- return {
72
- count: @count,
73
- page: @page_number,
74
- total_pages: @total_pages,
75
- results: serialized_page,
76
- }
77
- end
76
+ # Get the data page and return it so the caller can serialize the data in the proper format.
77
+ page_index = @page_number - 1
78
+ return @data.limit(@page_size).offset(page_index * @page_size)
78
79
  end
79
80
 
80
- # TODO: implement this
81
- # class CountOffsetPaginator
82
- # end
83
-
81
+ # Wrap the serialized page with appropriate metadata. TODO: include links.
82
+ def get_paginated_response(serialized_page)
83
+ return {
84
+ count: @count,
85
+ page: @page_number,
86
+ total_pages: @total_pages,
87
+ results: serialized_page,
88
+ }
89
+ end
84
90
  end
91
+
92
+
93
+ # TODO: implement this
94
+ # class RESTFramework::CountOffsetPaginator
95
+ # end
@@ -2,7 +2,7 @@ require 'action_dispatch/routing/mapper'
2
2
 
3
3
  module ActionDispatch::Routing
4
4
  class Mapper
5
- # Helper to take extra_actions hash and convert to a consistent format.
5
+ # Internal helper to take extra_actions hash and convert to a consistent format.
6
6
  protected def _parse_extra_actions(extra_actions)
7
7
  return (extra_actions || {}).map do |k,v|
8
8
  kwargs = {}
@@ -37,7 +37,7 @@ module ActionDispatch::Routing
37
37
  end
38
38
  end
39
39
 
40
- # Private interface to get the controller class from the name and current scope.
40
+ # Internal interface to get the controller class from the name and current scope.
41
41
  protected def _get_controller_class(name, pluralize: true, fallback_reverse_pluralization: true)
42
42
  # get class name
43
43
  name = name.to_s.camelize # camelize to leave plural names plural
@@ -71,7 +71,7 @@ module ActionDispatch::Routing
71
71
  return controller
72
72
  end
73
73
 
74
- # Core implementation of the `rest_resource(s)` router, both singular and plural.
74
+ # Internal core implementation of the `rest_resource(s)` router, both singular and plural.
75
75
  # @param default_singular [Boolean] the default plurality of the resource if the plurality is
76
76
  # not otherwise defined by the controller
77
77
  # @param name [Symbol] the resource name, from which path and controller are deduced by default
@@ -166,7 +166,7 @@ module ActionDispatch::Routing
166
166
  end
167
167
 
168
168
  # Route a controller's `#root` to '/' in the current scope/namespace, along with other actions.
169
- # @param label [Symbol] the snake_case name of the controller
169
+ # @param name [Symbol] the snake_case name of the controller
170
170
  def rest_root(name=nil, **kwargs, &block)
171
171
  # By default, use RootController#root.
172
172
  root_action = kwargs.delete(:action) || :root
@@ -1,116 +1,114 @@
1
- module RESTFramework
2
- class BaseSerializer
3
- attr_reader :errors
1
+ class RESTFramework::BaseSerializer
2
+ attr_reader :errors
4
3
 
5
- def initialize(object: nil, controller: nil, **kwargs)
6
- @object = object
7
- @controller = controller
8
- end
4
+ def initialize(object: nil, controller: nil, **kwargs)
5
+ @object = object
6
+ @controller = controller
9
7
  end
8
+ end
10
9
 
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).
14
- class NativeModelSerializer < BaseSerializer
15
- class_attribute :config
16
- class_attribute :singular_config
17
- class_attribute :plural_config
18
- class_attribute :action_config
19
-
20
- def initialize(many: nil, model: nil, **kwargs)
21
- super(**kwargs)
22
-
23
- if many.nil?
24
- @many = @object.respond_to?(:count) ? @object.count : nil
25
- else
26
- @many = many
27
- end
28
-
29
- @model = model || (@controller ? @controller.send(:get_model) : nil)
30
- end
31
10
 
32
- # Get controller action, if possible.
33
- def get_action
34
- return @controller&.action_name&.to_sym
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).
14
+ class RESTFramework::NativeModelSerializer < RESTFramework::BaseSerializer
15
+ class_attribute :config
16
+ class_attribute :singular_config
17
+ class_attribute :plural_config
18
+ class_attribute :action_config
19
+
20
+ def initialize(many: nil, model: nil, **kwargs)
21
+ super(**kwargs)
22
+
23
+ if many.nil?
24
+ @many = @object.respond_to?(:count) ? @object.count : nil
25
+ else
26
+ @many = many
35
27
  end
36
28
 
37
- # Get a locally defined configuration, if one is defined.
38
- def get_local_serializer_config
39
- action = self.get_action
29
+ @model = model || (@controller ? @controller.send(:get_model) : nil)
30
+ end
40
31
 
41
- if action && self.action_config
42
- # Index action should use :list serializer config if :index is not provided.
43
- action = :list if action == :index && !self.action_config.key?(:index)
32
+ # Get controller action, if possible.
33
+ def get_action
34
+ return @controller&.action_name&.to_sym
35
+ end
44
36
 
45
- return self.action_config[action] if self.action_config[action]
46
- end
37
+ # Get a locally defined configuration, if one is defined.
38
+ def get_local_serializer_config
39
+ action = self.get_action
47
40
 
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
41
+ if action && self.action_config
42
+ # Index action should use :list serializer config if :index is not provided.
43
+ action = :list if action == :index && !self.action_config.key?(:index)
51
44
 
52
- # Lastly, try returning the default config.
53
- return self.config
45
+ return self.action_config[action] if self.action_config[action]
54
46
  end
55
47
 
56
- # Get a configuration passable to `as_json` for the object.
57
- def get_serializer_config
58
- # Return a locally defined serializer config if one is defined.
59
- if local_config = self.get_local_serializer_config
60
- return local_config
61
- end
62
-
63
- # Return a serializer config if one is defined.
64
- if serializer_config = @controller.send(:get_native_serializer_config)
65
- return serializer_config
66
- end
67
-
68
- # 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) }
72
- return {only: columns, methods: methods}
73
- end
74
-
75
- return {}
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
51
+
52
+ # Lastly, try returning the default config.
53
+ return self.config
54
+ end
55
+
56
+ # Get a configuration passable to `as_json` for the object.
57
+ def get_serializer_config
58
+ # Return a locally defined serializer config if one is defined.
59
+ if local_config = self.get_local_serializer_config
60
+ return local_config
76
61
  end
77
62
 
78
- # Convert the object (record or recordset) to Ruby primitives.
79
- def serialize
80
- if @object
81
- @many = @object.respond_to?(:each) if @many.nil?
82
- return @object.as_json(self.get_serializer_config)
83
- end
84
- return nil
63
+ # Return a serializer config if one is defined.
64
+ if serializer_config = @controller.send(:get_native_serializer_config)
65
+ return serializer_config
85
66
  end
86
67
 
87
- # Allow a serializer instance to be used as a hash directly in a nested serializer config.
88
- def [](key)
89
- unless instance_variable_defined?(:@_nested_config)
90
- @_nested_config = self.get_serializer_config
91
- end
92
- return @_nested_config[key]
68
+ # 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) }
72
+ return {only: columns, methods: methods}
93
73
  end
94
- def []=(key, value)
95
- unless instance_variable_defined?(:@_nested_config)
96
- @_nested_config = self.get_serializer_config
97
- end
98
- return @_nested_config[key] = value
74
+
75
+ return {}
76
+ end
77
+
78
+ # Convert the object (record or recordset) to Ruby primitives.
79
+ def serialize
80
+ if @object
81
+ @many = @object.respond_to?(:each) if @many.nil?
82
+ return @object.as_json(self.get_serializer_config)
99
83
  end
84
+ return nil
85
+ end
100
86
 
101
- # Allow a serializer class to be used as a hash directly in a nested serializer config.
102
- def self.[](key)
103
- unless instance_variable_defined?(:@_nested_config)
104
- @_nested_config = self.new.get_serializer_config
105
- end
106
- return @_nested_config[key]
87
+ # Allow a serializer instance to be used as a hash directly in a nested serializer config.
88
+ def [](key)
89
+ unless instance_variable_defined?(:@_nested_config)
90
+ @_nested_config = self.get_serializer_config
107
91
  end
108
- def self.[]=(key, value)
109
- unless instance_variable_defined?(:@_nested_config)
110
- @_nested_config = self.new.get_serializer_config
111
- end
112
- return @_nested_config[key] = value
92
+ return @_nested_config[key]
93
+ end
94
+ def []=(key, value)
95
+ unless instance_variable_defined?(:@_nested_config)
96
+ @_nested_config = self.get_serializer_config
113
97
  end
98
+ return @_nested_config[key] = value
114
99
  end
115
100
 
101
+ # Allow a serializer class to be used as a hash directly in a nested serializer config.
102
+ def self.[](key)
103
+ unless instance_variable_defined?(:@_nested_config)
104
+ @_nested_config = self.new.get_serializer_config
105
+ end
106
+ return @_nested_config[key]
107
+ end
108
+ def self.[]=(key, value)
109
+ unless instance_variable_defined?(:@_nested_config)
110
+ @_nested_config = self.new.get_serializer_config
111
+ end
112
+ return @_nested_config[key] = value
113
+ end
116
114
  end