active_hash_relation 1.2.0 → 1.4.0

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
  SHA1:
3
- metadata.gz: a59228314ab4ecac89c7ba2f988514e453d2b434
4
- data.tar.gz: 16316aba61f2ff203189607d69fd9e738649187d
3
+ metadata.gz: 3ba8d3aaed9463815f83717410dabf1d47fd2959
4
+ data.tar.gz: 14e4eea13e73d7f60ada9f9313a0739e3311fb0f
5
5
  SHA512:
6
- metadata.gz: 67342eb6d946bea38b57f1b3e1f99f389c815fef0f6c89445c806ccde2750b18b33810d8cf60ac7b982281de08ad3564f329147d9d6b18be31070345a64f445e
7
- data.tar.gz: aa938f3c84a0054be00d9af1fe80cda17ef4a9e0f249bd4d91a23d065230d3052cb7f8da81a215171d54e4226c959bc0fe7cefad328be807ea0977f914e27998
6
+ metadata.gz: 608d20958f7fd3f37e121abbe57c7c4aa546af10112edcd27e47cbc05f0e702139e6cc23e3bced226c7af8eee354d886b779ffb7c7cd385b639d063c7609ad35
7
+ data.tar.gz: ebfd652d954d006b0fc13eb4b4b146f95dce213821d52a78f661f7b1a5a80f2a3a9f6723893c5440fae771526bbf45afb36348b47b8d329ef1b0f24db1c781d4
data/README.md CHANGED
@@ -1,36 +1,38 @@
1
1
  # ActiveHashRelation
2
2
  [ ![Codeship Status for kollegorna/active_hash_relation](https://app.codeship.com/projects/02f08850-cc66-0134-028f-5ad72e690a75/status?branch=master)](https://app.codeship.com/projects/200194)
3
3
 
4
+ ActiveHashRelation is a complete substitute of ActiveRecord::Relation that allows you to run ActiveRecord queries using only regular Hash using a very powerful yet simple API. It was initially built to allow front-end teams to specify from the API exactly what they need withoug bugging the backend developers, eventually emerged into its own little gem.
5
+
4
6
  ## Introduction
5
7
  Simple gem that allows you to manipulate ActiveRecord::Relation using JSON. For instance:
6
8
  ```ruby
7
- apply_filters(resource, {name: 'RPK', id: [1,2,3,4,5,6,7,8,9], start_date: {leq: "2014-10-19"}, act_status: "ongoing"})
9
+ apply_filters(User.all, {name: 'filippos', created_at: {leq: "2016-10-19"}, role: 'regular', email: {like: 'vasilakis'}})
8
10
  ```
9
- filter a resource based on it's associations:
11
+ filter a resource based on it's associations using a NOT filter:
10
12
  ```ruby
11
- apply_filters(resource, {updated_at: { geq: "2014-11-2 14:25:04"}, unit: {id: 9})
13
+ apply_filters(Microposts.all, {updated_at: { geq: "2014-11-2 14:25:04"}, user: {not: {email: vasilakisfil@gmail.com}})
12
14
  ```
13
- or even filter a resource based on it's associations' associations:
15
+ or even filter a resource based on it's associations' associations using an OR filter:
14
16
  ```ruby
15
- apply_filters(resource, {updated_at: { geq: "2014-11-2 14:25:04"}, unit: {id: 9, areas: {or: [{id: 22}, {id: 21}]} }})
17
+ apply_filters(Comments.all, {updated_at: { geq: "2014-11-2 14:25:04"}, user: {id: 9, units: {or: [{id: 22}, {id: 21}]} }})
16
18
  ```
17
- and the list could go on.. Basically your whole db is exposed\* there. It's perfect for filtering a collection of resources on APIs.
19
+ and the list could go on.. Basically you can run anything ActiveRecord supports, except from groupping. It's perfect for filtering a collection of resources on APIs.
18
20
 
19
21
  It should be noted that `apply_filters` calls `ActiveHashRelation::FilterApplier` class
20
22
  underneath with the same params.
21
23
 
22
- _\*Actually nothing is exposed, but a user could retrieve resources based
24
+ You can also do [__aggregation queries__](#aggregation-queries), like `sum`, `avg`, `min` and `max` on any column.
25
+
26
+ _\*A user could retrieve resources based
23
27
  on unknown attributes (attributes not returned from the API) by brute forcing
24
- which might or might not be a security issue. If you don't like that check
25
- [whitelisting](#whitelisting)._
28
+ which might or might not be a security issue. If you don't like that check you can specify from the `params` exactly what is allowed and what is not allowed. For more information check: [whitelisting](#whitelisting)._
26
29
 
27
- *New*! You can now do [__aggregation queries__](#aggregation-queries).
28
30
 
29
31
  ## Installation
30
32
 
31
33
  Add this line to your application's Gemfile:
32
34
 
33
- gem 'active_hash_relation', '~> 1.2.0
35
+ gem 'active_hash_relation', '~> 1.3.0
34
36
 
35
37
  And then execute:
36
38
 
@@ -61,7 +63,8 @@ class Api::V1::ResourceController < Api::V1::BaseController
61
63
  end
62
64
  ```
63
65
 
64
- If you **need to enable filtering on scopes**, you need to specify that explicitly from the initializer. For instance:
66
+ If you **need to enable filtering on scopes**, you need to specify that explicitly from the initializer. Please run:
67
+ `bundle exec rails g active_hash_relation:initialize` which will create an initializer with the following content:
65
68
 
66
69
  ```ruby
67
70
  ActiveHashRelation.configure do |config|
@@ -79,6 +82,7 @@ end
79
82
  #requires monkeyparched scopes, optional if you don't enable them
80
83
  ActiveHashRelation.initialize!
81
84
  ```
85
+ If you are not using Rails, just add the code above in your equivelant initialize block.
82
86
 
83
87
  ## The API
84
88
  ### Columns
@@ -140,8 +144,20 @@ However I would strongly advice you to use a pagination gem like Kaminari, and u
140
144
 
141
145
 
142
146
  ### Sorting
147
+ You can apply sorting using the property as the key of the hash and order as the value. For instance:
148
+ * `{sort: {created_at: desc}}`
149
+
150
+ You can also order by multiple attributes:
151
+ * `{sort: {created_at: desc, microposts_count: :asc}}`
152
+
153
+ If there is no column named after the property value, sorting is skipped.
154
+
155
+ #### Deprecated API (will be removed in version 2.0)
143
156
  You can apply sorting using the `property` and `order` attributes. For instance:
144
- * `{sort: {property: 'created_at', order: 'desc'}}`
157
+ * `{sort: {property: :created_at, order: :desc}}`
158
+
159
+ You can also order by multiple attributes:
160
+ * `{sort: [{property: :created_at, order: :desc}, {property: :created_at, order: :desc}]}`
145
161
 
146
162
  If there is no column named after the property value, sorting is skipped.
147
163
 
@@ -174,8 +190,21 @@ You can apply an `OR` on associations as well or even nested ones, there isn't m
174
190
  I suggest you though to take a look on the [tests](spec/tests/or_filter_spec.rb), cause the syntax gets a bit complex after a while ;)
175
191
 
176
192
 
193
+ ### NOT Filter
194
+ You can apply an SQL `NOT` (for ActiveRecord 4+) using the following syntax:
195
+ `{not: {name: 'Filippos', email: {ends_with: '@gmail.com'}}}`
196
+
197
+ It will generate: `WHERE (NOT (users.name = 'Filippos')) AND (NOT (users.email LIKE '%@gmail.com'))`
198
+
199
+ You can apply an `NOT` on associations as well or even nested ones, there isn't much limitation on that.
200
+ I suggest you to also take a look on the [tests](spec/tests/not_filter_spec.rb).
201
+
202
+ Also I should note that you need to add specific (partial) queries if you don't want
203
+ to have performance issues on tables with millions of rows.
204
+
205
+
177
206
  ### Scopes
178
- **Filtering on scopes is not enabled by default**.
207
+ **Filtering on scopes is not enabled by default. You need to add the initializer mentioned in the beginning of the [How to use](#how-to-use) section.**.
179
208
 
180
209
  Scopes are supported via a tiny monkeypatch in the ActiveRecord's scope class method which holds the name of each scope.
181
210
  The monkey patch is as gentle as it can be: it aliases the method, adds some sugar and executes it.
@@ -2,7 +2,11 @@ module ActiveHashRelation::ColumnFilters
2
2
  def filter_integer(resource, column, table_name, param)
3
3
  if param.is_a? Array
4
4
  n_param = param.to_s.gsub("\"","'").gsub("[","").gsub("]","") #fix this!
5
- return resource.where("#{table_name}.#{column} IN (#{n_param})")
5
+ if @is_not
6
+ return resource.where.not("#{table_name}.#{column} IN (#{n_param})")
7
+ else
8
+ return resource.where("#{table_name}.#{column} IN (#{n_param})")
9
+ end
6
10
  elsif param.is_a? Hash
7
11
  if !param[:null].nil?
8
12
  return null_filters(resource, table_name, column, param)
@@ -10,7 +14,11 @@ module ActiveHashRelation::ColumnFilters
10
14
  return apply_leq_geq_le_ge_filters(resource, table_name, column, param)
11
15
  end
12
16
  else
13
- return resource.where("#{table_name}.#{column} = ?", param)
17
+ if @is_not
18
+ return resource.where.not("#{table_name}.#{column} = ?", param)
19
+ else
20
+ return resource.where("#{table_name}.#{column} = ?", param)
21
+ end
14
22
  end
15
23
  end
16
24
 
@@ -25,7 +33,11 @@ module ActiveHashRelation::ColumnFilters
25
33
  def filter_string(resource, column, table_name, param)
26
34
  if param.is_a? Array
27
35
  n_param = param.to_s.gsub("\"","'").gsub("[","").gsub("]","") #fix this!
28
- return resource.where("#{table_name}.#{column} IN (#{n_param})")
36
+ if @is_not
37
+ return resource.where.not("#{table_name}.#{column} IN (#{n_param})")
38
+ else
39
+ return resource.where("#{table_name}.#{column} IN (#{n_param})")
40
+ end
29
41
  elsif param.is_a? Hash
30
42
  if !param[:null].nil?
31
43
  return null_filters(resource, table_name, column, param)
@@ -33,7 +45,11 @@ module ActiveHashRelation::ColumnFilters
33
45
  return apply_like_filters(resource, table_name, column, param)
34
46
  end
35
47
  else
36
- return resource.where("#{table_name}.#{column} = ?", param)
48
+ if @is_not
49
+ return resource.where.not("#{table_name}.#{column} = ?", param)
50
+ else
51
+ return resource.where("#{table_name}.#{column} = ?", param)
52
+ end
37
53
  end
38
54
  end
39
55
 
@@ -44,7 +60,11 @@ module ActiveHashRelation::ColumnFilters
44
60
  def filter_date(resource, column, table_name, param)
45
61
  if param.is_a? Array
46
62
  n_param = param.to_s.gsub("\"","'").gsub("[","").gsub("]","") #fix this!
47
- return resource.where("#{table_name}.#{column} IN (#{n_param})")
63
+ if @is_not
64
+ return resource.where.not("#{table_name}.#{column} IN (#{n_param})")
65
+ else
66
+ return resource.where("#{table_name}.#{column} IN (#{n_param})")
67
+ end
48
68
  elsif param.is_a? Hash
49
69
  if !param[:null].nil?
50
70
  return null_filters(resource, table_name, column, param)
@@ -52,7 +72,11 @@ module ActiveHashRelation::ColumnFilters
52
72
  return apply_leq_geq_le_ge_filters(resource, table_name, column, param)
53
73
  end
54
74
  else
55
- resource = resource.where(column => param)
75
+ if @is_not
76
+ resource = resource.where.not(column => param)
77
+ else
78
+ resource = resource.where(column => param)
79
+ end
56
80
  end
57
81
 
58
82
  return resource
@@ -61,7 +85,11 @@ module ActiveHashRelation::ColumnFilters
61
85
  def filter_datetime(resource, column, table_name, param)
62
86
  if param.is_a? Array
63
87
  n_param = param.to_s.gsub("\"","'").gsub("[","").gsub("]","") #fix this!
64
- return resource = resource.where("#{table_name}.#{column} IN (#{n_param})")
88
+ if @is_not
89
+ return resource = resource.where.not("#{table_name}.#{column} IN (#{n_param})")
90
+ else
91
+ return resource = resource.where("#{table_name}.#{column} IN (#{n_param})")
92
+ end
65
93
  elsif param.is_a? Hash
66
94
  if !param[:null].nil?
67
95
  return null_filters(resource, table_name, column, param)
@@ -69,7 +97,11 @@ module ActiveHashRelation::ColumnFilters
69
97
  return apply_leq_geq_le_ge_filters(resource, table_name, column, param)
70
98
  end
71
99
  else
72
- resource = resource.where(column => param)
100
+ if @is_not
101
+ resource = resource.where.not(column => param)
102
+ else
103
+ resource = resource.where(column => param)
104
+ end
73
105
  end
74
106
 
75
107
  return resource
@@ -85,7 +117,11 @@ module ActiveHashRelation::ColumnFilters
85
117
  b_param = ActiveRecord::Type::Boolean.new.type_cast_from_database(param)
86
118
  end
87
119
 
88
- resource = resource.where(column => b_param)
120
+ if @is_not
121
+ resource = resource.where.not(column => b_param)
122
+ else
123
+ resource = resource.where(column => b_param)
124
+ end
89
125
  end
90
126
  end
91
127
 
@@ -95,15 +131,31 @@ module ActiveHashRelation::ColumnFilters
95
131
  return resource.where("#{table_name}.#{column} = ?", param[:eq]) if param[:eq]
96
132
 
97
133
  if !param[:leq].blank?
98
- resource = resource.where("#{table_name}.#{column} <= ?", param[:leq])
134
+ if @is_not
135
+ resource = resource.where.not("#{table_name}.#{column} <= ?", param[:leq])
136
+ else
137
+ resource = resource.where("#{table_name}.#{column} <= ?", param[:leq])
138
+ end
99
139
  elsif !param[:le].blank?
100
- resource = resource.where("#{table_name}.#{column} < ?", param[:le])
140
+ if @is_not
141
+ resource = resource.where.not("#{table_name}.#{column} < ?", param[:le])
142
+ else
143
+ resource = resource.where("#{table_name}.#{column} < ?", param[:le])
144
+ end
101
145
  end
102
146
 
103
147
  if !param[:geq].blank?
104
- resource = resource.where("#{table_name}.#{column} >= ?", param[:geq])
148
+ if @is_not
149
+ resource = resource.where.not("#{table_name}.#{column} >= ?", param[:geq])
150
+ else
151
+ resource = resource.where("#{table_name}.#{column} >= ?", param[:geq])
152
+ end
105
153
  elsif !param[:ge].blank?
106
- resource = resource.where("#{table_name}.#{column} > ?", param[:ge])
154
+ if @is_not
155
+ resource = resource.where.not("#{table_name}.#{column} > ?", param[:ge])
156
+ else
157
+ resource = resource.where("#{table_name}.#{column} > ?", param[:ge])
158
+ end
107
159
  end
108
160
 
109
161
  return resource
@@ -114,19 +166,35 @@ module ActiveHashRelation::ColumnFilters
114
166
  like_method = "ILIKE" if param[:with_ilike]
115
167
 
116
168
  if !param[:starts_with].blank?
117
- resource = resource.where("#{table_name}.#{column} #{like_method} ?", "#{param[:starts_with]}%")
169
+ if @is_not
170
+ resource = resource.where.not("#{table_name}.#{column} #{like_method} ?", "#{param[:starts_with]}%")
171
+ else
172
+ resource = resource.where("#{table_name}.#{column} #{like_method} ?", "#{param[:starts_with]}%")
173
+ end
118
174
  end
119
175
 
120
176
  if !param[:ends_with].blank?
121
- resource = resource.where("#{table_name}.#{column} #{like_method} ?", "%#{param[:ends_with]}")
177
+ if @is_not
178
+ resource = resource.where.not("#{table_name}.#{column} #{like_method} ?", "%#{param[:ends_with]}")
179
+ else
180
+ resource = resource.where("#{table_name}.#{column} #{like_method} ?", "%#{param[:ends_with]}")
181
+ end
122
182
  end
123
183
 
124
184
  if !param[:like].blank?
125
- resource = resource.where("#{table_name}.#{column} #{like_method} ?", "%#{param[:like]}%")
185
+ if @is_not
186
+ resource = resource.where.not("#{table_name}.#{column} #{like_method} ?", "%#{param[:like]}%")
187
+ else
188
+ resource = resource.where("#{table_name}.#{column} #{like_method} ?", "%#{param[:like]}%")
189
+ end
126
190
  end
127
191
 
128
192
  if !param[:eq].blank?
129
- resource = resource.where("#{table_name}.#{column} = ?", param[:eq])
193
+ if @is_not
194
+ resource = resource.where.not("#{table_name}.#{column} = ?", param[:eq])
195
+ else
196
+ resource = resource.where("#{table_name}.#{column} = ?", param[:eq])
197
+ end
130
198
  end
131
199
 
132
200
  return resource
@@ -134,11 +202,19 @@ module ActiveHashRelation::ColumnFilters
134
202
 
135
203
  def null_filters(resource, table_name, column, param)
136
204
  if param[:null] == true
137
- resource = resource.where("#{table_name}.#{column} IS NULL")
205
+ if @is_not
206
+ resource = resource.where.not("#{table_name}.#{column} IS NULL")
207
+ else
208
+ resource = resource.where("#{table_name}.#{column} IS NULL")
209
+ end
138
210
  end
139
211
 
140
212
  if param[:null] == false
141
- resource = resource.where("#{table_name}.#{column} IS NOT NULL")
213
+ if @is_not
214
+ resource = resource.where.not("#{table_name}.#{column} IS NOT NULL")
215
+ else
216
+ resource = resource.where("#{table_name}.#{column} IS NOT NULL")
217
+ end
142
218
  end
143
219
 
144
220
  return resource
@@ -9,7 +9,7 @@ module ActiveHashRelation
9
9
 
10
10
  attr_reader :configuration
11
11
 
12
- def initialize(resource, params, include_associations: false, model: nil)
12
+ def initialize(resource, params, include_associations: false, model: nil, is_not: false)
13
13
  @configuration = Module.nesting.last.configuration
14
14
  @resource = resource
15
15
  if params.respond_to?(:to_unsafe_h)
@@ -19,10 +19,12 @@ module ActiveHashRelation
19
19
  end
20
20
  @include_associations = include_associations
21
21
  @model = find_model(model)
22
+ is_not ? @is_not = true : @is_not = false
22
23
  end
23
24
 
24
25
  def apply_filters
25
26
  run_or_filters
27
+ run_not_filters
26
28
 
27
29
  table_name = @model.table_name
28
30
  @model.columns.each do |c|
@@ -68,7 +70,7 @@ module ActiveHashRelation
68
70
  def run_or_filters
69
71
  if @params[:or].is_a?(Array)
70
72
  if ActiveRecord::VERSION::MAJOR < 5
71
- return Rails.logger.warn("OR query is supported on ActiveRecord 5+")
73
+ return Rails.logger.warn("OR query is supported on ActiveRecord 5+")
72
74
  end
73
75
 
74
76
  if @params[:or].length >= 2
@@ -83,5 +85,16 @@ module ActiveHashRelation
83
85
  end
84
86
  end
85
87
  end
88
+
89
+ def run_not_filters
90
+ if @params[:not].is_a?(Hash) && !@params[:not].blank?
91
+ @resource = self.class.new(
92
+ @resource,
93
+ @params[:not],
94
+ include_associations: @include_associations,
95
+ is_not: true
96
+ ).apply_filters
97
+ end
98
+ end
86
99
  end
87
100
  end
@@ -12,8 +12,16 @@ module ActiveHashRelation::SortFilters
12
12
  end
13
13
 
14
14
  def apply_hash_sort(resource, params, model = nil)
15
- if model.columns.map(&:name).include?(params[:property].to_s)
16
- resource = resource.order(params[:property] => (params[:order] || :desc) )
15
+ if not params[:property].blank?
16
+ if model.columns.map(&:name).include?(params[:property].to_s)
17
+ resource = resource.order(params[:property] => (params[:order] || :desc) )
18
+ end
19
+ else
20
+ params.each do |property, order|
21
+ if model.columns.map(&:name).include?(property.to_s)
22
+ resource = resource.order(property => (order || :desc) )
23
+ end
24
+ end
17
25
  end
18
26
 
19
27
  return resource
@@ -1,3 +1,3 @@
1
1
  module ActiveHashRelation
2
- VERSION = "1.2.0"
2
+ VERSION = "1.4.0"
3
3
  end
@@ -0,0 +1,105 @@
1
+ describe ActiveHashRelation do
2
+ include Helpers
3
+
4
+ context 'NOT filter' do
5
+ it "one NOT clause" do
6
+ hash = {not: {name: 'Filippos'}}
7
+
8
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
9
+ expected_query = q(
10
+ "SELECT users.* FROM users WHERE (NOT (users.name = 'Filippos'))"
11
+ )
12
+
13
+ expect(strip(query)).to eq expected_query.to_s
14
+ end
15
+
16
+ it "multiple NOT clauses" do
17
+ hash = {not: {name: 'Filippos', email: 'vasilakisfil@gmail.com'}}
18
+
19
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
20
+ expected_query = q(
21
+ "SELECT users.* FROM users WHERE",
22
+ "(NOT (users.name = 'Filippos'))",
23
+ "AND",
24
+ "(NOT (users.email = 'vasilakisfil@gmail.com'))"
25
+ )
26
+
27
+ expect(strip(query)).to eq expected_query.to_s
28
+ end
29
+
30
+ if ActiveRecord::VERSION::MAJOR >= 5
31
+ it "NOT clause inside OR clause" do
32
+ hash = {or: [{not: {name: 'Filippos', token: '123'}}, {not: {name: 'Vasilis'}}]}
33
+
34
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
35
+ expected_query = q(
36
+ "SELECT users.* FROM users",
37
+ "WHERE",
38
+ "(",
39
+ "(NOT (users.name = 'Filippos')) AND (NOT (users.token = '123'))",
40
+ "OR",
41
+ "(NOT (users.name = 'Vasilis'))",
42
+ ")"
43
+ )
44
+
45
+ expect(strip(query)).to eq expected_query.to_s
46
+ end
47
+ end
48
+
49
+ it "complex NOT clause" do
50
+ hash = {not: {name: 'Filippos', email: {ends_with: '@gmail.com'}}}
51
+
52
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
53
+ expected_query = q(
54
+ "SELECT users.* FROM users",
55
+ "WHERE",
56
+ "(NOT (users.name = 'Filippos'))",
57
+ "AND",
58
+ "(NOT (users.email LIKE '%@gmail.com'))",
59
+ )
60
+
61
+ expect(strip(query)).to eq expected_query.to_s
62
+ end
63
+
64
+ context "on NULL" do
65
+ it "NOT clause on null" do
66
+ hash = {not: {name: {null: true}}}
67
+
68
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
69
+ expected_query = q(
70
+ "SELECT users.* FROM users WHERE (NOT (users.name IS NULL))"
71
+ )
72
+
73
+ expect(strip(query)).to eq expected_query.to_s
74
+ end
75
+
76
+ it "NOT clause on not null" do
77
+ hash = {not: {name: {null: false}}}
78
+
79
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
80
+ expected_query = q(
81
+ "SELECT users.* FROM users WHERE (NOT (users.name IS NOT NULL))"
82
+ )
83
+
84
+ expect(strip(query)).to eq expected_query.to_s
85
+ end
86
+ end
87
+
88
+ it "NOT clause on associations" do
89
+ hash = {microposts: {not: {content: 'Sveavägen 4', id: 1}}}
90
+
91
+ query = HelperClass.new.apply_filters(User.all, hash, include_associations: true).to_sql
92
+ expected_query = q(
93
+ "SELECT users.* FROM users",
94
+ "INNER JOIN microposts ON microposts.user_id = users.id",
95
+ "WHERE",
96
+ "(NOT (microposts.id = 1))",
97
+ "AND",
98
+ "(NOT (microposts.content = 'Sveavägen 4'))"
99
+ )
100
+
101
+ expect(strip(query)).to eq expected_query.to_s
102
+ end
103
+
104
+ end
105
+ end
@@ -2,11 +2,12 @@ describe ActiveHashRelation do
2
2
  include Helpers
3
3
 
4
4
  context 'sorting' do
5
+
5
6
  context "one where clause" do
6
7
  it "asc" do
7
8
  hash = {
8
9
  microposts_count: 10,
9
- sort: {property: :microposts_count, order: :asc}
10
+ sort: {microposts_count: :asc}
10
11
  }
11
12
 
12
13
  query = HelperClass.new.apply_filters(User.all, hash).to_sql
@@ -19,10 +20,10 @@ describe ActiveHashRelation do
19
20
  expect(strip(query)).to eq expected_query.to_s
20
21
  end
21
22
 
22
- it "multiple where clause" do
23
+ it "desc" do
23
24
  hash = {
24
25
  microposts_count: 10,
25
- sort: {property: :microposts_count, order: :desc}
26
+ sort: {microposts_count: :desc}
26
27
  }
27
28
 
28
29
  query = HelperClass.new.apply_filters(User.all, hash).to_sql
@@ -41,7 +42,7 @@ describe ActiveHashRelation do
41
42
  hash = {
42
43
  followers_count: {leq: 20},
43
44
  microposts_count: 10,
44
- sort: {property: :microposts_count, order: :asc}
45
+ sort: {microposts_count: :asc}
45
46
  }
46
47
 
47
48
  query = HelperClass.new.apply_filters(User.all, hash).to_sql
@@ -55,11 +56,11 @@ describe ActiveHashRelation do
55
56
  expect(strip(query)).to eq expected_query.to_s
56
57
  end
57
58
 
58
- it "multiple where clause" do
59
+ it "desc" do
59
60
  hash = {
60
61
  followers_count: {leq: 20},
61
62
  microposts_count: 10,
62
- sort: {property: :microposts_count, order: :desc}
63
+ sort: {microposts_count: :desc}
63
64
  }
64
65
 
65
66
  query = HelperClass.new.apply_filters(User.all, hash).to_sql
@@ -75,26 +76,235 @@ describe ActiveHashRelation do
75
76
  end
76
77
 
77
78
  context "multiple sorting properties" do
78
- it "with single where clause" do
79
+ context "as a hashe" do
80
+ it "with single where clause" do
81
+ hash = {
82
+ microposts_count: 10,
83
+ sort: {
84
+ microposts_count: :asc,
85
+ followings_count: :desc
86
+ }
87
+ }
88
+
89
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
90
+ expected_query = q(
91
+ "SELECT users.* FROM users",
92
+ "WHERE (users.microposts_count = 10)",
93
+ "ORDER BY users.microposts_count ASC, users.followings_count DESC"
94
+ )
95
+
96
+ expect(strip(query)).to eq expected_query.to_s
97
+ end
98
+
99
+ it "when the sorting column does not exist" do
100
+ hash = {
101
+ microposts_count: 10,
102
+ sort: {
103
+ i_do_not_exist: :asc,
104
+ followings_count: :desc
105
+ }
106
+ }
107
+
108
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
109
+ expected_query = q(
110
+ "SELECT users.* FROM users",
111
+ "WHERE (users.microposts_count = 10)",
112
+ "ORDER BY users.followings_count DESC"
113
+ )
114
+
115
+ expect(strip(query)).to eq expected_query.to_s
116
+ end
117
+ end
118
+
119
+ context "as an array of hashes (not recommended)" do
120
+ it "with single where clause" do
121
+ hash = {
122
+ microposts_count: 10,
123
+ sort: [{
124
+ microposts_count: :asc,
125
+ }, {
126
+ followings_count: :desc
127
+ }]
128
+ }
129
+
130
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
131
+ expected_query = q(
132
+ "SELECT users.* FROM users",
133
+ "WHERE (users.microposts_count = 10)",
134
+ "ORDER BY users.microposts_count ASC, users.followings_count DESC"
135
+ )
136
+
137
+ expect(strip(query)).to eq expected_query.to_s
138
+ end
139
+
140
+ it "when the sorting column does not exist" do
141
+ hash = {
142
+ microposts_count: 10,
143
+ sort: [{
144
+ i_do_not_exist: :asc,
145
+ }, {
146
+ followings_count: :desc
147
+ }]
148
+ }
149
+
150
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
151
+ expected_query = q(
152
+ "SELECT users.* FROM users",
153
+ "WHERE (users.microposts_count = 10)",
154
+ "ORDER BY users.followings_count DESC"
155
+ )
156
+
157
+ expect(strip(query)).to eq expected_query.to_s
158
+ end
159
+ end
160
+
161
+ it "when the sorting column does not exist" do
79
162
  hash = {
80
163
  microposts_count: 10,
81
- sort: [{
82
- property: :microposts_count, order: :asc,
83
- }, {
84
- property: :followings_count, order: :desc
85
- }]
164
+ sort: {i_do_not_exist: :asc}
86
165
  }
87
166
 
88
167
  query = HelperClass.new.apply_filters(User.all, hash).to_sql
89
168
  expected_query = q(
90
169
  "SELECT users.* FROM users",
91
- "WHERE (users.microposts_count = 10)",
92
- "ORDER BY users.microposts_count ASC, users.followings_count DESC"
170
+ "WHERE (users.microposts_count = 10)"
93
171
  )
94
172
 
95
173
  expect(strip(query)).to eq expected_query.to_s
174
+
96
175
  end
97
176
  end
98
- end
99
177
 
178
+ context "deprecated API" do
179
+ context "one where clause" do
180
+ it "asc" do
181
+ hash = {
182
+ microposts_count: 10,
183
+ sort: {property: :microposts_count, order: :asc}
184
+ }
185
+
186
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
187
+ expected_query = q(
188
+ "SELECT users.* FROM users",
189
+ "WHERE (users.microposts_count = 10)",
190
+ "ORDER BY users.microposts_count ASC"
191
+ )
192
+
193
+ expect(strip(query)).to eq expected_query.to_s
194
+ end
195
+
196
+ it "desc" do
197
+ hash = {
198
+ microposts_count: 10,
199
+ sort: {property: :microposts_count, order: :desc}
200
+ }
201
+
202
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
203
+ expected_query = q(
204
+ "SELECT users.* FROM users",
205
+ "WHERE (users.microposts_count = 10)",
206
+ "ORDER BY users.microposts_count DESC"
207
+ )
208
+
209
+ expect(strip(query)).to eq expected_query.to_s
210
+ end
211
+ end
212
+
213
+ context "multiple where clauses" do
214
+ it "asc" do
215
+ hash = {
216
+ followers_count: {leq: 20},
217
+ microposts_count: 10,
218
+ sort: {property: :microposts_count, order: :asc}
219
+ }
220
+
221
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
222
+ expected_query = q(
223
+ "SELECT users.* FROM users",
224
+ "WHERE (users.microposts_count = 10)",
225
+ "AND (users.followers_count <= 20)",
226
+ "ORDER BY users.microposts_count ASC"
227
+ )
228
+
229
+ expect(strip(query)).to eq expected_query.to_s
230
+ end
231
+
232
+ it "desc" do
233
+ hash = {
234
+ followers_count: {leq: 20},
235
+ microposts_count: 10,
236
+ sort: {property: :microposts_count, order: :desc}
237
+ }
238
+
239
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
240
+ expected_query = q(
241
+ "SELECT users.* FROM users",
242
+ "WHERE (users.microposts_count = 10)",
243
+ "AND (users.followers_count <= 20)",
244
+ "ORDER BY users.microposts_count DESC"
245
+ )
246
+
247
+ expect(strip(query)).to eq expected_query.to_s
248
+ end
249
+ end
250
+
251
+ context "multiple sorting properties" do
252
+ it "with single where clause" do
253
+ hash = {
254
+ microposts_count: 10,
255
+ sort: [{
256
+ property: :microposts_count, order: :asc,
257
+ }, {
258
+ property: :followings_count, order: :desc
259
+ }]
260
+ }
261
+
262
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
263
+ expected_query = q(
264
+ "SELECT users.* FROM users",
265
+ "WHERE (users.microposts_count = 10)",
266
+ "ORDER BY users.microposts_count ASC, users.followings_count DESC"
267
+ )
268
+
269
+ expect(strip(query)).to eq expected_query.to_s
270
+ end
271
+
272
+ it "when the sorting column does not exist" do
273
+ hash = {
274
+ microposts_count: 10,
275
+ sort: [{
276
+ property: :i_do_not_exist, order: :asc,
277
+ }, {
278
+ property: :followings_count, order: :desc
279
+ }]
280
+ }
281
+
282
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
283
+ expected_query = q(
284
+ "SELECT users.* FROM users",
285
+ "WHERE (users.microposts_count = 10)",
286
+ "ORDER BY users.followings_count DESC"
287
+ )
288
+
289
+ expect(strip(query)).to eq expected_query.to_s
290
+ end
291
+ end
292
+
293
+ it "when the sorting column does not exist" do
294
+ hash = {
295
+ microposts_count: 10,
296
+ sort: {property: :i_do_not_exist, order: :asc}
297
+ }
298
+
299
+ query = HelperClass.new.apply_filters(User.all, hash).to_sql
300
+ expected_query = q(
301
+ "SELECT users.* FROM users",
302
+ "WHERE (users.microposts_count = 10)"
303
+ )
304
+
305
+ expect(strip(query)).to eq expected_query.to_s
306
+
307
+ end
308
+ end
309
+ end
100
310
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_hash_relation
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Filippos Vasilakis
@@ -331,6 +331,7 @@ files:
331
331
  - spec/tests/associations/has_one_spec.rb
332
332
  - spec/tests/booleans_spec.rb
333
333
  - spec/tests/limit_spec.rb
334
+ - spec/tests/not_filter_spec.rb
334
335
  - spec/tests/null_spec.rb
335
336
  - spec/tests/numbers_spec.rb
336
337
  - spec/tests/or_filter_spec.rb
@@ -472,6 +473,7 @@ test_files:
472
473
  - spec/tests/associations/has_one_spec.rb
473
474
  - spec/tests/booleans_spec.rb
474
475
  - spec/tests/limit_spec.rb
476
+ - spec/tests/not_filter_spec.rb
475
477
  - spec/tests/null_spec.rb
476
478
  - spec/tests/numbers_spec.rb
477
479
  - spec/tests/or_filter_spec.rb