active_hash_relation 1.2.0 → 1.4.0

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