active_queryable 0.2.0 → 0.3.1

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 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