fetcheable_on_api 0.2.1 → 0.2.2
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/Gemfile.lock +3 -3
- data/lib/fetcheable_on_api.rb +26 -5
- data/lib/fetcheable_on_api/configuration.rb +1 -0
- data/lib/fetcheable_on_api/filtreable.rb +35 -35
- data/lib/fetcheable_on_api/pagineable.rb +21 -8
- data/lib/fetcheable_on_api/sortable.rb +46 -17
- data/lib/fetcheable_on_api/version.rb +1 -1
- data/lib/generators/fetcheable_on_api/install_generator.rb +5 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 936c435eaac09ab3f50c5fe2fc650df5f857f99077a3a7cd24c9a7cbfa16bbeb
|
4
|
+
data.tar.gz: 5f029dd6d1fe43c945f0740b947ce0242444a6d6b21a556bc93c6c43295f02fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e11946ac5da7685b8a10e5638fd05c9dd5d55f5cf95309ccd2398e4b597ccd324ce68b3bdb307b8ed305500797f8a758af2c1b805ffdd671e7ad3ca407810bc
|
7
|
+
data.tar.gz: f60417bd41596796aba7191c24a5dcd2b077945b50ae11dccee646a8d0c6feb539b7b8aa5198e96b83b6eb19c0079f36f1ffefd28d83baadbeb21b37adeb2156
|
data/Gemfile.lock
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
fetcheable_on_api (0.
|
4
|
+
fetcheable_on_api (0.2.2)
|
5
5
|
activesupport (>= 4.1)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activesupport (5.2.
|
10
|
+
activesupport (5.2.2)
|
11
11
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
12
12
|
i18n (>= 0.7, < 2)
|
13
13
|
minitest (~> 5.1)
|
@@ -63,4 +63,4 @@ DEPENDENCIES
|
|
63
63
|
rubocop (~> 0.59.2)
|
64
64
|
|
65
65
|
BUNDLED WITH
|
66
|
-
1.
|
66
|
+
1.17.1
|
data/lib/fetcheable_on_api.rb
CHANGED
@@ -8,10 +8,16 @@ require 'fetcheable_on_api/version'
|
|
8
8
|
require 'active_support'
|
9
9
|
require 'date'
|
10
10
|
|
11
|
+
# Detects params from url and apply
|
12
|
+
# filters/sort/paginations to your classes.
|
11
13
|
module FetcheableOnApi
|
12
14
|
#
|
13
15
|
# Configuration
|
14
16
|
#
|
17
|
+
# Configures global settings for FetcheableOnApi
|
18
|
+
# FetcheableOnApi.configure do |config|
|
19
|
+
# config.pagination_default_size = 25
|
20
|
+
# end
|
15
21
|
class << self
|
16
22
|
attr_accessor :configuration
|
17
23
|
end
|
@@ -50,6 +56,7 @@ module FetcheableOnApi
|
|
50
56
|
#
|
51
57
|
protected
|
52
58
|
|
59
|
+
# Apply filters, sort and page on a collection.
|
53
60
|
def apply_fetcheable(collection)
|
54
61
|
collection = apply_filters(collection)
|
55
62
|
collection = apply_sort(collection)
|
@@ -57,26 +64,40 @@ module FetcheableOnApi
|
|
57
64
|
apply_pagination(collection)
|
58
65
|
end
|
59
66
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
67
|
+
# Checks if the type of arguments is included in the permitted types
|
68
|
+
def foa_valid_parameters!(
|
69
|
+
*keys, foa_permitted_types: foa_default_permitted_types
|
70
|
+
)
|
71
|
+
return if foa_valid_params_types(
|
72
|
+
*keys,
|
73
|
+
foa_permitted_types: foa_permitted_types
|
74
|
+
)
|
75
|
+
|
76
|
+
raise FetcheableOnApi::ArgumentError,
|
77
|
+
"Incorrect type #{params.dig(*keys).class} for params #{keys}"
|
64
78
|
end
|
65
79
|
|
66
|
-
def foa_valid_params_types(
|
80
|
+
def foa_valid_params_types(
|
81
|
+
*keys, foa_permitted_types: foa_default_permitted_types
|
82
|
+
)
|
67
83
|
foa_permitted_types.inject(false) do |res, type|
|
68
84
|
res || foa_valid_params_type(params.dig(*keys), type)
|
69
85
|
end
|
70
86
|
end
|
71
87
|
|
88
|
+
# Returns true if class is the class of value,
|
89
|
+
# or if class is one of the superclasses of value
|
90
|
+
# or modules included in value.
|
72
91
|
def foa_valid_params_type(value, type)
|
73
92
|
value.is_a?(type)
|
74
93
|
end
|
75
94
|
|
95
|
+
# Types allowed by default.
|
76
96
|
def foa_default_permitted_types
|
77
97
|
[ActionController::Parameters, Hash]
|
78
98
|
end
|
79
99
|
|
100
|
+
# Convert string to datetime.
|
80
101
|
def foa_string_to_datetime(string)
|
81
102
|
DateTime.strptime(string, '%s')
|
82
103
|
end
|
@@ -1,31 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module FetcheableOnApi
|
4
|
+
# Applying the filter to a collection.
|
4
5
|
module Filtreable
|
5
6
|
#
|
6
7
|
# Supports
|
7
8
|
#
|
8
|
-
PREDICATES_WITH_ARRAY = [
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
9
|
+
PREDICATES_WITH_ARRAY = %i[
|
10
|
+
does_not_match_all
|
11
|
+
does_not_match_any
|
12
|
+
eq_all
|
13
|
+
eq_any
|
14
|
+
gt_all
|
15
|
+
gt_any
|
16
|
+
gteq_all
|
17
|
+
gteq_any
|
18
|
+
in_all
|
19
|
+
in_any
|
20
|
+
lt_all
|
21
|
+
lt_any
|
22
|
+
lteq_all
|
23
|
+
lteq_any
|
24
|
+
matches_all
|
25
|
+
matches_any
|
26
|
+
not_eq_all
|
27
|
+
not_eq_any
|
28
|
+
not_in_all
|
29
|
+
not_in_any
|
29
30
|
].freeze
|
30
31
|
|
31
32
|
#
|
@@ -39,6 +40,7 @@ module FetcheableOnApi
|
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
43
|
+
# Detects url parameters and applies the filter
|
42
44
|
module ClassMethods
|
43
45
|
def filter_by(*attrs)
|
44
46
|
options = attrs.extract_options!
|
@@ -52,7 +54,7 @@ module FetcheableOnApi
|
|
52
54
|
as: options[:as] || attr
|
53
55
|
}
|
54
56
|
|
55
|
-
filters_configuration[attr]
|
57
|
+
filters_configuration[attr].merge!(options)
|
56
58
|
end
|
57
59
|
end
|
58
60
|
end
|
@@ -66,24 +68,16 @@ module FetcheableOnApi
|
|
66
68
|
#
|
67
69
|
protected
|
68
70
|
|
69
|
-
# def
|
70
|
-
# array
|
71
|
-
# array.map! { |el| foa_string_to_datetime(el.to_s) } if format == :datetime
|
72
|
-
|
73
|
-
# [array]
|
74
|
-
# rescue JSON::ParserError
|
75
|
-
# nil
|
71
|
+
# def convert_to_datetime(array)
|
72
|
+
# array.map { |el| foa_string_to_datetime(el.to_s) }
|
76
73
|
# end
|
77
74
|
|
78
|
-
def convert_to_datetime(array)
|
79
|
-
array.map { |el| foa_string_to_datetime(el.to_s) }
|
80
|
-
end
|
81
|
-
|
82
75
|
def valid_keys
|
83
76
|
keys = filters_configuration.keys
|
84
77
|
keys.each_with_index do |key, index|
|
85
78
|
predicate = filters_configuration[key.to_sym].fetch(:with, :ilike)
|
86
|
-
next if predicate.respond_to?(:call) ||
|
79
|
+
next if predicate.respond_to?(:call) ||
|
80
|
+
PREDICATES_WITH_ARRAY.exclude?(predicate.to_sym)
|
87
81
|
|
88
82
|
keys[index] = { key => [] }
|
89
83
|
end
|
@@ -93,6 +87,7 @@ module FetcheableOnApi
|
|
93
87
|
|
94
88
|
def apply_filters(collection)
|
95
89
|
return collection if params[:filter].blank?
|
90
|
+
|
96
91
|
foa_valid_parameters!(:filter)
|
97
92
|
|
98
93
|
filter_params = params.require(:filter)
|
@@ -118,6 +113,7 @@ module FetcheableOnApi
|
|
118
113
|
collection.where(filtering.flatten.compact.inject(:and))
|
119
114
|
end
|
120
115
|
|
116
|
+
# Apply arel predicate on collection
|
121
117
|
def predicates(predicate, collection, klass, column_name, value)
|
122
118
|
case predicate
|
123
119
|
when :between
|
@@ -187,12 +183,16 @@ module FetcheableOnApi
|
|
187
183
|
when :not_in_any
|
188
184
|
klass.arel_table[column_name].not_in_any(value)
|
189
185
|
else
|
190
|
-
|
186
|
+
unless predicate.respond_to?(:call)
|
187
|
+
raise ArgumentError,
|
188
|
+
"unsupported predicate `#{predicate}`"
|
189
|
+
end
|
191
190
|
|
192
191
|
predicate.call(collection, value)
|
193
192
|
end
|
194
193
|
end
|
195
194
|
|
195
|
+
# Types allowed by default for filter action.
|
196
196
|
def foa_default_permitted_types
|
197
197
|
[ActionController::Parameters, Hash, Array]
|
198
198
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module FetcheableOnApi
|
4
|
+
# Application of a pagination on a collection
|
4
5
|
module Pagineable
|
5
6
|
#
|
6
7
|
# Supports
|
@@ -21,22 +22,34 @@ module FetcheableOnApi
|
|
21
22
|
|
22
23
|
def apply_pagination(collection)
|
23
24
|
return collection if params[:page].blank?
|
25
|
+
|
24
26
|
foa_valid_parameters!(:page)
|
25
27
|
|
26
|
-
limit =
|
27
|
-
|
28
|
-
FetcheableOnApi.configuration.pagination_default_size
|
29
|
-
).to_i
|
28
|
+
limit, offset, count, page = extract_pagination_informations(collection)
|
29
|
+
define_header_pagination(limit, count, page)
|
30
30
|
|
31
|
-
|
32
|
-
|
31
|
+
collection.limit(limit).offset(offset)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
33
35
|
|
34
|
-
|
36
|
+
def define_header_pagination(limit, count, page)
|
37
|
+
response.headers['Pagination-Current-Page'] = page
|
35
38
|
response.headers['Pagination-Per'] = limit
|
36
39
|
response.headers['Pagination-Total-Pages'] = 1 + (count / limit).ceil
|
37
40
|
response.headers['Pagination-Total-Count'] = count
|
41
|
+
end
|
38
42
|
|
39
|
-
|
43
|
+
def extract_pagination_informations(collection)
|
44
|
+
limit = params[:page].fetch(
|
45
|
+
:size, FetcheableOnApi.configuration.pagination_default_size
|
46
|
+
).to_i
|
47
|
+
|
48
|
+
page = params[:page].fetch(:number, 1).to_i
|
49
|
+
offset = (page - 1) * limit
|
50
|
+
count = collection.except(:offset, :limit, :order).count
|
51
|
+
|
52
|
+
[limit, offset, count, page]
|
40
53
|
end
|
41
54
|
end
|
42
55
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module FetcheableOnApi
|
4
|
+
# Application of a sorting on a collection
|
4
5
|
module Sortable
|
5
6
|
#
|
6
7
|
# Supports
|
@@ -8,7 +9,7 @@ module FetcheableOnApi
|
|
8
9
|
SORT_ORDER = {
|
9
10
|
'+' => :asc,
|
10
11
|
'-' => :desc
|
11
|
-
}
|
12
|
+
}.freeze
|
12
13
|
|
13
14
|
#
|
14
15
|
# Public class methods
|
@@ -16,11 +17,12 @@ module FetcheableOnApi
|
|
16
17
|
def self.included(base)
|
17
18
|
base.class_eval do
|
18
19
|
extend ClassMethods
|
19
|
-
class_attribute :sorts_configuration, :
|
20
|
+
class_attribute :sorts_configuration, instance_writer: false
|
20
21
|
self.sorts_configuration = {}
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
25
|
+
# Detects url parameters and applies sorting
|
24
26
|
module ClassMethods
|
25
27
|
def sort_by(*attrs)
|
26
28
|
options = attrs.extract_options!
|
@@ -33,7 +35,7 @@ module FetcheableOnApi
|
|
33
35
|
as: attr
|
34
36
|
}
|
35
37
|
|
36
|
-
sorts_configuration[attr] =
|
38
|
+
sorts_configuration[attr] = sorts_configuration[attr].merge(options)
|
37
39
|
end
|
38
40
|
end
|
39
41
|
end
|
@@ -49,30 +51,57 @@ module FetcheableOnApi
|
|
49
51
|
|
50
52
|
def apply_sort(collection)
|
51
53
|
return collection if params[:sort].blank?
|
52
|
-
foa_valid_parameters!(:sort, foa_permitted_types: [String])
|
53
|
-
|
54
|
-
ordering = []
|
55
54
|
|
56
|
-
|
57
|
-
klass = sorts_configuration[attr.to_sym].fetch(:class_name, collection.klass)
|
58
|
-
field = sorts_configuration[attr.to_sym].fetch(:as, attr.to_sym).to_s
|
59
|
-
next unless klass.attribute_names.include?(field)
|
55
|
+
foa_valid_parameters!(:sort, foa_permitted_types: [String])
|
60
56
|
|
61
|
-
|
62
|
-
|
63
|
-
.arel_table[field]
|
64
|
-
.send(SORT_ORDER[sort_sign])
|
57
|
+
ordering = format_params(params[:sort]).map do |attr_name, sort_method|
|
58
|
+
arel_sort(attr_name, sort_method)
|
65
59
|
end
|
66
60
|
|
67
|
-
collection.order(ordering)
|
61
|
+
collection.order(ordering.compact)
|
68
62
|
end
|
69
63
|
|
70
64
|
private
|
71
65
|
|
72
|
-
def
|
66
|
+
def arel_sort(attr_name, sort_method)
|
67
|
+
return if sorts_configuration[attr_name].blank?
|
68
|
+
|
69
|
+
klass = class_for(attr_name, collection)
|
70
|
+
field = field_for(attr_name)
|
71
|
+
return unless belong_to_attributes_for?(klass, field)
|
72
|
+
|
73
|
+
klass.arel_table[field].send(sort_method)
|
74
|
+
end
|
75
|
+
|
76
|
+
def class_for(attr_name, collection)
|
77
|
+
sorts_configuration[attr_name].fetch(:class_name, collection.klass)
|
78
|
+
end
|
79
|
+
|
80
|
+
def field_for(attr_name)
|
81
|
+
sorts_configuration[attr_name].fetch(:as, attr_name).to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
def belong_to_attributes_for?(klass, field)
|
85
|
+
klass.attribute_names.include?(field)
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# input: "-email,first_name"
|
90
|
+
# return: { email: :desc, first_name: :asc }
|
91
|
+
#
|
92
|
+
def format_params(params)
|
93
|
+
res = {}
|
94
|
+
|
73
95
|
params
|
74
96
|
.split(',')
|
75
|
-
.
|
97
|
+
.each do |attribute|
|
98
|
+
res[attribute.to_sym] = SORT_ORDER[sort_sign(attribute)]
|
99
|
+
end
|
100
|
+
res
|
101
|
+
end
|
102
|
+
|
103
|
+
def sort_sign(attribute)
|
104
|
+
attribute =~ /\A[+-]/ ? attribute.slice!(0) : '+'
|
76
105
|
end
|
77
106
|
end
|
78
107
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
module FetcheableOnApi
|
2
2
|
module Generators
|
3
|
+
# Create conf file
|
3
4
|
class InstallGenerator < Rails::Generators::Base
|
4
|
-
source_root File.expand_path(
|
5
|
-
desc
|
5
|
+
source_root File.expand_path('../templates', __dir__)
|
6
|
+
desc 'Creates FetcheableOnApi initializer for your application'
|
6
7
|
|
7
8
|
def copy_initializer
|
8
|
-
template
|
9
|
+
template 'fetcheable_on_api_initializer.rb',
|
10
|
+
'config/initializers/fetcheable_on_api.rb'
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fetcheable_on_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fabien
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-01-
|
11
|
+
date: 2019-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|