active_queryable 0.2.0 → 0.3.1

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: 85318f4379a335f5d2398c1b85ab19ddb63cfbfa6955882c4dcd9e7a24a5168f
4
- data.tar.gz: d9dbb94f9c174af1a34b680152b8add196cf1ddf1a38a895fc0f32370855a985
3
+ metadata.gz: ca76d46d098d260a1f59a1fd6e50d59cbab69eb95ba87f25470ccb08aa3050f2
4
+ data.tar.gz: 9562f2b4d7a4cce6ba1ba6bb070c6e6d9cf031d6205bf1006e5b5c1f21566ed4
5
5
  SHA512:
6
- metadata.gz: f64dda266de6dd21f1c6a21fae9bfba014a72a16f7acf6c560f6feea3771b4663db82b11b97c97fc751379963aa2b0b29b26a6a34339d2f8dade38576ee5868b
7
- data.tar.gz: 89bad17c363651ddbc6c75ca6eeda418d436682c9376a1c9670e81bbc959e491b5a3b0acc1345cf71879ea01ad5ccf623fc8624a00e698c34a8131e1f1730c63
6
+ metadata.gz: 5ec24780124afa2fd02e2d6bdf9a4f54f6dd980c8852adc43c4ae37144f887d54eccbdebf5e8d50765b3cc3e051762f4c1e62f7006a3a5385d5ed6962e3ce22f
7
+ data.tar.gz: aaf987e005a9f87b39ae00f63adaf7791a11b0facc962ae2f9b70e541e74f7b0281e1d71e6428ba71b97c7ba924dd20cf08b126e960430f9ecf8ef88d0ec9d0d
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveQueryable
2
- VERSION = '0.2.0'
4
+ # @return [String] the version of the gem
5
+ VERSION = '0.3.1'
3
6
  end
@@ -1,41 +1,110 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_support/concern'
4
+ require 'active_record'
4
5
  require 'kaminari/activerecord'
5
6
 
7
+ # A light and simple gem for sorting / filtering / paginating a model in Rails.
6
8
  module ActiveQueryable
7
- extend ActiveSupport::Concern
9
+ extend ActiveSupport::Concern
8
10
 
9
- QUERYABLE_VALID_PARAMS = [:filter, :sort, :page, :per].freeze
11
+ # @private
12
+ QUERYABLE_VALID_PARAMS = %i[filter sort page per].freeze
10
13
 
11
14
  included do
12
15
  class_attribute :_queryable_default_order
13
16
  class_attribute :_queryable_default_page
14
17
  class_attribute :_queryable_default_per
15
18
  class_attribute :_queryable_filter_keys
16
- end
19
+ class_attribute :_queryable_expandable_filter_keys
20
+ end
17
21
 
18
- module Initializer
22
+ # @private
23
+ module Initializer
24
+ # Enables ActiveQueryable for the model
25
+ # @example
26
+ # class User < ApplicationRecord
27
+ # as_queryable
28
+ # end
29
+ #
30
+ # @!parse include ActiveQueryable
31
+ # @return [void]
19
32
  def as_queryable
20
- send :include, ActiveQueryable
21
- end
22
- end
33
+ send :include, ActiveQueryable
34
+ end
35
+ end
23
36
 
37
+ # Extension methods for ActiveRecord::Base
38
+ # @!method query_by(params)
39
+ # Runs a query with the given params
40
+ # @example
41
+ # User.query_by(sort: '-name', filter: { name: 'John' })
42
+ # @param params [Hash,ActionController::Parameters]
43
+ # @option params [String] :sort
44
+ # @option params [Hash] :filter
45
+ # @option params [String] :page
46
+ # @option params [String] :per
47
+ # @option params [Hash] :page
48
+ # @return [ActiveRecord::Relation]
49
+ # @!method of_not(ids)
50
+ # @example
51
+ # User.of_not([1, 2, 3]) # => equivalent of: User.where.not(id: [1, 2, 3])
52
+ # Returns a scope with the given ids excluded
53
+ # @param ids [Array<Integer,String>]
54
+ # @return [ActiveRecord::Relation]
24
55
  module ClassMethods
56
+ # Configures the model to be queryable
57
+ # @example
58
+ # class User < ApplicationRecord
59
+ # as_queryable
60
+ # queryable order: { name: :asc }, page: 1, per: 25, filter: %i[name email]
61
+ # end
62
+ #
63
+ # @param options [Hash]
64
+ # @option options [Hash<Symbol,Symbol>] :order { id: :asc }
65
+ # @option options [Integer] :page 1
66
+ # @option options [Integer] :per 25
67
+ # @option options [Array<Symbol,String>] :filter []
68
+ # @return [void]
25
69
  def queryable(options)
26
- self._queryable_default_order = options[:order] || { id: :asc }
27
- self._queryable_default_page = options[:page] || 1
28
- self._queryable_default_per = options[:per] || 25
29
- self._queryable_filter_keys = ((options[:filter] || []) + ['not']).map(&:to_sym)
70
+ queryable_configure_options(options)
30
71
 
31
72
  scope :query_by, ->(params) { queryable_scope(params) }
32
73
  scope :of_not, ->(ids) { where.not(id: ids) }
33
74
  end
34
75
 
76
+ # A method to expand the filterable keys, useful to allow class inheritance
77
+ # @example
78
+ # class BaseItem < ApplicationRecord
79
+ # as_queryable
80
+ # queryable order: { name: :asc }, page: 1, per: 25, filter: %i[name email]
81
+ # end
82
+ # class AdvancedItem < BaseItem
83
+ # expand_queryable filter: %i[phone]
84
+ # end
85
+ #
86
+ #
87
+ # @param options [Hash]
88
+ # @option options [Array<Symbol,String>] :filter []
89
+ # @return [void]
90
+ def expand_queryable(options)
91
+ self._queryable_expandable_filter_keys ||= []
92
+ self._queryable_expandable_filter_keys += ((options[:filter] || [])).map(&:to_sym)
93
+ end
94
+
95
+ # @param params [Hash,ActionController::Parameters]
96
+ # @option params [String] :sort
97
+ # @option params [Hash] :filter
98
+ # @option params [String] :page
99
+ # @option params [String] :per
100
+ # @option params [Hash] :page
101
+ # @return [ActiveRecord::Relation]
35
102
  def queryable_scope(params)
36
103
  params = params.to_unsafe_h if params.respond_to? :to_unsafe_h
37
104
  params = params.with_indifferent_access if params.respond_to?(:with_indifferent_access)
38
- params.each_key { |k| QUERYABLE_VALID_PARAMS.include?(k.to_sym) || Rails.logger.error("Invalid key #{k} in queryable") }
105
+ params.each_key do |k|
106
+ QUERYABLE_VALID_PARAMS.include?(k.to_sym) || Rails.logger.error("Invalid key #{k} in queryable")
107
+ end
39
108
 
40
109
  order_params = queryable_validate_order_params(params[:sort])
41
110
  query = queryable_parse_order_scope(order_params, self)
@@ -45,6 +114,26 @@ module ActiveQueryable
45
114
 
46
115
  private
47
116
 
117
+ # @option options [Hash<Symbol,Symbol>] :order { id: :asc }
118
+ # @option options [Integer] :page 1
119
+ # @option options [Integer] :per 25
120
+ # @option options [Array<Symbol,String>] :filter []
121
+ # @return [void]
122
+ def queryable_configure_options(options)
123
+ self._queryable_default_order = options[:order] || { id: :asc }
124
+ self._queryable_default_page = options[:page] || 1
125
+ self._queryable_default_per = options[:per] || 25
126
+ self._queryable_filter_keys = (((options[:filter] || [])).map(&:to_sym))
127
+ end
128
+
129
+ # @param params [Hash,ActionController::Parameters]
130
+ # @option params [String] :sort
131
+ # @option params [Hash] :filter
132
+ # @option params [String] :page
133
+ # @option params [String] :per
134
+ # @option params [Hash] :page
135
+ # @param query [ActiveRecord::Relation]
136
+ # @return [ActiveRecord::Relation]
48
137
  def queryable_filtered_scope(params, query)
49
138
  filter_params = queryable_validate_filter_params(params[:filter])
50
139
 
@@ -61,10 +150,17 @@ module ActiveQueryable
61
150
  scope
62
151
  end
63
152
 
153
+ # @param params [String,nil]
154
+ # @return [Hash]
64
155
  def queryable_validate_order_params(params)
65
156
  queryable_parse_order_params(params) || _queryable_default_order
66
157
  end
67
158
 
159
+ # @param params [Hash,ActionController::Parameters]
160
+ # @option params [String] :page
161
+ # @option params [String] :per
162
+ # @option params [Hash] :page
163
+ # @return [Hash]
68
164
  def queryable_validate_page_params(params)
69
165
  page_params = {}
70
166
  if params[:page].respond_to?(:dig)
@@ -77,15 +173,20 @@ module ActiveQueryable
77
173
  page_params
78
174
  end
79
175
 
176
+ # @param filter_params [Hash,ActionController::Parameters,nil]
177
+ # @return [Hash]
80
178
  def queryable_validate_filter_params(filter_params)
81
179
  return nil if filter_params.nil?
82
180
 
83
- unpermitted = filter_params.except(*_queryable_filter_keys)
181
+ filters = (((_queryable_filter_keys || []) | (self._queryable_expandable_filter_keys || [])) + ['not']).map(&:to_sym)
182
+ unpermitted = filter_params.except(*filters)
84
183
  Rails.logger.warn("Unpermitted queryable parameters: #{unpermitted.keys.join(', ')}") if unpermitted.present?
85
184
 
86
- filter_params.slice(*_queryable_filter_keys)
185
+ filter_params.slice(*filters)
87
186
  end
88
187
 
188
+ # @param params [String,nil]
189
+ # @return [Hash]
89
190
  def queryable_parse_order_params(params)
90
191
  return nil unless params.is_a? String
91
192
 
@@ -95,6 +196,9 @@ module ActiveQueryable
95
196
  end.to_h
96
197
  end
97
198
 
199
+ # @param params [Hash,ActionController::Parameters,nil]
200
+ # @param query [ActiveRecord::Relation]
201
+ # @return [ActiveRecord::Relation]
98
202
  def queryable_parse_order_scope(params, query)
99
203
  return query unless params
100
204
 
@@ -109,13 +213,15 @@ module ActiveQueryable
109
213
  end || query
110
214
  end
111
215
 
216
+ # @param params [Hash,ActionController::Parameters,nil]
217
+ # @param query [ActiveRecord::Relation]
218
+ # @return [ActiveRecord::Relation]
112
219
  def queryable_parse_filter_scope(params, query)
113
220
  return query unless params
114
221
 
115
222
  params.inject(query) do |current_query, (k, v)|
116
223
  scope = "of_#{k}"
117
224
 
118
-
119
225
  if current_query.respond_to?(scope, true)
120
226
  current_query.public_send(scope, v)
121
227
  else
@@ -125,4 +231,7 @@ module ActiveQueryable
125
231
  end
126
232
  end
127
233
  end
128
- ActiveRecord::Base.send :extend, ActiveQueryable::Initializer
234
+
235
+ # rubocop:disable Lint/SendWithMixinArgument
236
+ ActiveRecord::Base.send(:extend, ActiveQueryable::Initializer)
237
+ # rubocop:enable Lint/SendWithMixinArgument
@@ -3,8 +3,9 @@ require 'spec_helper'
3
3
  describe 'Filters' do
4
4
  context 'default filters' do
5
5
  it 'applies column name-based filters' do
6
- query = Person.query_by(filter: { name: 'john doe' }, per: 'all')
6
+ query = Person.query_by(filter: { name: 'john doe', email: 'e@mail.com' }, per: 'all')
7
7
  expect(query.to_sql).to include('"people"."name" = \'john doe\'')
8
+ expect(query.to_sql).to include('"people"."email" = \'e@mail.com\'')
8
9
  expect(query).to include(Person.first)
9
10
  end
10
11
 
@@ -5,9 +5,16 @@ ActiveRecord::Base.establish_connection(
5
5
  database: ':memory:'
6
6
  )
7
7
 
8
- class Person < ActiveRecord::Base
8
+ class Common < ActiveRecord::Base
9
+ self.abstract_class = true
10
+
9
11
  as_queryable
10
- queryable order: { name: :asc }, filter: ['name', 'article_title']
12
+ queryable order: { name: :asc }, filter: ['name']
13
+ end
14
+
15
+ class Person < Common
16
+ expand_queryable filter: ['email']
17
+ expand_queryable filter: ['article_title']
11
18
 
12
19
  has_many :articles
13
20
 
@@ -16,6 +23,7 @@ class Person < ActiveRecord::Base
16
23
 
17
24
  end
18
25
 
26
+
19
27
  class Article < ActiveRecord::Base
20
28
  as_queryable
21
29
  queryable order: { title: :asc }, filter: ['title']
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_queryable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mònade
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '5'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '7'
22
+ version: '8'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: '5'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '7'
32
+ version: '8'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: kaminari-activerecord
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -99,21 +99,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - ">="
101
101
  - !ruby/object:Gem::Version
102
- version: 2.3.0
102
+ version: 2.7.0
103
103
  required_rubygems_version: !ruby/object:Gem::Requirement
104
104
  requirements:
105
105
  - - ">="
106
106
  - !ruby/object:Gem::Version
107
107
  version: '0'
108
108
  requirements: []
109
- rubygems_version: 3.0.3
109
+ rubygems_version: 3.2.33
110
110
  signing_key:
111
111
  specification_version: 4
112
112
  summary: Gem to make easier model's filtering, sorting and pagination
113
113
  test_files:
114
- - spec/spec_helper.rb
114
+ - spec/active_queryable/filters_spec.rb
115
115
  - spec/active_queryable/ordering_spec.rb
116
116
  - spec/active_queryable/pagination_spec.rb
117
- - spec/active_queryable/filters_spec.rb
118
- - spec/support/schema.rb
117
+ - spec/spec_helper.rb
119
118
  - spec/support/match_queries.rb
119
+ - spec/support/schema.rb