rest_framework 0.9.7 → 0.9.8
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 +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
|