rest_framework 0.9.7 → 0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +5 -0
- data/VERSION +1 -1
- data/app/views/layouts/rest_framework.html.erb +1 -1
- data/lib/rest_framework/filters/{base.rb → base_filter.rb} +4 -1
- data/lib/rest_framework/filters/{model_ordering.rb → model_ordering_filter.rb} +4 -1
- data/lib/rest_framework/filters/{model_query.rb → model_query_filter.rb} +4 -1
- data/lib/rest_framework/filters/{model_search.rb → model_search_filter.rb} +4 -1
- data/lib/rest_framework/filters/{ransack.rb → ransack_filter.rb} +4 -1
- data/lib/rest_framework/filters.rb +5 -5
- data/lib/rest_framework/{controller_mixins/base.rb → mixins/base_controller_mixin.rb} +4 -5
- data/lib/rest_framework/{controller_mixins/bulk.rb → mixins/bulk_model_controller_mixin.rb} +17 -5
- data/lib/rest_framework/{controller_mixins/models.rb → mixins/model_controller_mixin.rb} +39 -44
- data/lib/rest_framework/mixins.rb +7 -0
- data/lib/rest_framework/paginators/base_paginator.rb +19 -0
- data/lib/rest_framework/paginators/page_number_paginator.rb +73 -0
- data/lib/rest_framework/paginators.rb +3 -84
- data/lib/rest_framework/routers.rb +0 -1
- data/lib/rest_framework/serializers/active_model_serializer_adapter_factory.rb +20 -0
- data/lib/rest_framework/serializers/base_serializer.rb +40 -0
- data/lib/rest_framework/serializers/native_serializer.rb +360 -0
- data/lib/rest_framework/serializers.rb +4 -383
- data/lib/rest_framework.rb +1 -1
- metadata +17 -12
- data/lib/rest_framework/controller_mixins.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b06a61592498f8debce6f504b4eee90ceeef6fdff63dbb769bd70c623cd9631
|
4
|
+
data.tar.gz: ab5b1284a3504104b77cc38278bad01a1dfa30ce7ebcd65323b73e0dd31e58a2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed04505314f252ac1058732a875711090fb07499edac42814bf72e5b15f90f833040e40399df279654727041af30789c8a931e06d83b9fc1c67f46d4f0da850f
|
7
|
+
data.tar.gz: abd68ae8ab71f2fff8f7a933270a467811143096f7cdde16d21c99ac3faeadc8f958cb0bcdde94c1b57f1d620fc278f8d9b5e3b4e2cec5851fb4616f9c325d41
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
1
|
+
0.9.8
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class RESTFramework::BaseFilter
|
1
|
+
class RESTFramework::Filters::BaseFilter
|
2
2
|
def initialize(controller:)
|
3
3
|
@controller = controller
|
4
4
|
end
|
@@ -7,3 +7,6 @@ class RESTFramework::BaseFilter
|
|
7
7
|
raise NotImplementedError
|
8
8
|
end
|
9
9
|
end
|
10
|
+
|
11
|
+
# Alias for convenience.
|
12
|
+
RESTFramework::BaseFilter = RESTFramework::Filters::BaseFilter
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# A filter backend which handles ordering of the recordset.
|
2
|
-
class RESTFramework::ModelOrderingFilter < RESTFramework::BaseFilter
|
2
|
+
class RESTFramework::Filters::ModelOrderingFilter < RESTFramework::Filters::BaseFilter
|
3
3
|
# Get a list of ordering fields for the current action.
|
4
4
|
def _get_fields
|
5
5
|
return @controller.ordering_fields&.map(&:to_s) || @controller.get_fields
|
@@ -46,3 +46,6 @@ class RESTFramework::ModelOrderingFilter < RESTFramework::BaseFilter
|
|
46
46
|
return data
|
47
47
|
end
|
48
48
|
end
|
49
|
+
|
50
|
+
# Alias for convenience.
|
51
|
+
RESTFramework::ModelOrderingFilter = RESTFramework::Filters::ModelOrderingFilter
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# A simple filtering backend that supports filtering a recordset based on query parameters.
|
2
|
-
class RESTFramework::ModelQueryFilter < RESTFramework::BaseFilter
|
2
|
+
class RESTFramework::Filters::ModelQueryFilter < RESTFramework::Filters::BaseFilter
|
3
3
|
# Get a list of filterset fields for the current action.
|
4
4
|
def _get_fields
|
5
5
|
# Always return a list of strings; `@controller.get_fields` already does this.
|
@@ -49,3 +49,6 @@ class RESTFramework::ModelQueryFilter < RESTFramework::BaseFilter
|
|
49
49
|
return data
|
50
50
|
end
|
51
51
|
end
|
52
|
+
|
53
|
+
# Alias for convenience.
|
54
|
+
RESTFramework::ModelQueryFilter = RESTFramework::Filters::ModelQueryFilter
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Multi-field text searching on models.
|
2
|
-
class RESTFramework::ModelSearchFilter < RESTFramework::BaseFilter
|
2
|
+
class RESTFramework::Filters::ModelSearchFilter < RESTFramework::Filters::BaseFilter
|
3
3
|
# Get a list of search fields for the current action.
|
4
4
|
def _get_fields
|
5
5
|
if search_fields = @controller.search_fields
|
@@ -39,3 +39,6 @@ class RESTFramework::ModelSearchFilter < RESTFramework::BaseFilter
|
|
39
39
|
return data
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
# Alias for convenience.
|
44
|
+
RESTFramework::ModelSearchFilter = RESTFramework::Filters::ModelSearchFilter
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Adapter for the `ransack` gem.
|
2
|
-
class RESTFramework::RansackFilter < RESTFramework::BaseFilter
|
2
|
+
class RESTFramework::Filters::RansackFilter < RESTFramework::Filters::BaseFilter
|
3
3
|
# Filter data according to the request query parameters.
|
4
4
|
def get_filtered_data(data)
|
5
5
|
q = @controller.request.query_parameters[@controller.ransack_query_param]
|
@@ -23,3 +23,6 @@ class RESTFramework::RansackFilter < RESTFramework::BaseFilter
|
|
23
23
|
return data
|
24
24
|
end
|
25
25
|
end
|
26
|
+
|
27
|
+
# Alias for convenience.
|
28
|
+
RESTFramework::RansackFilter = RESTFramework::Filters::RansackFilter
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module RESTFramework::Filters
|
2
2
|
end
|
3
3
|
|
4
|
-
require_relative "filters/
|
4
|
+
require_relative "filters/base_filter"
|
5
5
|
|
6
|
-
require_relative "filters/
|
7
|
-
require_relative "filters/
|
8
|
-
require_relative "filters/
|
9
|
-
require_relative "filters/
|
6
|
+
require_relative "filters/model_ordering_filter"
|
7
|
+
require_relative "filters/model_query_filter"
|
8
|
+
require_relative "filters/model_search_filter"
|
9
|
+
require_relative "filters/ransack_filter"
|
@@ -1,11 +1,7 @@
|
|
1
|
-
require_relative "../errors"
|
2
|
-
require_relative "../serializers"
|
3
|
-
require_relative "../utils"
|
4
|
-
|
5
1
|
# This module provides the common functionality for any controller mixins, a `root` action, and
|
6
2
|
# the ability to route arbitrary actions with `extra_actions`. This is also where `api_response`
|
7
3
|
# is defined.
|
8
|
-
module RESTFramework::BaseControllerMixin
|
4
|
+
module RESTFramework::Mixins::BaseControllerMixin
|
9
5
|
RRF_BASE_CONFIG = {
|
10
6
|
extra_actions: nil,
|
11
7
|
extra_member_actions: nil,
|
@@ -348,3 +344,6 @@ module RESTFramework::BaseControllerMixin
|
|
348
344
|
return api_response(self.get_options_metadata)
|
349
345
|
end
|
350
346
|
end
|
347
|
+
|
348
|
+
# Alias for convenience.
|
349
|
+
RESTFramework::BaseControllerMixin = RESTFramework::Mixins::BaseControllerMixin
|
@@ -1,8 +1,8 @@
|
|
1
|
-
require_relative "
|
1
|
+
require_relative "model_controller_mixin"
|
2
2
|
|
3
3
|
# Mixin for creating records in bulk. This is unique compared to update/destroy because we overload
|
4
4
|
# the existing `create` action to support bulk creation.
|
5
|
-
module RESTFramework::BulkCreateModelMixin
|
5
|
+
module RESTFramework::Mixins::BulkCreateModelMixin
|
6
6
|
# While bulk update/destroy are obvious because they create new router endpoints, bulk create
|
7
7
|
# overloads the existing collection `POST` endpoint, so we add a special key to the options
|
8
8
|
# metadata to indicate bulk create is supported.
|
@@ -31,8 +31,11 @@ module RESTFramework::BulkCreateModelMixin
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
# Alias for convenience.
|
35
|
+
RESTFramework::BulkCreateModelMixin = RESTFramework::Mixins::BulkCreateModelMixin
|
36
|
+
|
34
37
|
# Mixin for updating records in bulk.
|
35
|
-
module RESTFramework::BulkUpdateModelMixin
|
38
|
+
module RESTFramework::Mixins::BulkUpdateModelMixin
|
36
39
|
def update_all
|
37
40
|
records = self.update_all!
|
38
41
|
serialized_records = self.bulk_serialize(records)
|
@@ -56,8 +59,11 @@ module RESTFramework::BulkUpdateModelMixin
|
|
56
59
|
end
|
57
60
|
end
|
58
61
|
|
62
|
+
# Alias for convenience.
|
63
|
+
RESTFramework::BulkUpdateModelMixin = RESTFramework::Mixins::BulkUpdateModelMixin
|
64
|
+
|
59
65
|
# Mixin for destroying records in bulk.
|
60
|
-
module RESTFramework::BulkDestroyModelMixin
|
66
|
+
module RESTFramework::Mixins::BulkDestroyModelMixin
|
61
67
|
def destroy_all
|
62
68
|
if params[:_json].is_a?(Array)
|
63
69
|
records = self.destroy_all!
|
@@ -83,8 +89,11 @@ module RESTFramework::BulkDestroyModelMixin
|
|
83
89
|
end
|
84
90
|
end
|
85
91
|
|
92
|
+
# Alias for convenience.
|
93
|
+
RESTFramework::BulkDestroyModelMixin = RESTFramework::Mixins::BulkDestroyModelMixin
|
94
|
+
|
86
95
|
# Mixin that includes all the CRUD bulk mixins.
|
87
|
-
module RESTFramework::BulkModelControllerMixin
|
96
|
+
module RESTFramework::Mixins::BulkModelControllerMixin
|
88
97
|
include RESTFramework::ModelControllerMixin
|
89
98
|
|
90
99
|
include RESTFramework::BulkCreateModelMixin
|
@@ -95,3 +104,6 @@ module RESTFramework::BulkModelControllerMixin
|
|
95
104
|
RESTFramework::ModelControllerMixin.included(base)
|
96
105
|
end
|
97
106
|
end
|
107
|
+
|
108
|
+
# Alias for convenience.
|
109
|
+
RESTFramework::BulkModelControllerMixin = RESTFramework::Mixins::BulkModelControllerMixin
|
@@ -1,8 +1,5 @@
|
|
1
|
-
require_relative "base"
|
2
|
-
require_relative "../filters"
|
3
|
-
|
4
1
|
# This module provides the core functionality for controllers based on models.
|
5
|
-
module RESTFramework::BaseModelControllerMixin
|
2
|
+
module RESTFramework::Mixins::BaseModelControllerMixin
|
6
3
|
BASE64_REGEX = /data:(.*);base64,(.*)/
|
7
4
|
BASE64_TRANSLATE = ->(field, value) {
|
8
5
|
_, content_type, payload = value.match(BASE64_REGEX).to_a
|
@@ -552,54 +549,28 @@ module RESTFramework::BaseModelControllerMixin
|
|
552
549
|
alias_method :get_create_params, :get_body_params
|
553
550
|
alias_method :get_update_params, :get_body_params
|
554
551
|
|
555
|
-
# Get the set of records this controller has access to.
|
556
|
-
# the view as the `@recordset` instance variable.
|
552
|
+
# Get the set of records this controller has access to.
|
557
553
|
def get_recordset
|
558
|
-
return
|
559
|
-
return (@recordset = self.class.recordset) if self.class.recordset
|
554
|
+
return self.class.recordset if self.class.recordset
|
560
555
|
|
561
556
|
# If there is a model, return that model's default scope (all records by default).
|
562
|
-
if
|
563
|
-
return
|
564
|
-
end
|
565
|
-
|
566
|
-
return @recordset = nil
|
567
|
-
end
|
568
|
-
|
569
|
-
# Get the recordset but with any associations included to avoid N+1 queries.
|
570
|
-
def get_recordset_with_includes
|
571
|
-
reflections = self.class.get_model.reflections
|
572
|
-
associations = self.get_fields.map { |f|
|
573
|
-
if reflections.key?(f)
|
574
|
-
f.to_sym
|
575
|
-
elsif reflections.key?("rich_text_#{f}")
|
576
|
-
:"rich_text_#{f}"
|
577
|
-
elsif reflections.key?("#{f}_attachment")
|
578
|
-
:"#{f}_attachment"
|
579
|
-
elsif reflections.key?("#{f}_attachments")
|
580
|
-
:"#{f}_attachments"
|
581
|
-
end
|
582
|
-
}.compact
|
583
|
-
|
584
|
-
if associations.any?
|
585
|
-
return self.get_recordset.includes(associations)
|
557
|
+
if model = self.class.get_model
|
558
|
+
return model.all
|
586
559
|
end
|
587
560
|
|
588
|
-
return
|
561
|
+
return nil
|
589
562
|
end
|
590
563
|
|
591
564
|
# Get the records this controller has access to *after* any filtering is applied.
|
592
565
|
def get_records
|
593
|
-
return @records
|
594
|
-
|
595
|
-
return @records = self.get_filtered_data(self.get_recordset_with_includes)
|
566
|
+
return @records ||= self.get_filtered_data(self.get_recordset)
|
596
567
|
end
|
597
568
|
|
598
569
|
# Get a single record by primary key or another column, if allowed. The return value is cached and
|
599
570
|
# exposed to the view as the `@record` instance variable.
|
600
571
|
def get_record
|
601
572
|
# Cache the result.
|
602
|
-
return @record if
|
573
|
+
return @record if @record
|
603
574
|
|
604
575
|
recordset = self.get_recordset
|
605
576
|
find_by_key = self.class.get_model.primary_key
|
@@ -660,8 +631,11 @@ module RESTFramework::BaseModelControllerMixin
|
|
660
631
|
end
|
661
632
|
end
|
662
633
|
|
634
|
+
# Alias for convenience.
|
635
|
+
RESTFramework::BaseModelControllerMixin = RESTFramework::Mixins::BaseModelControllerMixin
|
636
|
+
|
663
637
|
# Mixin for listing records.
|
664
|
-
module RESTFramework::ListModelMixin
|
638
|
+
module RESTFramework::Mixins::ListModelMixin
|
665
639
|
def index
|
666
640
|
return api_response(self.get_index_records)
|
667
641
|
end
|
@@ -688,15 +662,21 @@ module RESTFramework::ListModelMixin
|
|
688
662
|
end
|
689
663
|
end
|
690
664
|
|
665
|
+
# Alias for convenience.
|
666
|
+
RESTFramework::ListModelMixin = RESTFramework::Mixins::ListModelMixin
|
667
|
+
|
691
668
|
# Mixin for showing records.
|
692
|
-
module RESTFramework::ShowModelMixin
|
669
|
+
module RESTFramework::Mixins::ShowModelMixin
|
693
670
|
def show
|
694
671
|
return api_response(self.get_record)
|
695
672
|
end
|
696
673
|
end
|
697
674
|
|
675
|
+
# Alias for convenience.
|
676
|
+
RESTFramework::ShowModelMixin = RESTFramework::Mixins::ShowModelMixin
|
677
|
+
|
698
678
|
# Mixin for creating records.
|
699
|
-
module RESTFramework::CreateModelMixin
|
679
|
+
module RESTFramework::Mixins::CreateModelMixin
|
700
680
|
def create
|
701
681
|
return api_response(self.create!, status: :created)
|
702
682
|
end
|
@@ -707,8 +687,11 @@ module RESTFramework::CreateModelMixin
|
|
707
687
|
end
|
708
688
|
end
|
709
689
|
|
690
|
+
# Alias for convenience.
|
691
|
+
RESTFramework::CreateModelMixin = RESTFramework::Mixins::CreateModelMixin
|
692
|
+
|
710
693
|
# Mixin for updating records.
|
711
|
-
module RESTFramework::UpdateModelMixin
|
694
|
+
module RESTFramework::Mixins::UpdateModelMixin
|
712
695
|
def update
|
713
696
|
return api_response(self.update!)
|
714
697
|
end
|
@@ -721,8 +704,11 @@ module RESTFramework::UpdateModelMixin
|
|
721
704
|
end
|
722
705
|
end
|
723
706
|
|
707
|
+
# Alias for convenience.
|
708
|
+
RESTFramework::UpdateModelMixin = RESTFramework::Mixins::UpdateModelMixin
|
709
|
+
|
724
710
|
# Mixin for destroying records.
|
725
|
-
module RESTFramework::DestroyModelMixin
|
711
|
+
module RESTFramework::Mixins::DestroyModelMixin
|
726
712
|
def destroy
|
727
713
|
self.destroy!
|
728
714
|
return api_response("")
|
@@ -734,8 +720,11 @@ module RESTFramework::DestroyModelMixin
|
|
734
720
|
end
|
735
721
|
end
|
736
722
|
|
723
|
+
# Alias for convenience.
|
724
|
+
RESTFramework::DestroyModelMixin = RESTFramework::Mixins::DestroyModelMixin
|
725
|
+
|
737
726
|
# Mixin that includes show/list mixins.
|
738
|
-
module RESTFramework::ReadOnlyModelControllerMixin
|
727
|
+
module RESTFramework::Mixins::ReadOnlyModelControllerMixin
|
739
728
|
include RESTFramework::BaseModelControllerMixin
|
740
729
|
|
741
730
|
include RESTFramework::ListModelMixin
|
@@ -746,8 +735,11 @@ module RESTFramework::ReadOnlyModelControllerMixin
|
|
746
735
|
end
|
747
736
|
end
|
748
737
|
|
738
|
+
# Alias for convenience.
|
739
|
+
RESTFramework::ReadOnlyModelControllerMixin = RESTFramework::Mixins::ReadOnlyModelControllerMixin
|
740
|
+
|
749
741
|
# Mixin that includes all the CRUD mixins.
|
750
|
-
module RESTFramework::ModelControllerMixin
|
742
|
+
module RESTFramework::Mixins::ModelControllerMixin
|
751
743
|
include RESTFramework::BaseModelControllerMixin
|
752
744
|
|
753
745
|
include RESTFramework::ListModelMixin
|
@@ -760,3 +752,6 @@ module RESTFramework::ModelControllerMixin
|
|
760
752
|
RESTFramework::BaseModelControllerMixin.included(base)
|
761
753
|
end
|
762
754
|
end
|
755
|
+
|
756
|
+
# Alias for convenience.
|
757
|
+
RESTFramework::ModelControllerMixin = RESTFramework::Mixins::ModelControllerMixin
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class RESTFramework::Paginators::BasePaginator
|
2
|
+
def initialize(data:, controller:, **kwargs)
|
3
|
+
@data = data
|
4
|
+
@controller = controller
|
5
|
+
end
|
6
|
+
|
7
|
+
# Get the page and return it so the caller can serialize it.
|
8
|
+
def get_page
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
# Wrap the serialized page with appropriate metadata.
|
13
|
+
def get_paginated_response(serialized_page)
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Alias for convenience.
|
19
|
+
RESTFramework::BasePaginator = RESTFramework::Paginators::BasePaginator
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# A simple paginator based on page numbers.
|
2
|
+
#
|
3
|
+
# Example: http://example.com/api/users/?page=3&page_size=50
|
4
|
+
class RESTFramework::Paginators::PageNumberPaginator < RESTFramework::Paginators::BasePaginator
|
5
|
+
def initialize(**kwargs)
|
6
|
+
super
|
7
|
+
# Exclude any `select` clauses since that would cause `count` to fail with a SQL `SyntaxError`.
|
8
|
+
@count = @data.except(:select).count
|
9
|
+
@page_size = self._page_size
|
10
|
+
|
11
|
+
@total_pages = @count / @page_size
|
12
|
+
@total_pages += 1 if @count % @page_size != 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def _page_size
|
16
|
+
page_size = nil
|
17
|
+
|
18
|
+
# Get from context, if allowed.
|
19
|
+
if @controller.page_size_query_param
|
20
|
+
if page_size = @controller.params[@controller.page_size_query_param].presence
|
21
|
+
page_size = page_size.to_i
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Otherwise, get from config.
|
26
|
+
if !page_size && @controller.page_size
|
27
|
+
page_size = @controller.page_size
|
28
|
+
end
|
29
|
+
|
30
|
+
# Ensure we don't exceed the max page size.
|
31
|
+
if @controller.max_page_size && page_size > @controller.max_page_size
|
32
|
+
page_size = @controller.max_page_size
|
33
|
+
end
|
34
|
+
|
35
|
+
# Ensure we return at least 1.
|
36
|
+
return page_size.zero? ? 1 : page_size
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get the page and return it so the caller can serialize it.
|
40
|
+
def get_page(page_number=nil)
|
41
|
+
# If page number isn't provided, infer from the params or use 1 as a fallback value.
|
42
|
+
unless page_number
|
43
|
+
page_number = @controller&.params&.[](@controller.page_query_param&.to_sym)
|
44
|
+
if page_number.blank?
|
45
|
+
page_number = 1
|
46
|
+
else
|
47
|
+
page_number = page_number.to_i
|
48
|
+
if page_number.zero?
|
49
|
+
page_number = 1
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
@page_number = page_number
|
54
|
+
|
55
|
+
# Get the data page and return it so the caller can serialize the data in the proper format.
|
56
|
+
page_index = @page_number - 1
|
57
|
+
return @data.limit(@page_size).offset(page_index * @page_size)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Wrap the serialized page with appropriate metadata. TODO: include links.
|
61
|
+
def get_paginated_response(serialized_page)
|
62
|
+
return {
|
63
|
+
count: @count,
|
64
|
+
page: @page_number,
|
65
|
+
page_size: @page_size,
|
66
|
+
total_pages: @total_pages,
|
67
|
+
results: serialized_page,
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Alias for convenience.
|
73
|
+
RESTFramework::PageNumberPaginator = RESTFramework::Paginators::PageNumberPaginator
|
@@ -1,90 +1,9 @@
|
|
1
|
-
|
2
|
-
def initialize(data:, controller:, **kwargs)
|
3
|
-
@data = data
|
4
|
-
@controller = controller
|
5
|
-
end
|
6
|
-
|
7
|
-
# Get the page and return it so the caller can serialize it.
|
8
|
-
def get_page
|
9
|
-
raise NotImplementedError
|
10
|
-
end
|
11
|
-
|
12
|
-
# Wrap the serialized page with appropriate metadata.
|
13
|
-
def get_paginated_response(serialized_page)
|
14
|
-
raise NotImplementedError
|
15
|
-
end
|
1
|
+
module RESTFramework::Paginators
|
16
2
|
end
|
17
3
|
|
18
|
-
|
19
|
-
#
|
20
|
-
# Example: http://example.com/api/users/?page=3&page_size=50
|
21
|
-
class RESTFramework::PageNumberPaginator < RESTFramework::BasePaginator
|
22
|
-
def initialize(**kwargs)
|
23
|
-
super
|
24
|
-
# Exclude any `select` clauses since that would cause `count` to fail with a SQL `SyntaxError`.
|
25
|
-
@count = @data.except(:select).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
|
34
|
-
|
35
|
-
# Get from context, if allowed.
|
36
|
-
if @controller.page_size_query_param
|
37
|
-
if page_size = @controller.params[@controller.page_size_query_param].presence
|
38
|
-
page_size = page_size.to_i
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Otherwise, get from config.
|
43
|
-
if !page_size && @controller.page_size
|
44
|
-
page_size = @controller.page_size
|
45
|
-
end
|
4
|
+
require_relative "paginators/base_paginator"
|
46
5
|
|
47
|
-
|
48
|
-
if @controller.max_page_size && page_size > @controller.max_page_size
|
49
|
-
page_size = @controller.max_page_size
|
50
|
-
end
|
51
|
-
|
52
|
-
# Ensure we return at least 1.
|
53
|
-
return page_size.zero? ? 1 : page_size
|
54
|
-
end
|
55
|
-
|
56
|
-
# Get the page and return it so the caller can serialize it.
|
57
|
-
def get_page(page_number=nil)
|
58
|
-
# If page number isn't provided, infer from the params or use 1 as a fallback value.
|
59
|
-
unless page_number
|
60
|
-
page_number = @controller&.params&.[](@controller.page_query_param&.to_sym)
|
61
|
-
if page_number.blank?
|
62
|
-
page_number = 1
|
63
|
-
else
|
64
|
-
page_number = page_number.to_i
|
65
|
-
if page_number.zero?
|
66
|
-
page_number = 1
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
@page_number = page_number
|
71
|
-
|
72
|
-
# Get the data page and return it so the caller can serialize the data in the proper format.
|
73
|
-
page_index = @page_number - 1
|
74
|
-
return @data.limit(@page_size).offset(page_index * @page_size)
|
75
|
-
end
|
76
|
-
|
77
|
-
# Wrap the serialized page with appropriate metadata. TODO: include links.
|
78
|
-
def get_paginated_response(serialized_page)
|
79
|
-
return {
|
80
|
-
count: @count,
|
81
|
-
page: @page_number,
|
82
|
-
page_size: @page_size,
|
83
|
-
total_pages: @total_pages,
|
84
|
-
results: serialized_page,
|
85
|
-
}
|
86
|
-
end
|
87
|
-
end
|
6
|
+
require_relative "paginators/page_number_paginator"
|
88
7
|
|
89
8
|
# TODO: implement this
|
90
9
|
# class RESTFramework::CountOffsetPaginator
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# This is a helper factory to wrap an ActiveModelSerializer to provide a `serialize` method which
|
2
|
+
# accepts both collections and individual records. Use `.for` to build adapters.
|
3
|
+
class RESTFramework::Serializers::ActiveModelSerializerAdapterFactory
|
4
|
+
def self.for(active_model_serializer)
|
5
|
+
return Class.new(active_model_serializer) do
|
6
|
+
def serialize
|
7
|
+
if self.object.respond_to?(:to_ary)
|
8
|
+
return self.object.map { |r| self.class.superclass.new(r).serializable_hash }
|
9
|
+
end
|
10
|
+
|
11
|
+
return self.serializable_hash
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Alias for convenience.
|
18
|
+
# rubocop:disable Layout/LineLength
|
19
|
+
RESTFramework::ActiveModelSerializerAdapterFactory = RESTFramework::Serializers::ActiveModelSerializerAdapterFactory
|
20
|
+
# rubocop:enable Layout/LineLength
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# The base serializer defines the interface for all REST Framework serializers.
|
2
|
+
class RESTFramework::Serializers::BaseSerializer
|
3
|
+
# Add `object` accessor to be compatible with `ActiveModel::Serializer`.
|
4
|
+
attr_accessor :object
|
5
|
+
|
6
|
+
# Accept/ignore `*args` to be compatible with the `ActiveModel::Serializer#initialize` signature.
|
7
|
+
def initialize(object=nil, *args, controller: nil, **kwargs)
|
8
|
+
@object = object
|
9
|
+
@controller = controller
|
10
|
+
end
|
11
|
+
|
12
|
+
# The primary interface for extracting a native Ruby types. This works both for records and
|
13
|
+
# collections. We accept and ignore `*args` for compatibility with `active_model_serializers`.
|
14
|
+
def serialize(*args)
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
# Synonym for `serialize` for compatibility with `active_model_serializers`.
|
19
|
+
def serializable_hash(*args)
|
20
|
+
return self.serialize(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
# For compatibility with `active_model_serializers`.
|
24
|
+
def self.cache_enabled?
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
|
28
|
+
# For compatibility with `active_model_serializers`.
|
29
|
+
def self.fragment_cache_enabled?
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
|
33
|
+
# For compatibility with `active_model_serializers`.
|
34
|
+
def associations(*args, **kwargs)
|
35
|
+
return []
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Alias for convenience.
|
40
|
+
RESTFramework::BaseSerializer = RESTFramework::Serializers::BaseSerializer
|