activerecord_where_assoc 0.1.1 → 0.1.2
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
|
-
|
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
|