activerecord_where_assoc 0.1.1 → 0.1.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
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 78be7fdd6b3da54253f5fe51709c258256dbe013341b69c3f0c9a23a3e2968f3
|
4
|
+
data.tar.gz: 69c40eabe1332c945abef850347d2b39dc4412eedc65cc02aabcd07901ef2de5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f004ca618191fe6b8841d354ef10b3e0209e6721f5d35d5ebc6aff87f13fcbf62a92e0cd42bd77ef4b87b987310a56155bab9f50b59915b361871c3d51cebe4a
|
7
|
+
data.tar.gz: 84da6aaedd53c494913bad518a36ca18dd717c3ac2d3dbed3af5fe78561716ad0c38af67832272ea2b26dddd0319f5ae820a1d636cea5fb14aa27a8814a2f2dc
|
data/ALTERNATIVES_PROBLEMS.md
CHANGED
@@ -21,18 +21,17 @@ Summary of the problems of the alternatives that `activerecord_where_assoc` solv
|
|
21
21
|
* every alternatives (except raw SQL):
|
22
22
|
* treat `has_one` like a `has_many`.
|
23
23
|
* can't handle recursive associations. (ex: parent/children)
|
24
|
-
* no simple way of checking for more complex counts. (such as less than 5)
|
24
|
+
* no simple way of checking for more complex counts. (such as `less than 5`)
|
25
25
|
* `joins` / `includes`:
|
26
26
|
* doing `not exists` with conditions requires a `LEFT JOIN` with the conditions as part of the `ON`, which requires raw SQL.
|
27
|
-
* checking for 2 sets of conditions on different records of the same association won't work. (so your scopes
|
27
|
+
* checking for 2 sets of conditions on different records of the same association won't work. (so your scopes can be incompatible)
|
28
28
|
* can't be used with Rails 5's `or` unless both sides do the same `joins` / `includes` / `eager_load`.
|
29
29
|
* `joins`:
|
30
|
-
* `has_many` may return duplicate
|
30
|
+
* `has_many` may return duplicate records.
|
31
31
|
* using `uniq` / `distinct` to solve duplicate rows is an unexpected side-effect when this is in a scope.
|
32
32
|
* `includes`:
|
33
33
|
* triggers eagerloading, which makes your `scope` have unexpected bad performances if it's not necessary.
|
34
34
|
* when using a condition, the eagerloaded records are also filtered, which is very bug-prone when in a scope.
|
35
|
-
* can't do `not exists` with conditions.
|
36
35
|
* raw SQL:
|
37
36
|
* verbose, less clear on the goal of the queries (you don't even name the association the query is about).
|
38
37
|
* need to repeat conditions from the association / default_scope.
|
data/EXAMPLES.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Here are some example usages of the gem, along with the generated SQL. Each of those can be chained with scoping methods.
|
2
2
|
|
3
|
-
|
3
|
+
The models can be found in [examples/models.md](examples/models.md). The comments in that file explain how to get a console to try the queries. There are also example uses of the gem for scopes.
|
4
4
|
|
5
5
|
The content below is generated from running `ruby examples/examples.rb`
|
6
6
|
|
data/README.md
CHANGED
@@ -80,7 +80,7 @@ Post.where_assoc_not_exists(:comments, is_spam: true)
|
|
80
80
|
|
81
81
|
### `#where_assoc_count`
|
82
82
|
|
83
|
-
This is a generalization of `#where_assoc_exists` and `#where_assoc_not_exists`. It
|
83
|
+
This is a generalization of `#where_assoc_exists` and `#where_assoc_not_exists`. It behaves the same way as them, but is more flexible as it allows you to be specific about how many matches there should be. To clarify, here are equivalent examples:
|
84
84
|
|
85
85
|
```ruby
|
86
86
|
Post.where_assoc_exists(:comments, is_spam: true)
|
@@ -90,7 +90,12 @@ Post.where_assoc_not_exists(:comments, is_spam: true)
|
|
90
90
|
Post.where_assoc_count(0, :==, :comments, is_spam: true)
|
91
91
|
```
|
92
92
|
|
93
|
-
* 1st parameter:
|
93
|
+
* 1st parameter: the left side of the comparison. One of:
|
94
|
+
* a number
|
95
|
+
* a string of SQL to embed in the query
|
96
|
+
* a range (operator must be `:==` or `:!=`)
|
97
|
+
will use SQL's `BETWEEN` or `NOT BETWEEN`
|
98
|
+
supports infinite ranges and exclusive end
|
94
99
|
* 2nd parameter: the operator to use: `:<`, `:<=`, `:==`, `:!=`, `:>=`, `:>`
|
95
100
|
* 3rd, 4th, 5th parameters are the same as the 1st, 2nd and 3rd parameters of `#where_assoc_exists`.
|
96
101
|
* block: same as `#where_assoc_exists`' block
|
@@ -69,16 +69,9 @@ module ActiveRecordWhereAssoc
|
|
69
69
|
end
|
70
70
|
|
71
71
|
nested_relation = relation_on_association(base_relation, association_name, given_scope, options, deepest_scope_mod, NestWithSumBlock)
|
72
|
-
operator = case operator.to_s
|
73
|
-
when "=="
|
74
|
-
"="
|
75
|
-
when "!="
|
76
|
-
"<>"
|
77
|
-
else
|
78
|
-
operator
|
79
|
-
end
|
80
72
|
|
81
|
-
|
73
|
+
sql = sql_for_count_operator(left_operand, operator, "COALESCE((#{nested_relation.to_sql}), 0)")
|
74
|
+
base_relation.where(sql)
|
82
75
|
end
|
83
76
|
|
84
77
|
# Returns the receiver (with possible alterations) and a relation meant to be embed in the received.
|
@@ -333,5 +326,44 @@ module ActiveRecordWhereAssoc
|
|
333
326
|
parent = ActiveRecordCompat.parent_reflection(reflection)
|
334
327
|
parent && parent.macro == :has_and_belongs_to_many
|
335
328
|
end
|
329
|
+
|
330
|
+
# Doing (SQL) BETWEEN v1 AND v2, where v2 is infinite means (SQL) >= v1. However,
|
331
|
+
# we place the SQL on the right side, so the operator is flipped to become v1 <= (SQL).
|
332
|
+
# Doing (SQL) NOT BETWEEN v1 AND v2 where v2 is infinite means (SQL) < v1. However,
|
333
|
+
# we place the SQL on the right side, so the operator is flipped to become v1 > (SQL).
|
334
|
+
RIGHT_INFINITE_RANGE_OPERATOR_MAP = { "=" => "<=", "<>" => ">" }.freeze
|
335
|
+
# We flip the operators to use when it's about the left-side of the range.
|
336
|
+
LEFT_INFINITE_RANGE_OPERATOR_MAP = Hash[RIGHT_INFINITE_RANGE_OPERATOR_MAP.map { |k, v| [k, v.tr("<>", "><")] }].freeze
|
337
|
+
|
338
|
+
RANGE_OPERATOR_MAP = { "=" => "BETWEEN", "<>" => "NOT BETWEEN" }.freeze
|
339
|
+
|
340
|
+
def self.sql_for_count_operator(left_operand, operator, right_sql)
|
341
|
+
operator = case operator.to_s
|
342
|
+
when "=="
|
343
|
+
"="
|
344
|
+
when "!="
|
345
|
+
"<>"
|
346
|
+
else
|
347
|
+
operator.to_s
|
348
|
+
end
|
349
|
+
|
350
|
+
return "(#{left_operand}) #{operator} #{right_sql}" unless left_operand.is_a?(Range)
|
351
|
+
|
352
|
+
unless %w(= <>).include?(operator)
|
353
|
+
raise ArgumentError, "Operator should be one of '==', '=', '<>' or '!=' when using a Range not: #{operator.inspect}"
|
354
|
+
end
|
355
|
+
|
356
|
+
v1 = left_operand.begin || 0
|
357
|
+
v2 = left_operand.end || Float::INFINITY
|
358
|
+
|
359
|
+
v1 = 0 if v1 == -Float::INFINITY
|
360
|
+
|
361
|
+
return sql_for_count_operator(v1, RIGHT_INFINITE_RANGE_OPERATOR_MAP.fetch(operator), right_sql) if v2 == Float::INFINITY
|
362
|
+
|
363
|
+
# Its int or a float with no mantissa, exclude_end? means -1
|
364
|
+
v2 -= 1 if left_operand.exclude_end? && v2 % 1 == 0
|
365
|
+
|
366
|
+
"#{right_sql} #{RANGE_OPERATOR_MAP.fetch(operator)} #{v1} AND #{v2}"
|
367
|
+
end
|
336
368
|
end
|
337
369
|
end
|
@@ -150,8 +150,11 @@ module ActiveRecordWhereAssoc
|
|
150
150
|
# The usage is the same as with #where_assoc_exists, however, 2 arguments are inserted
|
151
151
|
# at the beginning.
|
152
152
|
#
|
153
|
-
# 1st argument:
|
154
|
-
#
|
153
|
+
# 1st argument: the left side of the comparison. One of:
|
154
|
+
# a number
|
155
|
+
# a string of SQL to embed in the query
|
156
|
+
# a range (operator must be :== or :!=), will use BETWEEN or NOT BETWEEN
|
157
|
+
# supports infinite ranges and exclusive end
|
155
158
|
# 2nd argument: the operator to use: :<, :<=, :==, :!=, :>=, :>
|
156
159
|
# 3rd, 4th and 5th arguments: same as #where_assoc_exists' 1st, 2nd and 3rd arguments
|
157
160
|
# block: same as #where_assoc_exists' block
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord_where_assoc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maxime Handfield Lapointe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -190,7 +190,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
190
190
|
version: '0'
|
191
191
|
requirements: []
|
192
192
|
rubyforge_project:
|
193
|
-
rubygems_version: 2.6
|
193
|
+
rubygems_version: 2.7.6
|
194
194
|
signing_key:
|
195
195
|
specification_version: 4
|
196
196
|
summary: Make ActiveRecord do conditions on your associations
|