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.
@@ -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