fetcheable_on_api 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6f6a27e74d75387bd75d978d8992ef31d2338c4b94685323f006efc42161400
4
- data.tar.gz: 506acb6f864eec138f3f91ce4715de0b86caaf603cffd3ec32d822888e1002df
3
+ metadata.gz: 936c435eaac09ab3f50c5fe2fc650df5f857f99077a3a7cd24c9a7cbfa16bbeb
4
+ data.tar.gz: 5f029dd6d1fe43c945f0740b947ce0242444a6d6b21a556bc93c6c43295f02fd
5
5
  SHA512:
6
- metadata.gz: bea41ab188e81170e282027ed91c0fec55dc13e0c94b8ebd59cdbd819a00eb0809454d1f4660520077f3fb70614d837ac6a842e5084d7fd78145d23b05d47425
7
- data.tar.gz: deeaaba6a658b4ec43ebbc29a4478166212576997a293d131992ecb3937e7dd4dc36e477ba5f54fcbc36a6fa127f5df1f98deff0a090cb00d75a308ce6dc165a
6
+ metadata.gz: 5e11946ac5da7685b8a10e5638fd05c9dd5d55f5cf95309ccd2398e4b597ccd324ce68b3bdb307b8ed305500797f8a758af2c1b805ffdd671e7ad3ca407810bc
7
+ data.tar.gz: f60417bd41596796aba7191c24a5dcd2b077945b50ae11dccee646a8d0c6feb539b7b8aa5198e96b83b6eb19c0079f36f1ffefd28d83baadbeb21b37adeb2156
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fetcheable_on_api (0.1.4)
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.1)
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.16.6
66
+ 1.17.1
@@ -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
- def foa_valid_parameters!(*keys, foa_permitted_types: foa_default_permitted_types)
61
- raise FetcheableOnApi::ArgumentError.new(
62
- "Incorrect type #{params.dig(*keys).class} for params #{keys}"
63
- ) unless foa_valid_params_types(*keys, foa_permitted_types: foa_permitted_types)
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(*keys, foa_permitted_types: foa_default_permitted_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,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FetcheableOnApi
4
+ # Default configuration
4
5
  class Configuration
5
6
  attr_accessor :pagination_default_size
6
7
 
@@ -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
- :does_not_match_all,
10
- :does_not_match_any,
11
- :eq_all,
12
- :eq_any,
13
- :gt_all,
14
- :gt_any,
15
- :gteq_all,
16
- :gteq_any,
17
- :in_all,
18
- :in_any,
19
- :lt_all,
20
- :lt_any,
21
- :lteq_all,
22
- :lteq_any,
23
- :matches_all,
24
- :matches_any,
25
- :not_eq_all,
26
- :not_eq_any,
27
- :not_in_all,
28
- :not_in_any
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] = filters_configuration[attr].merge(options)
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 try_parse_array(values, format)
70
- # array = JSON.parse(values)
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) || PREDICATES_WITH_ARRAY.exclude?(predicate.to_sym)
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
- raise ArgumentError, "unsupported predicate `#{predicate}`" unless predicate.respond_to?(:call)
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 = params[:page].fetch(
27
- :size,
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
- offset = (params[:page].fetch(:number, 1).to_i - 1) * limit
32
- count = collection.except(:offset, :limit, :order).count
31
+ collection.limit(limit).offset(offset)
32
+ end
33
+
34
+ private
33
35
 
34
- response.headers['Pagination-Current-Page'] = params[:page].fetch(:number, 1)
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
- collection.limit(limit).offset(offset)
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, :instance_writer => false
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] = self.sorts_configuration[attr].merge(options)
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
- clean_params(params[:sort]).each do |attr|
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
- sort_sign = (attr =~ /\A[+-]/) ? attr.slice!(0) : '+'
62
- ordering << klass
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 clean_params(params)
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
- .select { |e| sorts_configuration.keys.map(&:to_s).include?(e) }
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,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FetcheableOnApi
4
- VERSION = '0.2.1'.freeze
4
+ VERSION = '0.2.2'.freeze
5
5
  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("../../templates", __FILE__)
5
- desc "Creates FetcheableOnApi initializer for your application"
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 "fetcheable_on_api_initializer.rb", "config/initializers/fetcheable_on_api.rb"
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.1
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-08 00:00:00.000000000 Z
11
+ date: 2019-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport